Loading libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +17 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,9 @@ public class DesktopModeStatus { /** The maximum override density allowed for tasks inside the desktop. */ private static final int DESKTOP_DENSITY_MAX = 1000; /** The number of [WindowDecorViewHost] instances to warm up on system start. */ private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2; /** * Sysprop declaring whether to enters desktop mode by default when the windowing mode of the * display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM. Loading Loading @@ -121,6 +124,14 @@ public class DesktopModeStatus { */ private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit"; /** * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start. * * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used. */ private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP = "persist.wm.debug.desktop_window_decor_pre_warm_size"; /** * Return {@code true} if veiled resizing is active. If false, fluid resizing is used. */ Loading Loading @@ -176,6 +187,12 @@ public class DesktopModeStatus { return 0; } /** The number of [WindowDecorViewHost] instances to warm up on system start. */ public static int getWindowDecorPreWarmSize() { return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP, WINDOW_DECOR_PRE_WARM_SIZE); } /** * Return {@code true} if the current device supports desktop mode. */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +5 −2 Original line number Diff line number Diff line Loading @@ -349,10 +349,13 @@ public abstract class WMShellModule { @Provides static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier( @NonNull Context context, @ShellMainThread @NonNull CoroutineScope mainScope) { @ShellMainThread @NonNull CoroutineScope mainScope, @NonNull ShellInit shellInit) { final int poolSize = DesktopModeStatus.getWindowDecorScvhPoolSize(context); final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize(); if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && poolSize > 0) { return new PooledWindowDecorViewHostSupplier(mainScope, poolSize); return new PooledWindowDecorViewHostSupplier( context, mainScope, shellInit, poolSize, preWarmSize); } return new DefaultWindowDecorViewHostSupplier(mainScope); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt +37 −5 Original line number Diff line number Diff line Loading @@ -21,25 +21,57 @@ import android.util.Pools import android.view.Display import android.view.SurfaceControl import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.sysui.ShellInit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * A [WindowDecorViewHostSupplier] backed by a pool to allow recycling view hosts which may be * expensive to recreate for each new or updated window decoration. * * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled * object if available, or create a new instance and return it if needed. When finished using a * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back * into the pool and reused later on. * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled object if * available, or create a new instance and return it if needed. When finished using a * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back into the * pool and reused later on. * * This class also supports pre-warming [ReusableWindowDecorViewHost] instances, which will be put * into the pool immediately after creation. */ class PooledWindowDecorViewHostSupplier( private val context: Context, @ShellMainThread private val mainScope: CoroutineScope, shellInit: ShellInit, maxPoolSize: Int, private val preWarmSize: Int, ) : WindowDecorViewHostSupplier<WindowDecorViewHost> { private val pool: Pools.Pool<WindowDecorViewHost> = Pools.SynchronizedPool(maxPoolSize) private var nextDecorViewHostId = 0 init { require(preWarmSize <= maxPoolSize) { "Pre-warm size should not exceed pool size" } shellInit.addInitCallback(this::onShellInit, this) } private fun onShellInit() { if (preWarmSize <= 0) { return } preWarmViewHosts(preWarmSize) } private fun preWarmViewHosts(preWarmSize: Int) { mainScope.launch { // Applying isn't needed, as the surface was never actually shown. val t = SurfaceControl.Transaction() repeat(preWarmSize) { val warmedViewHost = newInstance(context, context.display).apply { warmUp() } // Put the warmed view host in the pool by releasing it. release(warmedViewHost, t) } } } override fun acquire(context: Context, display: Display): WindowDecorViewHost { val pooledViewHost = pool.acquire() if (pooledViewHost != null) { Loading @@ -64,7 +96,7 @@ class PooledWindowDecorViewHostSupplier( context = context, mainScope = mainScope, display = display, id = nextDecorViewHostId++ id = nextDecorViewHostId++, ) } } libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt +32 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,15 @@ package com.android.wm.shell.windowdecor.common.viewhost import android.content.Context import android.content.res.Configuration import android.graphics.PixelFormat import android.graphics.Region import android.view.Display import android.view.SurfaceControl import android.view.View import android.view.WindowManager import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH import android.view.WindowManager.LayoutParams.TYPE_APPLICATION import android.widget.FrameLayout import androidx.tracing.Trace import com.android.internal.annotations.VisibleForTesting Loading @@ -35,6 +39,9 @@ import kotlinx.coroutines.launch * 1) Replacing the root [View], meaning [WindowDecorViewHost.updateView] maybe be called with * different [View] instances. This is useful when reusing [WindowDecorViewHost]s instances for * vastly different view hierarchies, such as Desktop Windowing's App Handles and App Headers. * 2) Pre-warming of the underlying [SurfaceControlViewHostAdapter]s. Useful because their creation * and first root view assignment are expensive, which is undesirable in latency-sensitive code * paths like during a shell transition. */ class ReusableWindowDecorViewHost( private val context: Context, Loading @@ -44,7 +51,7 @@ class ReusableWindowDecorViewHost( @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter = SurfaceControlViewHostAdapter(context, display), ) : WindowDecorViewHost { ) : WindowDecorViewHost, Warmable { @VisibleForTesting val rootView = FrameLayout(context) private var currentUpdateJob: Job? = null Loading @@ -52,6 +59,30 @@ class ReusableWindowDecorViewHost( override val surfaceControl: SurfaceControl get() = viewHostAdapter.rootSurface override fun warmUp() { if (viewHostAdapter.isInitialized()) { // Already warmed up. return } Trace.beginSection("$TAG#warmUp") viewHostAdapter.prepareViewHost(context.resources.configuration, touchableRegion = null) viewHostAdapter.updateView( rootView, WindowManager.LayoutParams( 0 /* width*/, 0 /* height */, TYPE_APPLICATION, FLAG_NOT_FOCUSABLE or FLAG_SPLIT_TOUCH, PixelFormat.TRANSPARENT, ) .apply { setTitle("View root of $TAG#$id") setTrustedOverlay() }, ) Trace.endSection() } override fun updateView( view: View, attrs: WindowManager.LayoutParams, Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.windowdecor.common.viewhost /** * An interface for an object that can be warmed up before it's needed. */ interface Warmable { fun warmUp() } Loading
libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +17 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,9 @@ public class DesktopModeStatus { /** The maximum override density allowed for tasks inside the desktop. */ private static final int DESKTOP_DENSITY_MAX = 1000; /** The number of [WindowDecorViewHost] instances to warm up on system start. */ private static final int WINDOW_DECOR_PRE_WARM_SIZE = 2; /** * Sysprop declaring whether to enters desktop mode by default when the windowing mode of the * display's root TaskDisplayArea is set to WINDOWING_MODE_FREEFORM. Loading Loading @@ -121,6 +124,14 @@ public class DesktopModeStatus { */ private static final String MAX_TASK_LIMIT_SYS_PROP = "persist.wm.debug.desktop_max_task_limit"; /** * Sysprop declaring the number of [WindowDecorViewHost] instances to warm up on system start. * * <p>If it is not defined, then [WINDOW_DECOR_PRE_WARM_SIZE] is used. */ private static final String WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP = "persist.wm.debug.desktop_window_decor_pre_warm_size"; /** * Return {@code true} if veiled resizing is active. If false, fluid resizing is used. */ Loading Loading @@ -176,6 +187,12 @@ public class DesktopModeStatus { return 0; } /** The number of [WindowDecorViewHost] instances to warm up on system start. */ public static int getWindowDecorPreWarmSize() { return SystemProperties.getInt(WINDOW_DECOR_PRE_WARM_SIZE_SYS_PROP, WINDOW_DECOR_PRE_WARM_SIZE); } /** * Return {@code true} if the current device supports desktop mode. */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +5 −2 Original line number Diff line number Diff line Loading @@ -349,10 +349,13 @@ public abstract class WMShellModule { @Provides static WindowDecorViewHostSupplier<WindowDecorViewHost> provideWindowDecorViewHostSupplier( @NonNull Context context, @ShellMainThread @NonNull CoroutineScope mainScope) { @ShellMainThread @NonNull CoroutineScope mainScope, @NonNull ShellInit shellInit) { final int poolSize = DesktopModeStatus.getWindowDecorScvhPoolSize(context); final int preWarmSize = DesktopModeStatus.getWindowDecorPreWarmSize(); if (DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context) && poolSize > 0) { return new PooledWindowDecorViewHostSupplier(mainScope, poolSize); return new PooledWindowDecorViewHostSupplier( context, mainScope, shellInit, poolSize, preWarmSize); } return new DefaultWindowDecorViewHostSupplier(mainScope); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/PooledWindowDecorViewHostSupplier.kt +37 −5 Original line number Diff line number Diff line Loading @@ -21,25 +21,57 @@ import android.util.Pools import android.view.Display import android.view.SurfaceControl import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.sysui.ShellInit import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * A [WindowDecorViewHostSupplier] backed by a pool to allow recycling view hosts which may be * expensive to recreate for each new or updated window decoration. * * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled * object if available, or create a new instance and return it if needed. When finished using a * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back * into the pool and reused later on. * Callers can obtain a [WindowDecorViewHost] using [acquire], which will return a pooled object if * available, or create a new instance and return it if needed. When finished using a * [WindowDecorViewHost], it must be released using [release] to allow it to be sent back into the * pool and reused later on. * * This class also supports pre-warming [ReusableWindowDecorViewHost] instances, which will be put * into the pool immediately after creation. */ class PooledWindowDecorViewHostSupplier( private val context: Context, @ShellMainThread private val mainScope: CoroutineScope, shellInit: ShellInit, maxPoolSize: Int, private val preWarmSize: Int, ) : WindowDecorViewHostSupplier<WindowDecorViewHost> { private val pool: Pools.Pool<WindowDecorViewHost> = Pools.SynchronizedPool(maxPoolSize) private var nextDecorViewHostId = 0 init { require(preWarmSize <= maxPoolSize) { "Pre-warm size should not exceed pool size" } shellInit.addInitCallback(this::onShellInit, this) } private fun onShellInit() { if (preWarmSize <= 0) { return } preWarmViewHosts(preWarmSize) } private fun preWarmViewHosts(preWarmSize: Int) { mainScope.launch { // Applying isn't needed, as the surface was never actually shown. val t = SurfaceControl.Transaction() repeat(preWarmSize) { val warmedViewHost = newInstance(context, context.display).apply { warmUp() } // Put the warmed view host in the pool by releasing it. release(warmedViewHost, t) } } } override fun acquire(context: Context, display: Display): WindowDecorViewHost { val pooledViewHost = pool.acquire() if (pooledViewHost != null) { Loading @@ -64,7 +96,7 @@ class PooledWindowDecorViewHostSupplier( context = context, mainScope = mainScope, display = display, id = nextDecorViewHostId++ id = nextDecorViewHostId++, ) } }
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/ReusableWindowDecorViewHost.kt +32 −1 Original line number Diff line number Diff line Loading @@ -17,11 +17,15 @@ package com.android.wm.shell.windowdecor.common.viewhost import android.content.Context import android.content.res.Configuration import android.graphics.PixelFormat import android.graphics.Region import android.view.Display import android.view.SurfaceControl import android.view.View import android.view.WindowManager import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE import android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH import android.view.WindowManager.LayoutParams.TYPE_APPLICATION import android.widget.FrameLayout import androidx.tracing.Trace import com.android.internal.annotations.VisibleForTesting Loading @@ -35,6 +39,9 @@ import kotlinx.coroutines.launch * 1) Replacing the root [View], meaning [WindowDecorViewHost.updateView] maybe be called with * different [View] instances. This is useful when reusing [WindowDecorViewHost]s instances for * vastly different view hierarchies, such as Desktop Windowing's App Handles and App Headers. * 2) Pre-warming of the underlying [SurfaceControlViewHostAdapter]s. Useful because their creation * and first root view assignment are expensive, which is undesirable in latency-sensitive code * paths like during a shell transition. */ class ReusableWindowDecorViewHost( private val context: Context, Loading @@ -44,7 +51,7 @@ class ReusableWindowDecorViewHost( @VisibleForTesting val viewHostAdapter: SurfaceControlViewHostAdapter = SurfaceControlViewHostAdapter(context, display), ) : WindowDecorViewHost { ) : WindowDecorViewHost, Warmable { @VisibleForTesting val rootView = FrameLayout(context) private var currentUpdateJob: Job? = null Loading @@ -52,6 +59,30 @@ class ReusableWindowDecorViewHost( override val surfaceControl: SurfaceControl get() = viewHostAdapter.rootSurface override fun warmUp() { if (viewHostAdapter.isInitialized()) { // Already warmed up. return } Trace.beginSection("$TAG#warmUp") viewHostAdapter.prepareViewHost(context.resources.configuration, touchableRegion = null) viewHostAdapter.updateView( rootView, WindowManager.LayoutParams( 0 /* width*/, 0 /* height */, TYPE_APPLICATION, FLAG_NOT_FOCUSABLE or FLAG_SPLIT_TOUCH, PixelFormat.TRANSPARENT, ) .apply { setTitle("View root of $TAG#$id") setTrustedOverlay() }, ) Trace.endSection() } override fun updateView( view: View, attrs: WindowManager.LayoutParams, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/Warmable.kt 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.windowdecor.common.viewhost /** * An interface for an object that can be warmed up before it's needed. */ interface Warmable { fun warmUp() }