Merge "Migrate WindowContext#onConfigurationChanged to ClientTransaction (7/n)" into main
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 58c2548..a09d7dc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -203,6 +203,7 @@
 import android.window.SizeConfigurationBuckets;
 import android.window.SplashScreen;
 import android.window.SplashScreenView;
+import android.window.WindowContextInfo;
 import android.window.WindowProviderService;
 import android.window.WindowTokenClientController;
 
@@ -6248,10 +6249,9 @@
     }
 
     @Override
-    public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId) {
-        WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
-                configuration, displayId);
+    public void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info) {
+        WindowTokenClientController.getInstance().onWindowContextInfoChanged(clientToken, info);
     }
 
     @Override
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f7a43f4..6753cb8 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -28,6 +28,7 @@
 import android.util.MergedConfiguration;
 import android.view.SurfaceControl;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
+import android.window.WindowContextInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
@@ -163,9 +164,9 @@
     public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
             Configuration overrideConfig, int displayId);
 
-    /** Deliver {@link android.window.WindowContext} configuration change. */
-    public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId);
+    /** Deliver {@link android.window.WindowContextInfo} change. */
+    public abstract void handleWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info);
 
     /** Deliver {@link android.window.WindowContext} window removal event. */
     public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
deleted file mode 100644
index 3ac642f..0000000
--- a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2023 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 android.app.servertransaction;
-
-import static android.view.Display.INVALID_DISPLAY;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ClientTransactionHandler;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.os.Parcel;
-
-import java.util.Objects;
-
-/**
- * {@link android.window.WindowContext} configuration change message.
- * @hide
- */
-public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
-
-    @Nullable
-    private IBinder mClientToken;
-    @Nullable
-    private Configuration mConfiguration;
-    private int mDisplayId;
-
-    @Override
-    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
-            @NonNull PendingTransactionActions pendingActions) {
-        client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
-    }
-
-    // ObjectPoolItem implementation
-
-    private WindowContextConfigurationChangeItem() {}
-
-    /** Obtains an instance initialized with provided params. */
-    public static WindowContextConfigurationChangeItem obtain(
-            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
-        WindowContextConfigurationChangeItem instance =
-                ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
-        if (instance == null) {
-            instance = new WindowContextConfigurationChangeItem();
-        }
-        instance.mClientToken = requireNonNull(clientToken);
-        instance.mConfiguration = requireNonNull(config);
-        instance.mDisplayId = displayId;
-
-        return instance;
-    }
-
-    @Override
-    public void recycle() {
-        mClientToken = null;
-        mConfiguration = null;
-        mDisplayId = INVALID_DISPLAY;
-        ObjectPool.recycle(this);
-    }
-
-    // Parcelable implementation
-
-    /** Writes to Parcel. */
-    @Override
-    public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeStrongBinder(mClientToken);
-        dest.writeTypedObject(mConfiguration, flags);
-        dest.writeInt(mDisplayId);
-    }
-
-    /** Reads from Parcel. */
-    private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
-        mClientToken = in.readStrongBinder();
-        mConfiguration = in.readTypedObject(Configuration.CREATOR);
-        mDisplayId = in.readInt();
-    }
-
-    public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
-            new Creator<>() {
-                public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
-                    return new WindowContextConfigurationChangeItem(in);
-                }
-
-                public WindowContextConfigurationChangeItem[] newArray(int size) {
-                    return new WindowContextConfigurationChangeItem[size];
-                }
-    };
-
-    @Override
-    public boolean equals(@Nullable Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
-        return Objects.equals(mClientToken, other.mClientToken)
-                && Objects.equals(mConfiguration, other.mConfiguration)
-                && mDisplayId == other.mDisplayId;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + Objects.hashCode(mClientToken);
-        result = 31 * result + Objects.hashCode(mConfiguration);
-        result = 31 * result + mDisplayId;
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
-                + ", config=" + mConfiguration
-                + ", displayId=" + mDisplayId
-                + "}";
-    }
-}
diff --git a/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
new file mode 100644
index 0000000..74721d5
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextInfoChangeItem.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 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 android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.window.WindowContextInfo;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextInfoChangeItem extends ClientTransactionItem {
+
+    @Nullable
+    private IBinder mClientToken;
+    @Nullable
+    private WindowContextInfo mInfo;
+
+    @Override
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+            @NonNull PendingTransactionActions pendingActions) {
+        client.handleWindowContextInfoChanged(mClientToken, mInfo);
+    }
+
+    // ObjectPoolItem implementation
+
+    private WindowContextInfoChangeItem() {}
+
+    /** Obtains an instance initialized with provided params. */
+    public static WindowContextInfoChangeItem obtain(
+            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+        WindowContextInfoChangeItem instance =
+                ObjectPool.obtain(WindowContextInfoChangeItem.class);
+        if (instance == null) {
+            instance = new WindowContextInfoChangeItem();
+        }
+        instance.mClientToken = requireNonNull(clientToken);
+        instance.mInfo = new WindowContextInfo(config, displayId);
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        mClientToken = null;
+        mInfo = null;
+        ObjectPool.recycle(this);
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mClientToken);
+        dest.writeTypedObject(mInfo, flags);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextInfoChangeItem(@NonNull Parcel in) {
+        mClientToken = in.readStrongBinder();
+        mInfo = in.readTypedObject(WindowContextInfo.CREATOR);
+    }
+
+    public static final @NonNull Creator<WindowContextInfoChangeItem> CREATOR =
+            new Creator<>() {
+                public WindowContextInfoChangeItem createFromParcel(Parcel in) {
+                    return new WindowContextInfoChangeItem(in);
+                }
+
+                public WindowContextInfoChangeItem[] newArray(int size) {
+                    return new WindowContextInfoChangeItem[size];
+                }
+    };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextInfoChangeItem other = (WindowContextInfoChangeItem) o;
+        return Objects.equals(mClientToken, other.mClientToken)
+                && Objects.equals(mInfo, other.mInfo);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mClientToken);
+        result = 31 * result + Objects.hashCode(mInfo);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextInfoChangeItem{clientToken=" + mClientToken
+                + ", info=" + mInfo
+                + "}";
+    }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c1474eb..d3b7a5b 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -71,6 +71,7 @@
 import android.window.ISurfaceSyncGroupCompletedListener;
 import android.window.ITaskFpsCallback;
 import android.window.ScreenCapture;
