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
This commit is contained in:
Jernej Virag
2023-05-11 15:59:46 +02:00
parent 44db040f6c
commit f1a8c8d400
5 changed files with 76 additions and 8 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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)
}
}

View File

@@ -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)
}
}