diff options
15 files changed, 171 insertions, 8 deletions
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 02cd0372ea88..99a4f6b41ef3 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; +import android.graphics.HardwareRenderer; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; @@ -550,6 +551,11 @@ public final class WindowManagerGlobal { ThreadedRenderer.trimMemory(level); } + /** @hide */ + public void trimCaches(@HardwareRenderer.CacheTrimLevel int level) { + ThreadedRenderer.trimCaches(level); + } + public void dumpGfxInfo(FileDescriptor fd, String[] args) { FileOutputStream fout = new FileOutputStream(fd); PrintWriter pw = new FastPrintWriter(fout); diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 9ed3d9c3c94b..9cde1878d9d8 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -144,6 +144,32 @@ public class HardwareRenderer { public @interface DumpFlags { } + + /** + * Trims all Skia caches. + * @hide + */ + public static final int CACHE_TRIM_ALL = 0; + /** + * Trims Skia font caches. + * @hide + */ + public static final int CACHE_TRIM_FONT = 1; + /** + * Trims Skia resource caches. + * @hide + */ + public static final int CACHE_TRIM_RESOURCES = 2; + + /** @hide */ + @IntDef(prefix = {"CACHE_TRIM_"}, value = { + CACHE_TRIM_ALL, + CACHE_TRIM_FONT, + CACHE_TRIM_RESOURCES + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CacheTrimLevel {} + /** * Name of the file that holds the shaders cache. */ @@ -1131,6 +1157,20 @@ public class HardwareRenderer { nTrimMemory(level); } + /** + * Invoke this when all font caches should be flushed. This can cause jank on next render + * commands so use it only after expensive font allocation operations which would + * allocate large amount of temporary memory. + * + * @param level Hint about which caches to trim. See {@link #CACHE_TRIM_ALL}, + * {@link #CACHE_TRIM_FONT}, {@link #CACHE_TRIM_RESOURCES} + * + * @hide + */ + public static void trimCaches(@CacheTrimLevel int level) { + nTrimCaches(level); + } + /** @hide */ public static void overrideProperty(@NonNull String name, @NonNull String value) { if (name == null || value == null) { @@ -1497,6 +1537,8 @@ public class HardwareRenderer { private static native void nTrimMemory(int level); + private static native void nTrimCaches(int level); + private static native void nOverrideProperty(String name, String value); private static native void nFence(long nativeProxy); diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h index 139cdde5c0d2..347daf34f52a 100644 --- a/libs/hwui/MemoryPolicy.h +++ b/libs/hwui/MemoryPolicy.h @@ -31,6 +31,12 @@ enum class TrimLevel { RUNNING_MODERATE = 5, }; +enum class CacheTrimLevel { + ALL_CACHES = 0, + FONT_CACHE = 1, + RESOURCE_CACHE = 2, +}; + struct MemoryPolicy { // The initial scale factor applied to the display resolution. The default is 1, but // lower values may be used to start with a smaller initial cache size. The cache will diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 6a7411f5d859..d04de37f6961 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -362,6 +362,10 @@ static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, RenderProxy::trimMemory(level); } +static void android_view_ThreadedRenderer_trimCaches(JNIEnv* env, jobject clazz, jint level) { + RenderProxy::trimCaches(level); +} + static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, jstring name, jstring value) { const char* nameCharArray = env->GetStringUTFChars(name, NULL); @@ -1018,6 +1022,7 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_notifyCallbackPending}, {"nNotifyExpensiveFrame", "(J)V", (void*)android_view_ThreadedRenderer_notifyExpensiveFrame}, + {"nTrimCaches", "(I)V", (void*)android_view_ThreadedRenderer_trimCaches}, }; static JavaVM* mJvm = nullptr; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index c00a2707e0a2..babce88b8e1e 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -139,6 +139,25 @@ void CacheManager::trimMemory(TrimLevel mode) { } } +void CacheManager::trimCaches(CacheTrimLevel mode) { + switch (mode) { + case CacheTrimLevel::FONT_CACHE: + SkGraphics::PurgeFontCache(); + break; + case CacheTrimLevel::RESOURCE_CACHE: + SkGraphics::PurgeResourceCache(); + break; + case CacheTrimLevel::ALL_CACHES: + SkGraphics::PurgeAllCaches(); + if (mGrContext) { + mGrContext->purgeUnlockedResources(false); + } + break; + default: + break; + } +} + void CacheManager::trimStaleResources() { if (!mGrContext) { return; diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index d21ac9badc43..5e43ac209696 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -48,6 +48,7 @@ public: void configureContext(GrContextOptions* context, const void* identity, ssize_t size); #endif void trimMemory(TrimLevel mode); + void trimCaches(CacheTrimLevel mode); void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 31b4b203c670..224c878bf43d 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -231,6 +231,15 @@ void RenderProxy::trimMemory(int level) { } } +void RenderProxy::trimCaches(int level) { + // Avoid creating a RenderThread to do a trimMemory. + if (RenderThread::hasInstance()) { + RenderThread& thread = RenderThread::getInstance(); + const auto trimLevel = static_cast<CacheTrimLevel>(level); + thread.queue().post([&thread, trimLevel]() { thread.trimCaches(trimLevel); }); + } +} + void RenderProxy::purgeCaches() { if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 82072a6e2499..47c1b0cd28e5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -105,6 +105,7 @@ public: void destroyHardwareResources(); static void trimMemory(int level); + static void trimCaches(int level); static void purgeCaches(); static void overrideProperty(const char* name, const char* value); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 9ba67a2110c1..eb28c080c056 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -521,6 +521,11 @@ void RenderThread::trimMemory(TrimLevel level) { cacheManager().trimMemory(level); } +void RenderThread::trimCaches(CacheTrimLevel level) { + ATRACE_CALL(); + cacheManager().trimCaches(level); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c77cd4134d1e..79e57de9d66f 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -174,6 +174,7 @@ public: } void trimMemory(TrimLevel level); + void trimCaches(CacheTrimLevel level); /** * isCurrent provides a way to query, if the caller is running on diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index c8485dcdef41..cf517eb4f0e1 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -673,6 +673,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 8386a05b6647..d8affa4d6c21 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 df68e7eb037b..0414a14205d7 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 038fddc1f7a9..4111850b53ed 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 367d206814cb..548d26f2aaed 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) + } } |