diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 40b543712b398..1327f9c6e8270 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -93,6 +93,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.Context; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -2843,6 +2844,16 @@ public interface WindowManager extends ViewManager { */ public IBinder token = null; + /** + * The token of {@link android.app.WindowContext}. It is usually a + * {@link android.app.WindowTokenClient} and is used for updating + * {@link android.content.res.Resources} from {@link Configuration} propagated from the + * server side. + * + * @hide + */ + public IBinder mWindowContextToken = null; + /** * Name of the package owning this window. */ @@ -3552,6 +3563,7 @@ public interface WindowManager extends ViewManager { out.writeFloat(buttonBrightness); out.writeInt(rotationAnimation); out.writeStrongBinder(token); + out.writeStrongBinder(mWindowContextToken); out.writeString(packageName); TextUtils.writeToParcel(mTitle, out, parcelableFlags); out.writeInt(screenOrientation); @@ -3620,6 +3632,7 @@ public interface WindowManager extends ViewManager { buttonBrightness = in.readFloat(); rotationAnimation = in.readInt(); token = in.readStrongBinder(); + mWindowContextToken = in.readStrongBinder(); packageName = in.readString(); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); screenOrientation = in.readInt(); @@ -3781,6 +3794,11 @@ public interface WindowManager extends ViewManager { // already have one. token = o.token; } + if (mWindowContextToken == null) { + // NOTE: token only copied if the recipient doesn't + // already have one. + mWindowContextToken = o.mWindowContextToken; + } if (packageName == null) { // NOTE: packageName only copied if the recipient doesn't // already have one. diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 391e55a9050a9..23842b3a41acd 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UiContext; import android.app.ResourcesManager; import android.compat.annotation.UnsupportedAppUsage; @@ -74,23 +75,42 @@ public final class WindowManagerImpl implements WindowManager { public final Context mContext; private final Window mParentWindow; + /** + * If {@link LayoutParams#token} is {@code null} and no parent window is specified, the value + * of {@link LayoutParams#token} will be overridden to {@code mDefaultToken}. + */ private IBinder mDefaultToken; + /** + * This token will be set to {@link LayoutParams#mWindowContextToken} and used to receive + * configuration changes from the server side. + */ + @Nullable + private final IBinder mWindowContextToken; + public WindowManagerImpl(Context context) { - this(context, null); + this(context, null /* parentWindow */, null /* clientToken */); } - private WindowManagerImpl(Context context, Window parentWindow) { + private WindowManagerImpl(Context context, Window parentWindow, + @Nullable IBinder windowContextToken) { mContext = context; mParentWindow = parentWindow; + mWindowContextToken = windowContextToken; } public WindowManagerImpl createLocalWindowManager(Window parentWindow) { - return new WindowManagerImpl(mContext, parentWindow); + return new WindowManagerImpl(mContext, parentWindow, mWindowContextToken); } public WindowManagerImpl createPresentationWindowManager(Context displayContext) { - return new WindowManagerImpl(displayContext, mParentWindow); + return new WindowManagerImpl(displayContext, mParentWindow, mWindowContextToken); + } + + /** Creates a {@link WindowManager} for a {@link android.app.WindowContext}. */ + public static WindowManager createWindowContextWindowManager(Context context) { + final IBinder clientToken = context.getWindowContextToken(); + return new WindowManagerImpl(context, null /* parentWindow */, clientToken); } /** @@ -105,30 +125,27 @@ public final class WindowManagerImpl implements WindowManager { @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { - applyDefaultToken(params); + applyTokens(params); mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId()); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { - applyDefaultToken(params); + applyTokens(params); mGlobal.updateViewLayout(view, params); } - private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) { - // Only use the default token if we don't have a parent window. - if (mDefaultToken != null && mParentWindow == null) { - if (!(params instanceof WindowManager.LayoutParams)) { - throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); - } - - // Only use the default token if we don't already have a token. - final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; - if (wparams.token == null) { - wparams.token = mDefaultToken; - } + private void applyTokens(@NonNull ViewGroup.LayoutParams params) { + if (!(params instanceof WindowManager.LayoutParams)) { + throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } + final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; + // Only use the default token if we don't have a parent window and a token. + if (mDefaultToken != null && mParentWindow == null && wparams.token == null) { + wparams.token = mDefaultToken; + } + wparams.mWindowContextToken = mWindowContextToken; } @Override diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java index 1c2f32f90121c..dcf5e025f82b1 100644 --- a/core/tests/coretests/src/android/app/WindowContextTest.java +++ b/core/tests/coretests/src/android/app/WindowContextTest.java @@ -19,6 +19,7 @@ package android.app; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -28,7 +29,10 @@ import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.IWindowManager; +import android.view.View; +import android.view.WindowManager; import android.view.WindowManagerGlobal; +import android.view.WindowManagerImpl; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; @@ -50,23 +54,41 @@ import org.junit.runner.RunWith; @SmallTest @Presubmit public class WindowContextTest { + private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + private final WindowContext mWindowContext = createWindowContext(); + @Test public void testWindowContextRelease_doRemoveWindowToken() throws Throwable { - final Context instContext = InstrumentationRegistry.getInstrumentation() - .getTargetContext(); - final Display display = instContext.getSystemService(DisplayManager.class) - .getDisplay(DEFAULT_DISPLAY); - final Context context = instContext.createDisplayContext(display); - final WindowContext windowContext = new WindowContext(context, TYPE_APPLICATION_OVERLAY, - null /* options */); - - final IBinder token = windowContext.getWindowContextToken(); - + final IBinder token = mWindowContext.getWindowContextToken(); final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); + assertTrue("Token must be registered to WMS", wms.isWindowToken(token)); - windowContext.release(); + mWindowContext.release(); assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token)); } + + @Test + public void testCreateWindowContextWindowManagerAttachClientToken() { + final WindowManager windowContextWm = WindowManagerImpl + .createWindowContextWindowManager(mWindowContext); + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + mInstrumentation.runOnMainSync(() -> { + final View view = new View(mWindowContext); + windowContextWm.addView(view, params); + }); + + assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken); + } + + private WindowContext createWindowContext() { + final Context instContext = mInstrumentation.getTargetContext(); + final Display display = instContext.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + final Context context = instContext.createDisplayContext(display); + return new WindowContext(context, TYPE_APPLICATION_OVERLAY, + null /* options */); + } } diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java index 051ece38b26f9..3725d4df2f5b7 100644 --- a/services/core/java/com/android/server/wm/WindowContextListenerController.java +++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java @@ -29,7 +29,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import android.view.View; -import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -46,7 +45,7 @@ import java.util.Objects; * {@link WindowManagerService#registerWindowContextListener(IBinder, int, int, Bundle)} * automatically. *