Loading core/java/android/app/WindowContext.java +6 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -81,6 +82,11 @@ public class WindowContext extends ContextWrapper { mOwnsToken = false; throw e.rethrowFromSystemServer(); } if (result == ADD_TOO_MANY_TOKENS) { throw new UnsupportedOperationException("createWindowContext failed! Too many unused " + "window contexts. Please see Context#createWindowContext documentation for " + "detail."); } mOwnsToken = result == ADD_OKAY; Reference.reachabilityFence(this); } Loading core/java/android/content/Context.java +9 −1 Original line number Diff line number Diff line Loading @@ -5812,6 +5812,12 @@ public abstract class Context { * display.</b> If there is a need to add different window types, or non-associated windows, * separate Contexts should be used. * </p> * <p> * Creating a window context is an expensive operation. Misuse of this API may lead to a huge * performance drop. The best practice is to use the same window context when possible. * An approach is to create one window context with specific window type and display and * use it everywhere it's needed.. * </p> * * @param type Window type in {@link WindowManager.LayoutParams} * @param options Bundle used to pass window-related options. Loading @@ -5824,7 +5830,9 @@ public abstract class Context { * @see #WINDOW_SERVICE * @see #LAYOUT_INFLATER_SERVICE * @see #WALLPAPER_SERVICE * @throws IllegalArgumentException if token is invalid * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or * the current number of window contexts without adding any view by * {@link WindowManager#addView} <b>exceeds five</b>. */ public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options) { throw new RuntimeException("Not implemented. Must override in a subclass."); Loading core/java/android/view/WindowManagerGlobal.java +1 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ public final class WindowManagerGlobal { public static final int ADD_INVALID_DISPLAY = -9; public static final int ADD_INVALID_TYPE = -10; public static final int ADD_INVALID_USER = -11; public static final int ADD_TOO_MANY_TOKENS = -12; @UnsupportedAppUsage private static WindowManagerGlobal sDefaultWindowManager; Loading services/core/java/com/android/server/wm/DisplayContent.java +30 −0 Original line number Diff line number Diff line Loading @@ -5534,4 +5534,34 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } } /** * Returns the number of window tokens without surface on this display. A {@link WindowToken} * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}. * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and * limit the usage if the count exceeds a number. * * @param callingUid app calling uid * @return the number of window tokens without surface on this display * @see WindowToken#addWindow(WindowState) */ int getWindowTokensWithoutSurfaceCount(int callingUid) { List<WindowToken> tokens = new ArrayList<>(mTokenMap.values()); int count = 0; for (int i = tokens.size() - 1; i >= 0; i--) { final WindowToken token = tokens.get(i); if (callingUid != token.getOwnerUid()) { continue; } // Skip if token is an Activity if (token.asActivityRecord() != null) { continue; } if (token.mSurfaceControl != null) { continue; } count++; } return count; } } services/core/java/com/android/server/wm/WindowManagerService.java +28 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; Loading Loading @@ -74,6 +75,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; Loading Loading @@ -413,6 +415,12 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; /** The maximum count of window tokens without surface that an app can register. */ private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5; /** System UI can create more window context... */ private static final int SYSTEM_UI_MULTIPLIER = 2; // TODO(b/143053092): Remove the settings if it becomes stable. private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; boolean mIsFixedRotationTransformEnabled; Loading Loading @@ -2594,10 +2602,30 @@ public class WindowManagerService extends IWindowManager.Stub @Override public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName) { if (tokenCountExceed()) { return ADD_TOO_MANY_TOKENS; } return addWindowTokenWithOptions(binder, type, displayId, options, packageName, true /* fromClientToken */); } private boolean tokenCountExceed() { final int callingUid = Binder.getCallingUid(); // Don't check if caller is from system server. if (callingUid == myPid()) { return false; } final int limit = (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions")) ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE; synchronized (mGlobalLock) { int[] count = new int[1]; mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid)); return count[0] >= limit; } } private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName, boolean fromClientToken) { final boolean callerCanManageAppTokens = Loading Loading
core/java/android/app/WindowContext.java +6 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -81,6 +82,11 @@ public class WindowContext extends ContextWrapper { mOwnsToken = false; throw e.rethrowFromSystemServer(); } if (result == ADD_TOO_MANY_TOKENS) { throw new UnsupportedOperationException("createWindowContext failed! Too many unused " + "window contexts. Please see Context#createWindowContext documentation for " + "detail."); } mOwnsToken = result == ADD_OKAY; Reference.reachabilityFence(this); } Loading
core/java/android/content/Context.java +9 −1 Original line number Diff line number Diff line Loading @@ -5812,6 +5812,12 @@ public abstract class Context { * display.</b> If there is a need to add different window types, or non-associated windows, * separate Contexts should be used. * </p> * <p> * Creating a window context is an expensive operation. Misuse of this API may lead to a huge * performance drop. The best practice is to use the same window context when possible. * An approach is to create one window context with specific window type and display and * use it everywhere it's needed.. * </p> * * @param type Window type in {@link WindowManager.LayoutParams} * @param options Bundle used to pass window-related options. Loading @@ -5824,7 +5830,9 @@ public abstract class Context { * @see #WINDOW_SERVICE * @see #LAYOUT_INFLATER_SERVICE * @see #WALLPAPER_SERVICE * @throws IllegalArgumentException if token is invalid * @throws UnsupportedOperationException if this {@link Context} does not attach to a display or * the current number of window contexts without adding any view by * {@link WindowManager#addView} <b>exceeds five</b>. */ public @NonNull Context createWindowContext(@WindowType int type, @Nullable Bundle options) { throw new RuntimeException("Not implemented. Must override in a subclass."); Loading
core/java/android/view/WindowManagerGlobal.java +1 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,7 @@ public final class WindowManagerGlobal { public static final int ADD_INVALID_DISPLAY = -9; public static final int ADD_INVALID_TYPE = -10; public static final int ADD_INVALID_USER = -11; public static final int ADD_TOO_MANY_TOKENS = -12; @UnsupportedAppUsage private static WindowManagerGlobal sDefaultWindowManager; Loading
services/core/java/com/android/server/wm/DisplayContent.java +30 −0 Original line number Diff line number Diff line Loading @@ -5534,4 +5534,34 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } } /** * Returns the number of window tokens without surface on this display. A {@link WindowToken} * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}. * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and * limit the usage if the count exceeds a number. * * @param callingUid app calling uid * @return the number of window tokens without surface on this display * @see WindowToken#addWindow(WindowState) */ int getWindowTokensWithoutSurfaceCount(int callingUid) { List<WindowToken> tokens = new ArrayList<>(mTokenMap.values()); int count = 0; for (int i = tokens.size() - 1; i >= 0; i--) { final WindowToken token = tokens.get(i); if (callingUid != token.getOwnerUid()) { continue; } // Skip if token is an Activity if (token.asActivityRecord() != null) { continue; } if (token.mSurfaceControl != null) { continue; } count++; } return count; } }
services/core/java/com/android/server/wm/WindowManagerService.java +28 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; Loading Loading @@ -74,6 +75,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS; import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY; import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC; import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED; Loading Loading @@ -413,6 +415,12 @@ public class WindowManagerService extends IWindowManager.Stub private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; /** The maximum count of window tokens without surface that an app can register. */ private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5; /** System UI can create more window context... */ private static final int SYSTEM_UI_MULTIPLIER = 2; // TODO(b/143053092): Remove the settings if it becomes stable. private static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; boolean mIsFixedRotationTransformEnabled; Loading Loading @@ -2594,10 +2602,30 @@ public class WindowManagerService extends IWindowManager.Stub @Override public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName) { if (tokenCountExceed()) { return ADD_TOO_MANY_TOKENS; } return addWindowTokenWithOptions(binder, type, displayId, options, packageName, true /* fromClientToken */); } private boolean tokenCountExceed() { final int callingUid = Binder.getCallingUid(); // Don't check if caller is from system server. if (callingUid == myPid()) { return false; } final int limit = (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions")) ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE; synchronized (mGlobalLock) { int[] count = new int[1]; mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid)); return count[0] >= limit; } } private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options, String packageName, boolean fromClientToken) { final boolean callerCanManageAppTokens = Loading