From 74e1d37cad08384459054179e02d2015b093a2b4 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Mon, 21 Dec 2020 17:08:34 +0800 Subject: [PATCH] Add a UI Context for RootTaskDisplayAreaOrganizer Bug: 175416931 Test: atest SplitScreenTests WMShellUnitTests Change-Id: Id11bfef19076840e46222014e268a553bd94f2ef --- core/java/android/app/ContextImpl.java | 14 ++++ core/java/android/content/Context.java | 26 ++++++- core/java/android/content/ContextWrapper.java | 9 +++ .../shell/RootTaskDisplayAreaOrganizer.java | 74 ++++++++++++++++++- .../systemui/wmshell/WMShellBaseModule.java | 2 +- .../src/android/test/mock/MockContext.java | 6 ++ 6 files changed, 125 insertions(+), 6 deletions(-) diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 700d8ff36446..733270118909 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2489,6 +2489,20 @@ class ContextImpl extends Context { return new WindowContext(this, display, type, options); } + @NonNull + @Override + public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) { + if (display == null) { + throw new UnsupportedOperationException("Token context can only be created from " + + "other visual contexts, such as Activity or one created with " + + "Context#createDisplayContext(Display)"); + } + final ContextImpl tokenContext = createBaseWindowContext(token, display); + tokenContext.setResources(createWindowContextResources()); + return tokenContext; + } + + ContextImpl createBaseWindowContext(IBinder token, Display display) { ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag, mSplitName, token, mUser, mFlags, mClassLoader, null); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index abe7fdae0bf7..29ffa0b70313 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6014,10 +6014,13 @@ public abstract class Context { } /** - * A special version of {@link #createWindowContext(int, Bundle)} which also takes - * {@link Display}. The only difference between this API and - * {@link #createWindowContext(int, Bundle)} is that this API can create window context from - * any context even if the context which is not associated to a {@link Display} instance. + * Creates a {@code Context} for a non-{@link android.app.Activity activity} window on the given + * {@link Display}. + * + *

+ * Similar to {@link #createWindowContext(int, Bundle)}, but the {@code display} is passed in, + * instead of implicitly using the {@link #getDisplay() original Context's Display}. + *

* * @param display The {@link Display} to associate with * @param type Window type in {@link WindowManager.LayoutParams} @@ -6122,6 +6125,21 @@ public abstract class Context { @SystemApi public abstract Context createCredentialProtectedStorageContext(); + /** + * Creates a UI context with a {@code token}. The users of this API should handle this context's + * configuration changes. + * + * @param token The token to associate with the {@link Resources} + * @param display The display to associate with the token context + * + * @hide + */ + @UiContext + @NonNull + public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + /** * Gets the display adjustments holder for this context. This information * is provided on a per-application or activity basis and is used to simulate lower density diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index e450c08de280..8e2a8fbcbd2e 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.annotation.UiContext; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.compat.annotation.UnsupportedAppUsage; @@ -1049,6 +1050,14 @@ public class ContextWrapper extends Context { return mBase.createCredentialProtectedStorageContext(); } + /** @hide */ + @UiContext + @NonNull + @Override + public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) { + return mBase.createTokenContext(token, display); + } + @Override public boolean isDeviceProtectedStorage() { return mBase.isDeviceProtectedStorage(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java index 4a8b45076297..4ef489ffd480 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java @@ -16,13 +16,23 @@ package com.android.wm.shell; +import android.annotation.UiContext; +import android.app.ResourcesManager; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Configuration; +import android.hardware.display.DisplayManager; +import android.os.Binder; +import android.os.IBinder; import android.util.SparseArray; +import android.view.Display; import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.DisplayAreaInfo; import android.window.DisplayAreaOrganizer; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.io.PrintWriter; import java.util.ArrayList; @@ -42,8 +52,13 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { private final SparseArray> mListeners = new SparseArray<>(); - public RootTaskDisplayAreaOrganizer(Executor executor) { + private final SparseArray mDisplayAreaContexts = new SparseArray<>(); + + private final Context mContext; + + public RootTaskDisplayAreaOrganizer(Executor executor, Context context) { super(executor); + mContext = context; List infos = registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER); for (int i = infos.size() - 1; i >= 0; --i) { onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash()); @@ -103,6 +118,7 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { listeners.get(i).onDisplayAreaAppeared(displayAreaInfo); } } + applyConfigChangesToContext(displayId, displayAreaInfo.configuration); } @Override @@ -123,6 +139,7 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { listeners.get(i).onDisplayAreaVanished(displayAreaInfo); } } + mDisplayAreaContexts.remove(displayId); } @Override @@ -143,6 +160,33 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { listeners.get(i).onDisplayAreaInfoChanged(displayAreaInfo); } } + applyConfigChangesToContext(displayId, displayAreaInfo.configuration); + } + + /** + * Applies the {@link Configuration} to the {@link DisplayAreaContext} specified by + * {@code displayId}. + * + * @param displayId The ID of the {@link Display} which the {@link DisplayAreaContext} is + * associated with + * @param newConfig The propagated configuration + */ + private void applyConfigChangesToContext(int displayId, @NonNull Configuration newConfig) { + DisplayAreaContext daContext = mDisplayAreaContexts.get(displayId); + if (daContext == null) { + daContext = new DisplayAreaContext(mContext, displayId); + mDisplayAreaContexts.put(displayId, daContext); + } + daContext.updateConfigurationChanges(newConfig); + } + + /** + * Returns the UI context associated with RootTaskDisplayArea specified by {@code displayId}. + */ + @Nullable + @UiContext + public Context getContext(int displayId) { + return mDisplayAreaContexts.get(displayId); } public void dump(@NonNull PrintWriter pw, String prefix) { @@ -170,4 +214,32 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer { default void dump(@NonNull PrintWriter pw, String prefix) { } } + + /** + * A UI context to associate with a {@link com.android.server.wm.DisplayArea}. + * + * This context receives configuration changes through {@link DisplayAreaOrganizer} callbacks + * and the core implementation is {@link Context#createTokenContext(IBinder, Display)} to apply + * the configuration updates to the {@link android.content.res.Resources}. + */ + @UiContext + public static class DisplayAreaContext extends ContextWrapper { + private final IBinder mToken = new Binder(); + private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); + + public DisplayAreaContext(@NonNull Context context, int displayId) { + super(null); + final Display display = context.getSystemService(DisplayManager.class) + .getDisplay(displayId); + attachBaseContext(context.createTokenContext(mToken, display)); + } + + private void updateConfigurationChanges(@NonNull Configuration newConfig) { + final Configuration config = getResources().getConfiguration(); + final boolean configChanged = config.diff(newConfig) != 0; + if (configChanged) { + mResourcesManager.updateResourcesForActivity(mToken, newConfig, getDisplayId()); + } + } + } } \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index bcb2a8cec413..88cbddd4f088 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -285,7 +285,7 @@ public abstract class WMShellBaseModule { @Provides static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer( @ShellMainThread ShellExecutor mainExecutor, Context context) { - return new RootTaskDisplayAreaOrganizer(mainExecutor); + return new RootTaskDisplayAreaOrganizer(mainExecutor, context); } @WMSingleton diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index f7cebd12fcff..98e7b53a30cc 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -891,6 +891,12 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) { + throw new UnsupportedOperationException(); + } + @Override public boolean isDeviceProtectedStorage() { throw new UnsupportedOperationException();