From f1a8c8d4002da1929fa98b24143b6501fc6cd49d Mon Sep 17 00:00:00 2001 From: Jernej Virag Date: Thu, 11 May 2023 15:59:46 +0200 Subject: [PATCH] Trim font caches when keyguard goes away Right now we send a trim UI_HIDDEN command to trim the resources used by lockscreen after unlock, but that does not clean up any font caches generated by e.g. lockscreen clock animation. This can result in significant memory bloat for user apps once the device is unlock. This trims font caches after unlock as well to increase availability of resources for apps. Bug:275486055 Bug:278225145 Bug:283203305 Test: newly added unit tests for ResourceTrimmer ran LockscreenWithSwipeMicrobenchmark and confirmed lower memory use Change-Id: I84637049a106a94bf9f06c5261c2941b55db4ba0 --- .../src/com/android/systemui/flags/Flags.kt | 4 +++ .../systemui/keyguard/ResourceTrimmer.kt | 34 ++++++++++++++++-- .../phone/StatusBarKeyguardViewManager.java | 4 --- .../systemui/utils/GlobalWindowManager.kt | 6 ++++ .../systemui/keyguard/ResourceTrimmerTest.kt | 36 +++++++++++++++++-- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index f71a46caef344..be0fcd3cd7234 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -676,6 +676,10 @@ object Flags { val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK = unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock") + // TODO:(b/283203305): Tracking bug + @JvmField + val TRIM_FONT_CACHES_AT_UNLOCK = releasedFlag(2402, "trim_font_caches_on_unlock") + // 2700 - unfold transitions // TODO(b/265764985): Tracking Bug @Keep diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt index 8386a05b6647f..d8affa4d6c21a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard import android.annotation.WorkerThread import android.content.ComponentCallbacks2 +import android.graphics.HardwareRenderer import android.os.Trace import android.util.Log import com.android.systemui.CoreStartable @@ -27,12 +28,13 @@ import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.utils.GlobalWindowManager import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -50,6 +52,7 @@ class ResourceTrimmer @Inject constructor( private val keyguardInteractor: KeyguardInteractor, + private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val globalWindowManager: GlobalWindowManager, @Application private val applicationScope: CoroutineScope, @Background private val bgDispatcher: CoroutineDispatcher, @@ -58,7 +61,10 @@ constructor( override fun start() { Log.d(LOG_TAG, "Resource trimmer registered.") - if (!featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)) { + if ( + !(featureFlags.isEnabled(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK) || + featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK)) + ) { return } @@ -78,6 +84,30 @@ constructor( .distinctUntilChanged() .collect { onWakefulnessUpdated(it.first, it.second, it.third) } } + + applicationScope.launch(bgDispatcher) { + // We drop 1 to avoid triggering on initial collect(). + keyguardTransitionInteractor.anyStateToGoneTransition.collect { transition -> + if (transition.transitionState == TransitionState.FINISHED) { + onKeyguardGone() + } + } + } + } + + @WorkerThread + private fun onKeyguardGone() { + if (!featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK)) { + return + } + + if (DEBUG) { + Log.d(LOG_TAG, "Trimming font caches since keyguard went away.") + } + // We want to clear temporary caches we've created while rendering and animating + // lockscreen elements, especially clocks. + globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) + globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_FONT) } @WorkerThread diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index df68e7eb037b8..0414a14205d7b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -23,7 +23,6 @@ import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; -import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.ColorStateList; import android.hardware.biometrics.BiometricSourceType; @@ -36,7 +35,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; -import android.view.WindowManagerGlobal; import android.window.BackEvent; import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedDispatcher; @@ -985,8 +983,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mShadeViewController.resetViewGroupFade(); mCentralSurfaces.finishKeyguardFadingAway(); mBiometricUnlockController.finishKeyguardFadingAway(); - WindowManagerGlobal.getInstance().trimMemory( - ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); } private void wakeAndUnlockDejank() { diff --git a/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt b/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt index 038fddc1f7a9c..4111850b53ed8 100644 --- a/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt +++ b/packages/SystemUI/src/com/android/systemui/utils/GlobalWindowManager.kt @@ -1,5 +1,6 @@ package com.android.systemui.utils +import android.graphics.HardwareRenderer.CacheTrimLevel import android.view.WindowManagerGlobal import javax.inject.Inject @@ -13,4 +14,9 @@ class GlobalWindowManager @Inject constructor() { fun trimMemory(level: Int) { WindowManagerGlobal.getInstance().trimMemory(level) } + + /** Sends a trim caches command to [WindowManagerGlobal]. */ + fun trimCaches(@CacheTrimLevel level: Int) { + WindowManagerGlobal.getInstance().trimCaches(level) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt index 367d206814cbc..548d26f2aaed3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt @@ -1,6 +1,7 @@ package com.android.systemui.keyguard import android.content.ComponentCallbacks2 +import android.graphics.HardwareRenderer import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -9,7 +10,11 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState @@ -25,6 +30,7 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations @@ -37,6 +43,7 @@ class ResourceTrimmerTest : SysuiTestCase() { private val testScope = TestScope(testDispatcher) private val keyguardRepository = FakeKeyguardRepository() private val featureFlags = FakeFeatureFlags() + private val keyguardTransitionRepository = FakeKeyguardTransitionRepository() @Mock private lateinit var globalWindowManager: GlobalWindowManager private lateinit var resourceTrimmer: ResourceTrimmer @@ -45,13 +52,15 @@ class ResourceTrimmerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) featureFlags.set(Flags.TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK, true) + featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true) featureFlags.set(Flags.FACE_AUTH_REFACTOR, false) keyguardRepository.setWakefulnessModel( WakefulnessModel(WakefulnessState.AWAKE, WakeSleepReason.OTHER, WakeSleepReason.OTHER) ) keyguardRepository.setDozeAmount(0f) + keyguardRepository.setKeyguardGoingAway(false) - val interactor = + val keyguardInteractor = KeyguardInteractor( keyguardRepository, FakeCommandQueue(), @@ -60,7 +69,8 @@ class ResourceTrimmerTest : SysuiTestCase() { ) resourceTrimmer = ResourceTrimmer( - interactor, + keyguardInteractor, + KeyguardTransitionInteractor(keyguardTransitionRepository), globalWindowManager, testScope.backgroundScope, testDispatcher, @@ -191,4 +201,26 @@ class ResourceTrimmerTest : SysuiTestCase() { verifyZeroInteractions(globalWindowManager) } } + + @Test + fun keyguardTransitionsToGone_trimsFontCache() = + testScope.runTest { + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + ) + verify(globalWindowManager, times(1)) + .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) + verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT) + verifyNoMoreInteractions(globalWindowManager) + } + + @Test + fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() = + testScope.runTest { + featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep(KeyguardState.LOCKSCREEN, KeyguardState.GONE) + ) + verifyNoMoreInteractions(globalWindowManager) + } }