Migrate WindowContext#onConfigurationChanged to ClientTransaction (2/n)

Introduce WindowContextConfigurationChangeItem and
WindowContextWindowRemovalItem to dispatch non-activity window
change/removal. This is not functional until the server side is hooked.

Bug: 290876897
Test: atest FrameworksCoreTests:WindowTokenClientControllerTest
Test: atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
Test: atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
Test: atest FrameworksCoreTests:ActivityThreadTest
Change-Id: I4e6346cc267d7ccb8b3ab0e8c6eb67c8fa12e091
This commit is contained in:
Chris Li
2023-07-14 13:02:56 +08:00
parent b37168d3e8
commit 7ba782a838
12 changed files with 706 additions and 10 deletions

View File

@@ -65,6 +65,7 @@ import android.app.servertransaction.PendingTransactionActions.StopInfo;
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 @@ public final class ActivityThread extends ClientTransactionHandler
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.
*

View File

@@ -163,6 +163,13 @@ public abstract class ClientTransactionHandler {
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);

View File

@@ -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
+ "}";
}
}

View File

@@ -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 + "}";
}
}

View File

@@ -27,6 +27,7 @@ import android.os.Bundle;
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 @@ import com.android.internal.annotations.VisibleForTesting;
*/
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 @@ public class WindowTokenClientController {
/** 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 @@ public class WindowTokenClientController {
if (configuration == null) {
return false;
}
onWindowContainerTokenAttached(client, displayId, configuration);
onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -116,7 +118,7 @@ public class WindowTokenClientController {
if (configuration == null) {
return false;
}
onWindowContainerTokenAttached(client, displayId, configuration);
onWindowContextTokenAttached(client, displayId, configuration);
return true;
}
@@ -153,7 +155,7 @@ public class WindowTokenClientController {
}
}
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 @@ public class WindowTokenClientController {
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;
}
}

View File

@@ -34,6 +34,7 @@ import android.util.ArraySet;
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 @@ public final class WindowManagerGlobal {
}
}
/** 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) {

View File

@@ -20,7 +20,6 @@ import static android.window.ConfigurationHelper.isDifferentDisplay;
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 @@ public class WindowTokenClient extends IWindowToken.Stub {
* @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 @@ public class WindowTokenClient extends IWindowToken.Stub {
}
}
@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());
}

View File

@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_EDIT;
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.assertEquals;
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.ConfigurationChangeItem;
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 androidx.test.runner.AndroidJUnit4;
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 @@ import java.util.function.Consumer;
/**
* 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 @@ public class ActivityThreadTest {
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 @@ public class ActivityThreadTest {
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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -71,14 +71,14 @@ public class WindowContextControllerTest {
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)