1 package com.android.systemui.keyguard 2 3 import android.content.ComponentCallbacks2 4 import android.graphics.HardwareRenderer 5 import android.testing.AndroidTestingRunner 6 import androidx.test.filters.SmallTest 7 import com.android.systemui.SysuiTestCase 8 import com.android.systemui.flags.FakeFeatureFlags 9 import com.android.systemui.flags.Flags 10 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 11 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository 12 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory 13 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory 14 import com.android.systemui.keyguard.shared.model.KeyguardState 15 import com.android.systemui.keyguard.shared.model.TransitionStep 16 import com.android.systemui.keyguard.shared.model.WakeSleepReason 17 import com.android.systemui.keyguard.shared.model.WakefulnessModel 18 import com.android.systemui.keyguard.shared.model.WakefulnessState 19 import com.android.systemui.util.mockito.any 20 import com.android.systemui.utils.GlobalWindowManager 21 import kotlinx.coroutines.ExperimentalCoroutinesApi 22 import kotlinx.coroutines.test.TestScope 23 import kotlinx.coroutines.test.UnconfinedTestDispatcher 24 import kotlinx.coroutines.test.runCurrent 25 import kotlinx.coroutines.test.runTest 26 import org.junit.Before 27 import org.junit.Test 28 import org.junit.runner.RunWith 29 import org.mockito.Mock 30 import org.mockito.Mockito.times 31 import org.mockito.Mockito.verify 32 import org.mockito.Mockito.verifyNoMoreInteractions 33 import org.mockito.Mockito.verifyZeroInteractions 34 import org.mockito.MockitoAnnotations 35 36 @OptIn(ExperimentalCoroutinesApi::class) 37 @RunWith(AndroidTestingRunner::class) 38 @SmallTest 39 class ResourceTrimmerTest : SysuiTestCase() { 40 41 private val testDispatcher = UnconfinedTestDispatcher() 42 private val testScope = TestScope(testDispatcher) 43 private val keyguardRepository = FakeKeyguardRepository() 44 private val featureFlags = FakeFeatureFlags() 45 private val keyguardTransitionRepository = FakeKeyguardTransitionRepository() 46 47 @Mock private lateinit var globalWindowManager: GlobalWindowManager 48 private lateinit var resourceTrimmer: ResourceTrimmer 49 50 @Before 51 fun setUp() { 52 MockitoAnnotations.initMocks(this) 53 featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true) 54 featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true) 55 featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) 56 keyguardRepository.setWakefulnessModel( 57 WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER) 58 ) 59 keyguardRepository.setDozeAmount(0f) 60 keyguardRepository.setKeyguardGoingAway(false) 61 62 val withDeps = 63 KeyguardInteractorFactory.create( 64 repository = keyguardRepository, 65 featureFlags = featureFlags, 66 ) 67 val keyguardInteractor = withDeps.keyguardInteractor 68 resourceTrimmer = 69 ResourceTrimmer( 70 keyguardInteractor, 71 KeyguardTransitionInteractorFactory.create( 72 scope = TestScope().backgroundScope, 73 repository = keyguardTransitionRepository, 74 ) 75 .keyguardTransitionInteractor, 76 globalWindowManager, 77 testScope.backgroundScope, 78 testDispatcher, 79 featureFlags 80 ) 81 resourceTrimmer.start() 82 } 83 84 @Test 85 fun noChange_noOutputChanges() = 86 testScope.runTest { 87 testScope.runCurrent() 88 verifyZeroInteractions(globalWindowManager) 89 } 90 91 @Test 92 fun dozeAodDisabled_sleep_trimsMemory() = 93 testScope.runTest { 94 keyguardRepository.setWakefulnessModel( 95 WakefulnessModel( 96 WakefulnessState.ASLEEP, 97 WakeSleepReason.OTHER, 98 WakeSleepReason.OTHER 99 ) 100 ) 101 testScope.runCurrent() 102 verify(globalWindowManager, times(1)) 103 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) 104 verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL) 105 } 106 107 @Test 108 fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() = 109 testScope.runTest { 110 keyguardRepository.setDreaming(true) 111 keyguardRepository.setDozeAmount(1f) 112 keyguardRepository.setWakefulnessModel( 113 WakefulnessModel( 114 WakefulnessState.ASLEEP, 115 WakeSleepReason.OTHER, 116 WakeSleepReason.OTHER 117 ) 118 ) 119 testScope.runCurrent() 120 verify(globalWindowManager, times(1)) 121 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) 122 verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL) 123 } 124 125 @Test 126 fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() = 127 testScope.runTest { 128 keyguardRepository.setDreaming(true) 129 keyguardRepository.setDozeAmount(0f) 130 keyguardRepository.setWakefulnessModel( 131 WakefulnessModel( 132 WakefulnessState.ASLEEP, 133 WakeSleepReason.OTHER, 134 WakeSleepReason.OTHER 135 ) 136 ) 137 testScope.runCurrent() 138 verifyZeroInteractions(globalWindowManager) 139 } 140 141 @Test 142 fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() { 143 testScope.runTest { 144 keyguardRepository.setDreaming(true) 145 keyguardRepository.setDozeAmount(0f) 146 keyguardRepository.setWakefulnessModel( 147 WakefulnessModel( 148 WakefulnessState.ASLEEP, 149 WakeSleepReason.OTHER, 150 WakeSleepReason.OTHER 151 ) 152 ) 153 154 testScope.runCurrent() 155 verifyZeroInteractions(globalWindowManager) 156 157 generateSequence(0f) { it + 0.1f } 158 .takeWhile { it < 1f } 159 .forEach { 160 keyguardRepository.setDozeAmount(it) 161 testScope.runCurrent() 162 } 163 verifyZeroInteractions(globalWindowManager) 164 165 keyguardRepository.setDozeAmount(1f) 166 testScope.runCurrent() 167 verify(globalWindowManager, times(1)) 168 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) 169 verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL) 170 } 171 } 172 173 @Test 174 fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() { 175 testScope.runTest { 176 keyguardRepository.setDreaming(true) 177 keyguardRepository.setDozeAmount(0f) 178 keyguardRepository.setWakefulnessModel( 179 WakefulnessModel( 180 WakefulnessState.ASLEEP, 181 WakeSleepReason.OTHER, 182 WakeSleepReason.OTHER 183 ) 184 ) 185 186 testScope.runCurrent() 187 verifyZeroInteractions(globalWindowManager) 188 189 generateSequence(0f) { it + 0.1f } 190 .takeWhile { it < 0.8f } 191 .forEach { 192 keyguardRepository.setDozeAmount(it) 193 testScope.runCurrent() 194 } 195 verifyZeroInteractions(globalWindowManager) 196 197 generateSequence(0.8f) { it - 0.1f } 198 .takeWhile { it >= 0f } 199 .forEach { 200 keyguardRepository.setDozeAmount(it) 201 testScope.runCurrent() 202 } 203 204 keyguardRepository.setDozeAmount(0f) 205 testScope.runCurrent() 206 verifyZeroInteractions(globalWindowManager) 207 } 208 } 209 210 @Test 211 fun keyguardTransitionsToGone_trimsFontCache() = 212 testScope.runTest { 213 keyguardTransitionRepository.sendTransitionStep( 214 TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) 215 ) 216 verify(globalWindowManager, times(1)) 217 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) 218 verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT) 219 verifyNoMoreInteractions(globalWindowManager) 220 } 221 222 @Test 223 fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() = 224 testScope.runTest { 225 featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false) 226 keyguardTransitionRepository.sendTransitionStep( 227 TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) 228 ) 229 // Memory hidden should still be called. 230 verify(globalWindowManager, times(1)) 231 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) 232 verify(globalWindowManager, times(0)).trimCaches(any()) 233 } 234 } 235