Merge "Migrate WindowContext#onConfigurationChanged to ClientTransaction (2/n)" into main
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 853528f..12ffdb3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -65,6 +65,7 @@
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
+import android.app.servertransaction.WindowTokenClientController;
import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -6221,6 +6222,18 @@
false /* clearPending */);
}
+ @Override
+ public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
+ configuration, displayId);
+ }
+
+ @Override
+ public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) {
+ WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Sends windowing mode change callbacks to {@link Activity} if applicable.
*
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 49fb794..f7a43f4 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -163,6 +163,13 @@
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.WindowContext} window removal event. */
+ public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
+
/** Deliver result from another activity. */
public abstract void handleSendResult(
@NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
new file mode 100644
index 0000000..3ac642f
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
@@ -0,0 +1,135 @@
+/*
+ * 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/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
new file mode 100644
index 0000000..ed52a64
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -0,0 +1,112 @@
+/*
+ * 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.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} window removal message.
+ * @hide
+ */
+public class WindowContextWindowRemovalItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextWindowRemoval(mClientToken);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextWindowRemovalItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
+ WindowContextWindowRemovalItem instance =
+ ObjectPool.obtain(WindowContextWindowRemovalItem.class);
+ if (instance == null) {
+ instance = new WindowContextWindowRemovalItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextWindowRemovalItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ }
+
+ public static final @NonNull Creator<WindowContextWindowRemovalItem> CREATOR = new Creator<>() {
+ public WindowContextWindowRemovalItem createFromParcel(Parcel in) {
+ return new WindowContextWindowRemovalItem(in);
+ }
+
+ public WindowContextWindowRemovalItem[] newArray(int size) {
+ return new WindowContextWindowRemovalItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextWindowRemovalItem other = (WindowContextWindowRemovalItem) o;
+ return Objects.equals(mClientToken, other.mClientToken);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextWindowRemovalItem{clientToken=" + mClientToken + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
index 28e2040..5d123a0 100644
--- a/core/java/android/app/servertransaction/WindowTokenClientController.java
+++ b/core/java/android/app/servertransaction/WindowTokenClientController.java
@@ -27,6 +27,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.IWindowManager;
import android.window.WindowContext;
import android.window.WindowTokenClient;
@@ -41,6 +42,7 @@
*/
public class WindowTokenClientController {
+ private static final String TAG = WindowTokenClientController.class.getSimpleName();
private static WindowTokenClientController sController;
private final Object mLock = new Object();
@@ -61,7 +63,7 @@
/** Overrides the {@link #getInstance()} for test only. */
@VisibleForTesting
- public static void overrideInstance(@NonNull WindowTokenClientController controller) {
+ public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
synchronized (WindowTokenClientController.class) {
sController = controller;
}
@@ -90,7 +92,7 @@
if (configuration == null) {
return false;
}
- onWindowContainerTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -116,7 +118,7 @@
if (configuration == null) {
return false;
}
- onWindowContainerTokenAttached(client, displayId, configuration);
+ onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -153,7 +155,7 @@
}
}
- private void onWindowContainerTokenAttached(@NonNull WindowTokenClient client, int displayId,
+ private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
@NonNull Configuration configuration) {
synchronized (mLock) {
mWindowTokenClientMap.put(client.asBinder(), client);
@@ -161,4 +163,33 @@
client.onConfigurationChanged(configuration, displayId,
false /* shouldReportConfigChange */);
}
+
+ /** Called when receives {@link WindowContextConfigurationChangeItem}. */
+ public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onConfigurationChanged(configuration, displayId);
+ }
+ }
+
+ /** Called when receives {@link WindowContextWindowRemovalItem}. */
+ public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onWindowTokenRemoved();
+ }
+ }
+
+ @Nullable
+ private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient;
+ synchronized (mLock) {
+ windowTokenClient = mWindowTokenClientMap.get(clientToken);
+ }
+ if (windowTokenClient == null) {
+ Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
+ }
+ return windowTokenClient;
+ }
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b..6aa8506 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -34,6 +34,7 @@
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -184,6 +185,15 @@
}
}
+ /** Overrides the {@link #getWindowManagerService()} for test only. */
+ @VisibleForTesting
+ public static void overrideWindowManagerServiceForTesting(
+ @NonNull IWindowManager windowManager) {
+ synchronized (WindowManagerGlobal.class) {
+ sWindowManagerService = windowManager;
+ }
+ }
+
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 55b823b..47d3df87 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -20,7 +20,6 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.AnyThread;
-import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.app.ActivityThread;
@@ -97,9 +96,10 @@
* @param newConfig the updated {@link Configuration}
* @param newDisplayId the updated {@link android.view.Display} ID
*/
- @BinderThread
+ @AnyThread
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
}
@@ -188,9 +188,10 @@
}
}
- @BinderThread
+ @AnyThread
@Override
public void onWindowTokenRemoved() {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(
WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4857741..c1b55cd 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,6 +21,7 @@
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.app.Activity;
@@ -46,6 +49,7 @@
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.Intent;
import android.content.res.CompatibilityInfo;
@@ -70,6 +74,7 @@
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,7 +88,7 @@
/**
* Test for verifying {@link android.app.ActivityThread} class.
* Build/Install/Run:
- * atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
+ * atest FrameworksCoreTests:ActivityThreadTest
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -100,14 +105,24 @@
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ private WindowTokenClientController mOriginalWindowTokenClientController;
+
private ArrayList<VirtualDisplay> mCreatedVirtualDisplays;
+ @Before
+ public void setup() {
+ // Keep track of the original controller, so that it can be used to restore in tearDown()
+ // when there is override in some test cases.
+ mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+ }
+
@After
public void tearDown() {
if (mCreatedVirtualDisplays != null) {
mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
mCreatedVirtualDisplays = null;
}
+ WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
}
@Test
@@ -730,6 +745,39 @@
assertFalse(activity.enterPipSkipped());
}
+ @Test
+ public void testHandleWindowContextConfigurationChanged() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+ final Configuration configuration = new Configuration();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY));
+
+ verify(windowTokenClientController).onWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testHandleWindowContextWindowRemoval() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextWindowRemoval(clientToken));
+
+ verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int)} to try to push activity configuration to the activity for the given
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
new file mode 100644
index 0000000..7811e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.DEFAULT_DISPLAY;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextConfigurationChangeItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ */
+public class WindowContextConfigurationChangeItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+ .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
+ DEFAULT_DISPLAY);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
new file mode 100644
index 0000000..2c83c70
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextWindowRemovalItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
+ */
+public class WindowContextWindowRemovalItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mClientToken);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
new file mode 100644
index 0000000..3b2fe58
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowTokenClientController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowTokenClientControllerTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTokenClientControllerTest {
+
+ @Mock
+ private IWindowManager mWindowManagerService;
+ @Mock
+ private WindowTokenClient mWindowTokenClient;
+ @Mock
+ private IBinder mClientToken;
+ @Mock
+ private IBinder mWindowToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ private IWindowManager mOriginalWindowManagerService;
+
+ private WindowTokenClientController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mOriginalWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mWindowManagerService);
+ doReturn(mClientToken).when(mWindowTokenClient).asBinder();
+ mController = spy(WindowTokenClientController.getInstance());
+ }
+
+ @After
+ public void tearDown() {
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mOriginalWindowManagerService);
+ }
+
+ @Test
+ public void testAttachToDisplayArea() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertFalse(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowManagerService).attachWindowContextToDisplayArea(mWindowTokenClient,
+ TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayArea_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToDisplayContent() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowManagerService).attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayContent_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToWindowToken() throws RemoteException {
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ verify(mWindowManagerService).attachWindowContextToWindowToken(mWindowTokenClient,
+ mWindowToken);
+ }
+
+ @Test
+ public void testAttachToWindowToken_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testOnWindowContextConfigurationChanged() {
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testOnWindowContextWindowRemoved() {
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient).onWindowTokenRemoved();
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 813c360..5f2aecc 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -71,14 +71,14 @@
mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
mOriginalController = WindowTokenClientController.getInstance();
- WindowTokenClientController.overrideInstance(mWindowTokenClientController);
+ WindowTokenClientController.overrideForTesting(mWindowTokenClientController);
doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
eq(mMockToken), anyInt(), anyInt(), any());
}
@After
public void tearDown() {
- WindowTokenClientController.overrideInstance(mOriginalController);
+ WindowTokenClientController.overrideForTesting(mOriginalController);
}
@Test(expected = IllegalStateException.class)