+import android.window.WindowContextInfo;
 
 /**
  * System private interface to the window manager.
@@ -858,10 +859,10 @@
      * @param displayId The display associated with the window context
      * @param options A bundle used to pass window-related options and choose the right DisplayArea
      *
-     * @return the DisplayArea's {@link android.app.res.Configuration} if the WindowContext is
-     * attached to the DisplayArea successfully. {@code null}, otherwise.
+     * @return the {@link WindowContextInfo} of the DisplayArea if the WindowContext is attached to
+     * the DisplayArea successfully. {@code null}, otherwise.
      */
-    @nullable Configuration attachWindowContextToDisplayArea(in IApplicationThread appThread,
+    @nullable WindowContextInfo attachWindowContextToDisplayArea(in IApplicationThread appThread,
             IBinder clientToken, int type, int displayId, in @nullable Bundle options);
 
     /**
@@ -879,13 +880,15 @@
      * the WindowContext's token}
      * @param token the WindowToken to attach
      *
+     * @return the {@link WindowContextInfo} of the WindowToken if the WindowContext is attached to
+     * the WindowToken successfully. {@code null}, otherwise.
      * @throws IllegalArgumentException if the {@code clientToken} have not been attached to
      * the server or the WindowContext's type doesn't match WindowToken {@code token}'s type.
      *
      * @see #attachWindowContextToDisplayArea(IBinder, int, int, Bundle)
      */
-    void attachWindowContextToWindowToken(in IApplicationThread appThread, IBinder clientToken,
-            IBinder token);
+    @nullable WindowContextInfo  attachWindowContextToWindowToken(in IApplicationThread appThread,
+            IBinder clientToken, IBinder token);
 
     /**
      * Attaches a {@code clientToken} to associate with DisplayContent.
@@ -899,11 +902,11 @@
      * the WindowContext's token}
      * @param displayId The display associated with the window context
      *
-     * @return the DisplayContent's {@link android.app.res.Configuration} if the Context is
-     * attached to the DisplayContent successfully. {@code null}, otherwise.
+     * @return the {@link WindowContextInfo} of the DisplayContent if the WindowContext is attached
+     * to the DisplayContent successfully. {@code null}, otherwise.
      * @throws android.view.WindowManager.InvalidDisplayException if the display ID is invalid
      */
-    @nullable Configuration attachWindowContextToDisplayContent(in IApplicationThread appThread,
+    @nullable WindowContextInfo attachWindowContextToDisplayContent(in IApplicationThread appThread,
             IBinder clientToken, int displayId);
 
     /**
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 36eef53..c9ac245 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -137,12 +137,14 @@
      * @see WindowProviderService#attachToWindowToken(IBinder))
      * @see IWindowManager#attachWindowContextToWindowToken
      */
-    public void attachToWindowToken(IBinder windowToken) {
+    public void attachToWindowToken(@NonNull IBinder windowToken) {
         if (mAttachedToDisplayArea != AttachStatus.STATUS_ATTACHED) {
             throw new IllegalStateException("The Window Context should have been attached"
                     + " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
         }
-        getWindowTokenClientController().attachToWindowToken(mToken, windowToken);
+        if (!getWindowTokenClientController().attachToWindowToken(mToken, windowToken)) {
+            Log.e(TAG, "attachToWindowToken fail");
+        }
     }
 
     /** Detaches the window context from the node it's currently associated with. */
diff --git a/core/java/android/window/WindowContextInfo.aidl b/core/java/android/window/WindowContextInfo.aidl
new file mode 100644
index 0000000..360431c
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2023 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 android.window;
+
+/** @hide */
+parcelable WindowContextInfo;
\ No newline at end of file
diff --git a/core/java/android/window/WindowContextInfo.java b/core/java/android/window/WindowContextInfo.java
new file mode 100644
index 0000000..3c21cd4
--- /dev/null
+++ b/core/java/android/window/WindowContextInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 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 android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Stores information about a particular window that a {@link WindowContext} is attached to.
+ * @hide
+ */
+public class WindowContextInfo implements Parcelable {
+
+    /**
+     * Configuration of the window.
+     */
+    @NonNull
+    private final Configuration mConfiguration;
+
+    /**
+     * The display id that the window is attached to.
+     */
+    private final int mDisplayId;
+
+    public WindowContextInfo(@NonNull Configuration configuration, int displayId) {
+        mConfiguration = requireNonNull(configuration);
+        mDisplayId = displayId;
+    }
+
+    @NonNull
+    public Configuration getConfiguration() {
+        return mConfiguration;
+    }
+
+    public int getDisplayId() {
+        return mDisplayId;
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mConfiguration, flags);
+        dest.writeInt(mDisplayId);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextInfo(@NonNull Parcel in) {
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+        mDisplayId = in.readInt();
+    }
+
+    public static final @NonNull Creator<WindowContextInfo> CREATOR = new Creator<>() {
+        public WindowContextInfo createFromParcel(Parcel in) {
+            return new WindowContextInfo(in);
+        }
+
+        public WindowContextInfo[] newArray(int size) {
+            return new WindowContextInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextInfo other = (WindowContextInfo) o;
+        return Objects.equals(mConfiguration, other.mConfiguration)
+                && mDisplayId == other.mDisplayId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mConfiguration);
+        result = 31 * result + mDisplayId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextInfo{config=" + mConfiguration
+                + ", displayId=" + mDisplayId
+                + "}";
+    }
+}
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 7458563..6a32529 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -99,6 +99,13 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
         // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
+        postOnConfigurationChanged(newConfig, newDisplayId);
+    }
+
+    /**
+     * Posts an {@link #onConfigurationChanged} to the main thread.
+     */
+    public void postOnConfigurationChanged(@NonNull Configuration newConfig, int newDisplayId) {
         mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
                 newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
     }
@@ -162,7 +169,6 @@
                 windowContext.dispatchConfigurationChanged(newConfig);
             }
 
-
             if (shouldReportConfigChange && diff != 0
                     && context instanceof WindowProviderService) {
                 final WindowProviderService windowProviderService = (WindowProviderService) context;
diff --git a/core/java/android/window/WindowTokenClientController.java b/core/java/android/window/WindowTokenClientController.java
index 14b9df6..7a84123 100644
--- a/core/java/android/window/WindowTokenClientController.java
+++ b/core/java/android/window/WindowTokenClientController.java
@@ -22,10 +22,9 @@
 import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.IApplicationThread;
-import android.app.servertransaction.WindowContextConfigurationChangeItem;
+import android.app.servertransaction.WindowContextInfoChangeItem;
 import android.app.servertransaction.WindowContextWindowRemovalItem;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -94,17 +93,17 @@
      */
     public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
             @WindowType int type, int displayId, @Nullable Bundle options) {
-        final Configuration configuration;
+        final WindowContextInfo info;
         try {
-            configuration = getWindowManagerService().attachWindowContextToDisplayArea(
+            info = getWindowManagerService().attachWindowContextToDisplayArea(
                     mAppThread, client, type, displayId, options);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        if (configuration == null) {
+        if (info == null) {
             return false;
         }
-        onWindowContextTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
         return true;
     }
 
@@ -121,16 +120,16 @@
         if (wms == null) {
             return false;
         }
-        final Configuration configuration;
+        final WindowContextInfo info;
         try {
-            configuration = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
+            info = wms.attachWindowContextToDisplayContent(mAppThread, client, displayId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        if (configuration == null) {
+        if (info == null) {
             return false;
         }
-        onWindowContextTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, info, false /* shouldReportConfigChange */);
         return true;
     }
 
@@ -139,19 +138,23 @@
      *
      * @param client The {@link WindowTokenClient} to attach.
      * @param windowToken the window token to associated with
+     * @return {@code true} if attaching successfully.
      */
-    public void attachToWindowToken(@NonNull WindowTokenClient client,
+    public boolean attachToWindowToken(@NonNull WindowTokenClient client,
             @NonNull IBinder windowToken) {
+        final WindowContextInfo info;
         try {
-            getWindowManagerService().attachWindowContextToWindowToken(
+            info = getWindowManagerService().attachWindowContextToWindowToken(
                     mAppThread, client, windowToken);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-        // We don't report configuration change for now.
-        synchronized (mLock) {
-            mWindowTokenClientMap.put(client.asBinder(), client);
+        if (info == null) {
+            return false;
         }
+        // We currently report configuration for WindowToken after attached.
+        onWindowContextTokenAttached(client, info, true /* shouldReportConfigChange */);
+        return true;
     }
 
     /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
@@ -168,21 +171,30 @@
         }
     }
 
-    private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
-            @NonNull Configuration configuration) {
+    private void onWindowContextTokenAttached(@NonNull WindowTokenClient client,
+            @NonNull WindowContextInfo info, boolean shouldReportConfigChange) {
         synchronized (mLock) {
             mWindowTokenClientMap.put(client.asBinder(), client);
         }
-        client.onConfigurationChanged(configuration, displayId,
-                false /* shouldReportConfigChange */);
+        if (shouldReportConfigChange) {
+            // Should trigger an #onConfigurationChanged callback to the WindowContext. Post the
+            // dispatch in the next loop to prevent the callback from being dispatched before
+            // #onCreate or WindowContext creation..
+            client.postOnConfigurationChanged(info.getConfiguration(), info.getDisplayId());
+        } else {
+            // Apply the config change directly in case users get stale values after WindowContext
+            // creation.
+            client.onConfigurationChanged(info.getConfiguration(), info.getDisplayId(),
+                    false /* shouldReportConfigChange */);
+        }
     }
 
-    /** Called when receives {@link WindowContextConfigurationChangeItem}. */
-    public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
-            @NonNull Configuration configuration, int displayId) {
+    /** Called when receives {@link WindowContextInfoChangeItem}. */
+    public void onWindowContextInfoChanged(@NonNull IBinder clientToken,
+            @NonNull WindowContextInfo info) {
         final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
         if (windowTokenClient != null) {
-            windowTokenClient.onConfigurationChanged(configuration, displayId);
+            windowTokenClient.onConfigurationChanged(info.getConfiguration(), info.getDisplayId());
         }
     }
 
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8da6d74..c904d96 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -64,6 +64,7 @@
 import android.util.MergedConfiguration;
 import android.view.Display;
 import android.view.View;
+import android.window.WindowContextInfo;
 import android.window.WindowTokenClientController;
 
 import androidx.test.filters.MediumTest;
@@ -753,13 +754,12 @@
         WindowTokenClientController.overrideForTesting(windowTokenClientController);
         final IBinder clientToken = mock(IBinder.class);
         final Configuration configuration = new Configuration();
+        final WindowContextInfo info = new WindowContextInfo(configuration, DEFAULT_DISPLAY);
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
-                .handleWindowContextConfigurationChanged(
-                        clientToken, configuration, DEFAULT_DISPLAY));
+                .handleWindowContextInfoChanged(clientToken, info));
 
-        verify(windowTokenClientController).onWindowContextConfigurationChanged(
-                clientToken, configuration, DEFAULT_DISPLAY);
+        verify(windowTokenClientController).onWindowContextInfoChanged(clientToken, info);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
similarity index 79%
rename from core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
rename to core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
index 7811e1a..37a517e 100644
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
@@ -23,6 +23,7 @@
 import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
+import android.window.WindowContextInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -30,12 +31,12 @@
 import org.mockito.MockitoAnnotations;
 
 /**
- * Tests for {@link WindowContextConfigurationChangeItem}.
+ * Tests for {@link WindowContextInfoChangeItem}.
  *
  * Build/Install/Run:
- *  atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ *  atest FrameworksCoreTests:WindowContextInfoChangeItemTest
  */
-public class WindowContextConfigurationChangeItemTest {
+public class WindowContextInfoChangeItemTest {
 
     @Mock
     private ClientTransactionHandler mHandler;
@@ -55,11 +56,11 @@
 
     @Test
     public void testExecute() {
-        final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+        final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
                 .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
         item.execute(mHandler, mToken, mPendingActions);
 
-        verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
-                DEFAULT_DISPLAY);
+        verify(mHandler).handleWindowContextInfoChanged(mClientToken,
+                new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
     }
 }
diff --git a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
index c4f7b3b..7bd6f05 100644
--- a/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowTokenClientControllerTest.java
@@ -64,6 +64,7 @@
     // Can't mock final class.
     private final Configuration mConfiguration = new Configuration();
 
+    private WindowContextInfo mWindowContextInfo;
     private WindowTokenClientController mController;
 
     @Before
@@ -72,6 +73,7 @@
         doReturn(mClientToken).when(mWindowTokenClient).asBinder();
         mController = spy(WindowTokenClientController.createInstanceForTesting());
         doReturn(mWindowManagerService).when(mController).getWindowManagerService();
+        mWindowContextInfo = new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY);
     }
 
     @Test
@@ -86,7 +88,7 @@
                 TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
                 any(), any(), anyInt(), anyInt(), any());
 
         assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
@@ -109,7 +111,7 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToDisplayArea(
                 any(), any(), anyInt(), anyInt(), any());
         mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
                 DEFAULT_DISPLAY, null /* options */);
@@ -129,8 +131,8 @@
                 DEFAULT_DISPLAY);
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
-                any(), any(), anyInt());
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToDisplayContent(any(), any(), anyInt());
 
         assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
         verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
@@ -150,8 +152,8 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
-        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayContent(
-                any(), any(), anyInt());
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToDisplayContent(any(), any(), anyInt());
         mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
         mController.detachIfNeeded(mWindowTokenClient);
 
@@ -160,11 +162,20 @@
 
     @Test
     public void testAttachToWindowToken() throws RemoteException {
-        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
 
+        assertFalse(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
         verify(mWindowManagerService).attachWindowContextToWindowToken(
                 ActivityThread.currentActivityThread().getApplicationThread(), mWindowTokenClient,
                 mWindowToken);
+        verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
+        verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
     }
 
     @Test
@@ -173,6 +184,15 @@
 
         verify(mWindowManagerService, never()).detachWindowContext(any());
 
+        doReturn(null).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContext(any());
+
+        doReturn(mWindowContextInfo).when(mWindowManagerService).attachWindowContextToWindowToken(
+                any(), any(), any());
         mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
         mController.detachIfNeeded(mWindowTokenClient);
 
@@ -180,28 +200,43 @@
     }
 
     @Test
-    public void testOnWindowContextConfigurationChanged() {
-        mController.onWindowContextConfigurationChanged(
-                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+    public void testOnWindowContextInfoChanged() throws RemoteException {
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        // No invoke if not attached.
+        mController.onWindowContextInfoChanged(mClientToken, mWindowContextInfo);
 
         verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
 
-        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        // Invoke postOnConfigurationChanged when attached
+        assertTrue(mController.attachToWindowToken(mWindowTokenClient, mWindowToken));
 
-        mController.onWindowContextConfigurationChanged(
-                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+        verify(mWindowTokenClient).postOnConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
 
-        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+        // Invoke onConfigurationChanged when onWindowContextInfoChanged
+        mController.onWindowContextInfoChanged(
+                mClientToken, new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY + 1));
+
+        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY + 1);
     }
 
     @Test
-    public void testOnWindowContextWindowRemoved() {
+    public void testOnWindowContextWindowRemoved() throws RemoteException {
+        doReturn(mWindowContextInfo).when(mWindowManagerService)
+                .attachWindowContextToWindowToken(any(), any(), any());
+
+        // No invoke if not attached.
         mController.onWindowContextWindowRemoved(mClientToken);
 
         verify(mWindowTokenClient, never()).onWindowTokenRemoved();
 
+        // No invoke if not onWindowTokenRemoved.
         mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
 
+        verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+        // Invoke onWindowTokenRemoved when onWindowContextWindowRemoved
         mController.onWindowContextWindowRemoved(mClientToken);
 
         verify(mWindowTokenClient).onWindowTokenRemoved();
diff --git a/services/core/java/com/android/server/wm/WindowContextListenerController.java b/services/core/java/com/android/server/wm/WindowContextListenerController.java
index 26aab07..726ae5c 100644
--- a/services/core/java/com/android/server/wm/WindowContextListenerController.java
+++ b/services/core/java/com/android/server/wm/WindowContextListenerController.java
@@ -76,7 +76,7 @@
             @NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
             @WindowType int type, @Nullable Bundle options) {
         registerWindowContainerListener(wpc, clientToken, container, type, options,
-                true /* shouDispatchConfigWhenRegistering */);
+                true /* shouldDispatchConfigWhenRegistering */);
     }
 
     /**
@@ -91,19 +91,19 @@
      * @param container the {@link WindowContainer} which the listener is going to listen to.
      * @param type the window type
      * @param options a bundle used to pass window-related options.
-     * @param shouDispatchConfigWhenRegistering {@code true} to indicate the current
+     * @param shouldDispatchConfigWhenRegistering {@code true} to indicate the current
      *                {@code container}'s config will dispatch to the client side when
      *                registering the {@link WindowContextListenerImpl}
      */
     void registerWindowContainerListener(@NonNull WindowProcessController wpc,
             @NonNull IBinder clientToken, @NonNull WindowContainer<?> container,
             @WindowType int type, @Nullable Bundle options,
-            boolean shouDispatchConfigWhenRegistering) {
+            boolean shouldDispatchConfigWhenRegistering) {
         WindowContextListenerImpl listener = mListeners.get(clientToken);
         if (listener == null) {
             listener = new WindowContextListenerImpl(wpc, clientToken, container, type,
                     options);
-            listener.register(shouDispatchConfigWhenRegistering);
+            listener.register(shouldDispatchConfigWhenRegistering);
         } else {
             updateContainerForWindowContextListener(clientToken, container);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 210378f..261d6bc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -305,6 +305,7 @@
 import android.window.ScreenCapture;
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
+import android.window.WindowContextInfo;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -2737,7 +2738,7 @@
 
     @Nullable
     @Override
-    public Configuration attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
+    public WindowContextInfo attachWindowContextToDisplayArea(@NonNull IApplicationThread appThread,
             @NonNull IBinder clientToken, @LayoutParams.WindowType int type, int displayId,
             @Nullable Bundle options) {
         Objects.requireNonNull(appThread);
@@ -2766,8 +2767,8 @@
                 final DisplayArea<?> da = dc.findAreaForWindowType(type, options,
                         callerCanManageAppTokens, false /* roundedCornerOverlay */);
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
-                        da, type, options, false /* shouDispatchConfigWhenRegistering */);
-                return da.getConfiguration();
+                        da, type, options, false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(da.getConfiguration(), displayId);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2776,8 +2777,8 @@
 
     @Nullable
     @Override
-    public Configuration attachWindowContextToDisplayContent(@NonNull IApplicationThread appThread,
-            @NonNull IBinder clientToken, int displayId) {
+    public WindowContextInfo attachWindowContextToDisplayContent(
+            @NonNull IApplicationThread appThread, @NonNull IBinder clientToken, int displayId) {
         Objects.requireNonNull(appThread);
         Objects.requireNonNull(clientToken);
         final int callingPid = Binder.getCallingPid();
@@ -2809,16 +2810,17 @@
 
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                         dc, INVALID_WINDOW_TYPE, null /* options */,
-                        false /* shouDispatchConfigWhenRegistering */);
-                return dc.getConfiguration();
+                        false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(dc.getConfiguration(), displayId);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
+    @Nullable
     @Override
-    public void attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
+    public WindowContextInfo attachWindowContextToWindowToken(@NonNull IApplicationThread appThread,
             @NonNull IBinder clientToken, @NonNull IBinder token) {
         Objects.requireNonNull(appThread);
         Objects.requireNonNull(clientToken);
@@ -2834,13 +2836,13 @@
                 if (wpc == null) {
                     ProtoLog.w(WM_ERROR, "attachWindowContextToWindowToken: calling from"
                             + " non-existing process pid=%d uid=%d", callingPid, callingUid);
-                    return;
+                    return null;
                 }
                 final WindowToken windowToken = mRoot.getWindowToken(token);
                 if (windowToken == null) {
                     ProtoLog.w(WM_ERROR, "Then token:%s is invalid. It might be "
                             + "removed", token);
-                    return;
+                    return null;
                 }
                 final int type = mWindowContextListenerController.getWindowType(clientToken);
                 if (type == INVALID_WINDOW_TYPE) {
@@ -2854,10 +2856,13 @@
                 }
                 if (!mWindowContextListenerController.assertCallerCanModifyListener(clientToken,
                         callerCanManageAppTokens, callingUid)) {
-                    return;
+                    return null;
                 }
                 mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
-                        windowToken, windowToken.windowType, windowToken.mOptions);
+                        windowToken, windowToken.windowType, windowToken.mOptions,
+                                               false /* shouldDispatchConfigWhenRegistering */);
+                return new WindowContextInfo(windowToken.getConfiguration(),
+                        windowToken.getDisplayContent().getDisplayId());
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index 1180ebd..c3db241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -47,6 +47,7 @@
 import android.view.IWindowManager;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.WindowContextInfo;
 import android.window.WindowTokenClient;
 
 import com.android.server.inputmethod.InputMethodDialogWindowContext;
@@ -99,7 +100,7 @@
             final WindowProcessController wpc = mAtm.getProcessController(appThread);
             mWm.mWindowContextListenerController.registerWindowContainerListener(wpc, clientToken,
                     dc.getImeContainer(), TYPE_INPUT_METHOD_DIALOG, null /* options */);
-            return dc.getImeContainer().getConfiguration();
+            return new WindowContextInfo(dc.getImeContainer().getConfiguration(), displayId);
         }).when(mIWindowManager).attachWindowContextToDisplayArea(any(), any(),
                 eq(TYPE_INPUT_METHOD_DIALOG), anyInt(), any());
         mDisplayManagerGlobal = DisplayManagerGlobal.getInstance();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 325176f..55fda05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -468,7 +468,7 @@
         mWm.attachWindowContextToWindowToken(mAppThread, new Binder(), windowToken.token);
 
         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(
-                any(), any(), any(), anyInt(), any());
+                any(), any(), any(), anyInt(), any(), anyBoolean());
     }
 
     @Test
@@ -484,9 +484,9 @@
         final IBinder clientToken = new Binder();
         mWm.attachWindowContextToWindowToken(mAppThread, clientToken, windowToken.token);
         final WindowProcessController wpc = mAtm.getProcessController(mAppThread);
-        verify(mWm.mWindowContextListenerController).registerWindowContainerListener(eq(wpc),
-                eq(clientToken), eq(windowToken), eq(TYPE_INPUT_METHOD),
-                eq(windowToken.mOptions));
+        verify(mWm.mWindowContextListenerController).registerWindowContainerListener(wpc,
+                clientToken, windowToken, TYPE_INPUT_METHOD, windowToken.mOptions,
+                false /* shouldDispatchConfigWhenRegistering */);
     }
 
     @Test
@@ -514,7 +514,7 @@
                 new InsetsSourceControl.Array(), new Rect(), new float[1]);
 
         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
-                any(), any(), anyInt(), any());
+                any(), any(), anyInt(), any(), anyBoolean());
     }
 
     @Test