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

Commit f1a8c8d4 authored by Jernej Virag's avatar Jernej Virag
Browse files

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
parent 44db040f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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
+32 −2
Original line number Diff line number Diff line
@@ -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
+0 −4
Original line number Diff line number Diff line
@@ -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() {
+6 −0
Original line number Diff line number Diff line
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)
    }
}
+34 −2
Original line number Diff line number Diff line
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)
        }
}