diff --git a/core/java/android/window/WindowContext.java b/core/java/android/window/WindowContext.java index bff2252e3b6d7..375f4cf63c3b0 100644 --- a/core/java/android/window/WindowContext.java +++ b/core/java/android/window/WindowContext.java @@ -27,10 +27,7 @@ import android.content.ContextWrapper; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; -import android.os.RemoteException; -import android.view.IWindowManager; import android.view.WindowManager; -import android.view.WindowManagerGlobal; import com.android.internal.annotations.VisibleForTesting; @@ -48,13 +45,11 @@ import java.lang.ref.Reference; @UiContext public class WindowContext extends ContextWrapper { private final WindowManager mWindowManager; - private final IWindowManager mWms; - private final @NonNull IBinder mToken; private final @WindowManager.LayoutParams.WindowType int mType; private final @Nullable Bundle mOptions; - private boolean mListenerRegistered; private final ComponentCallbacksController mCallbacksController = new ComponentCallbacksController(); + private final WindowContextController mController; /** * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to @@ -69,9 +64,9 @@ public class WindowContext extends ContextWrapper { mType = type; mOptions = options; - mWms = WindowManagerGlobal.getWindowManagerService(); - mToken = getWindowContextToken(); mWindowManager = createWindowContextWindowManager(this); + IBinder token = getWindowContextToken(); + mController = new WindowContextController(token); Reference.reachabilityFence(this); } @@ -81,12 +76,7 @@ public class WindowContext extends ContextWrapper { * to receive configuration changes of the associated {@link WindowManager} node. */ public void registerWithServer() { - try { - mListenerRegistered = mWms.registerWindowContextListener(mToken, mType, getDisplayId(), - mOptions); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mController.registerListener(mType, getDisplayId(), mOptions); } @Override @@ -106,14 +96,7 @@ public class WindowContext extends ContextWrapper { /** Used for test to invoke because we can't invoke finalize directly. */ @VisibleForTesting public void release() { - if (mListenerRegistered) { - mListenerRegistered = false; - try { - mWms.unregisterWindowContextListener(mToken); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } + mController.unregisterListenerIfNeeded(); destroy(); } diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java new file mode 100644 index 0000000000000..61434145290fa --- /dev/null +++ b/core/java/android/window/WindowContextController.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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 android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.IWindowManager; +import android.view.WindowManager.LayoutParams.WindowType; +import android.view.WindowManagerGlobal; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * The controller to manage {@link WindowContext} listener, such as registering and unregistering + * the listener. + * + * @hide + */ +public class WindowContextController { + private final IWindowManager mWms; + @VisibleForTesting + public boolean mListenerRegistered; + @NonNull + private final IBinder mToken; + + /** + * Window Context Controller constructor + * + * @param token The token to register to the window context listener. It is usually from + * {@link Context#getWindowContextToken()}. + */ + public WindowContextController(@NonNull IBinder token) { + mToken = token; + mWms = WindowManagerGlobal.getWindowManagerService(); + } + + /** Used for test only. DO NOT USE it in production code. */ + @VisibleForTesting + public WindowContextController(@NonNull IBinder token, IWindowManager mockWms) { + mToken = token; + mWms = mockWms; + } + + /** + * Registers the {@code mToken} to the window context listener. + * + * @param type The window type of the {@link WindowContext} + * @param displayId The {@link Context#getDisplayId() ID of display} to associate with + * @param options The window context launched option + */ + public void registerListener(@WindowType int type, int displayId, @Nullable Bundle options) { + if (mListenerRegistered) { + throw new UnsupportedOperationException("A Window Context can only register a listener" + + " once."); + } + try { + mListenerRegistered = mWms.registerWindowContextListener(mToken, type, displayId, + options); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters the window context listener associated with the {@code mToken} if it has been + * registered. + */ + public void unregisterListenerIfNeeded() { + if (mListenerRegistered) { + try { + mWms.unregisterWindowContextListener(mToken); + mListenerRegistered = false; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } +} diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java new file mode 100644 index 0000000000000..e4fc19c0e58da --- /dev/null +++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2021 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 android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.os.Binder; +import android.platform.test.annotations.Presubmit; +import android.view.IWindowManager; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link WindowContextController} + * + *
Build/Install/Run: + * atest FrameworksCoreTests:WindowContextControllerTest + * + *
This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class WindowContextControllerTest { + private WindowContextController mController; + private IWindowManager mMockWms; + + @Before + public void setUp() throws Exception { + mMockWms = mock(IWindowManager.class); + mController = new WindowContextController(new Binder(), mMockWms); + + doReturn(true).when(mMockWms).registerWindowContextListener( + any(), anyInt(), anyInt(), any()); + } + + @Test(expected = UnsupportedOperationException.class) + public void testRegisterListenerTwiceThrowException() { + mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, + null /* options */); + mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, + null /* options */); + } + + @Test + public void testUnregisterListenerIfNeeded_NotRegisteredYet_DoNothing() throws Exception { + mController.unregisterListenerIfNeeded(); + + verify(mMockWms, never()).registerWindowContextListener(any(), anyInt(), anyInt(), any()); + } + + @Test + public void testRegisterAndUnRegisterListener() { + mController.registerListener(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, + null /* options */); + + assertThat(mController.mListenerRegistered).isTrue(); + + mController.unregisterListenerIfNeeded(); + + assertThat(mController.mListenerRegistered).isFalse(); + } +} diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 7a0be97fa2bfc..bcd6ed73e1333 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -61,7 +61,8 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.PendingInsetsControllerTest", "android.window.WindowContextTest", "android.window.WindowMetricsHelperTest", - "android.app.activity.ActivityThreadTest" + "android.app.activity.ActivityThreadTest", + "android.window.WindowContextControllerTest" }; public FrameworksTestsFilter(Bundle testArgs) {