From bf0d57b9b4bd2d46aba185f1c53842b7044ae124 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Mon, 19 Nov 2018 16:09:58 +0900 Subject: Move WindowManager tests to WmTests Bug: 113800711 Test: All presubmit and non-flaky tests in WmTests pass $ tradefed.sh run commandAndExit WmTests \ --include-annotation android.platform.test.annotations.Presubmit \ --exclude-annotation androidx.test.filters.FlakyTest Change-Id: I593eb03c8ee3c297eae85e39434d04138a67ab15 --- services/tests/servicestests/AndroidManifest.xml | 9 - .../wm/AnimatingAppWindowTokenRegistryTest.java | 104 --- .../server/wm/AppTransitionControllerTest.java | 80 -- .../com/android/server/wm/AppTransitionTests.java | 169 ---- .../server/wm/AppWindowTokenAnimationTests.java | 116 --- .../com/android/server/wm/AppWindowTokenTests.java | 427 ---------- .../server/wm/BoundsAnimationControllerTests.java | 588 -------------- .../src/com/android/server/wm/DimmerTests.java | 277 ------- .../com/android/server/wm/DisplayContentTests.java | 623 -------------- .../server/wm/DisplayPolicyInsetsTests.java | 180 ----- .../server/wm/DisplayPolicyLayoutTests.java | 422 ---------- .../com/android/server/wm/DisplayPolicyTests.java | 202 ----- .../android/server/wm/DisplayPolicyTestsBase.java | 184 ----- .../server/wm/DisplayWindowSettingsTests.java | 445 ---------- .../wm/DockedStackDividerControllerTests.java | 91 --- .../android/server/wm/DragDropControllerTests.java | 190 ----- .../server/wm/InsetsSourceProviderTest.java | 78 -- .../server/wm/InsetsStateControllerTest.java | 79 -- .../server/wm/PinnedStackControllerTest.java | 81 -- .../server/wm/RecentsAnimationControllerTest.java | 140 ---- .../server/wm/RemoteAnimationControllerTest.java | 220 ----- .../server/wm/StackWindowControllerTests.java | 111 --- .../server/wm/SurfaceAnimationRunnerTest.java | 250 ------ .../com/android/server/wm/SurfaceAnimatorTest.java | 309 ------- .../com/android/server/wm/TaskPositionerTests.java | 496 ------------ .../server/wm/TaskPositioningControllerTests.java | 119 --- .../android/server/wm/TaskSnapshotCacheTest.java | 113 --- .../server/wm/TaskSnapshotControllerTest.java | 112 --- .../server/wm/TaskSnapshotPersisterLoaderTest.java | 292 ------- .../server/wm/TaskSnapshotPersisterTestBase.java | 135 ---- .../android/server/wm/TaskSnapshotSurfaceTest.java | 247 ------ .../server/wm/TaskStackContainersTests.java | 128 --- .../src/com/android/server/wm/TaskStackTests.java | 109 --- .../wm/TaskWindowContainerControllerTests.java | 145 ---- .../src/com/android/server/wm/TestIWindow.java | 103 --- .../android/server/wm/TestWindowManagerPolicy.java | 403 --------- .../wm/UnknownAppVisibilityControllerTest.java | 87 -- .../server/wm/WallpaperControllerTests.java | 81 -- .../server/wm/WindowConfigurationTests.java | 243 ------ .../server/wm/WindowContainerControllerTests.java | 111 --- .../android/server/wm/WindowContainerTests.java | 898 --------------------- .../server/wm/WindowContainerTraversalTests.java | 63 -- .../com/android/server/wm/WindowFrameTests.java | 522 ------------ .../server/wm/WindowManagerServiceRule.java | 210 ----- .../server/wm/WindowManagerServiceRuleTest.java | 44 - .../com/android/server/wm/WindowStateTests.java | 422 ---------- .../src/com/android/server/wm/WindowTestUtils.java | 322 -------- .../src/com/android/server/wm/WindowTestsBase.java | 400 --------- .../com/android/server/wm/WindowTokenTests.java | 132 --- .../src/com/android/server/wm/ZOrderingTests.java | 422 ---------- services/tests/wmtests/AndroidManifest.xml | 2 + .../wm/AnimatingAppWindowTokenRegistryTest.java | 104 +++ .../server/wm/AppTransitionControllerTest.java | 80 ++ .../com/android/server/wm/AppTransitionTests.java | 170 ++++ .../server/wm/AppWindowTokenAnimationTests.java | 117 +++ .../com/android/server/wm/AppWindowTokenTests.java | 431 ++++++++++ .../server/wm/BoundsAnimationControllerTests.java | 588 ++++++++++++++ .../src/com/android/server/wm/DimmerTests.java | 278 +++++++ .../com/android/server/wm/DisplayContentTests.java | 623 ++++++++++++++ .../server/wm/DisplayPolicyInsetsTests.java | 177 ++++ .../server/wm/DisplayPolicyLayoutTests.java | 419 ++++++++++ .../com/android/server/wm/DisplayPolicyTests.java | 199 +++++ .../android/server/wm/DisplayPolicyTestsBase.java | 184 +++++ .../server/wm/DisplayWindowSettingsTests.java | 446 ++++++++++ .../wm/DockedStackDividerControllerTests.java | 88 ++ .../android/server/wm/DragDropControllerTests.java | 191 +++++ .../server/wm/InsetsSourceProviderTest.java | 76 ++ .../server/wm/InsetsStateControllerTest.java | 77 ++ .../server/wm/PinnedStackControllerTest.java | 82 ++ .../server/wm/RecentsAnimationControllerTest.java | 140 ++++ .../server/wm/RemoteAnimationControllerTest.java | 221 +++++ .../server/wm/StackWindowControllerTests.java | 111 +++ .../server/wm/SurfaceAnimationRunnerTest.java | 251 ++++++ .../com/android/server/wm/SurfaceAnimatorTest.java | 310 +++++++ .../com/android/server/wm/TaskPositionerTests.java | 496 ++++++++++++ .../server/wm/TaskPositioningControllerTests.java | 120 +++ .../android/server/wm/TaskSnapshotCacheTest.java | 113 +++ .../server/wm/TaskSnapshotControllerTest.java | 112 +++ .../server/wm/TaskSnapshotPersisterLoaderTest.java | 292 +++++++ .../server/wm/TaskSnapshotPersisterTestBase.java | 135 ++++ .../android/server/wm/TaskSnapshotSurfaceTest.java | 248 ++++++ .../server/wm/TaskStackContainersTests.java | 128 +++ .../src/com/android/server/wm/TaskStackTests.java | 109 +++ .../wm/TaskWindowContainerControllerTests.java | 145 ++++ .../src/com/android/server/wm/TestIWindow.java | 103 +++ .../android/server/wm/TestWindowManagerPolicy.java | 404 +++++++++ .../wm/UnknownAppVisibilityControllerTest.java | 87 ++ .../server/wm/WallpaperControllerTests.java | 82 ++ .../server/wm/WindowConfigurationTests.java | 243 ++++++ .../server/wm/WindowContainerControllerTests.java | 111 +++ .../android/server/wm/WindowContainerTests.java | 898 +++++++++++++++++++++ .../server/wm/WindowContainerTraversalTests.java | 64 ++ .../com/android/server/wm/WindowFrameTests.java | 522 ++++++++++++ .../server/wm/WindowManagerServiceRule.java | 212 +++++ .../server/wm/WindowManagerServiceRuleTest.java | 44 + .../com/android/server/wm/WindowStateTests.java | 423 ++++++++++ .../src/com/android/server/wm/WindowTestUtils.java | 261 ++++++ .../src/com/android/server/wm/WindowTestsBase.java | 400 +++++++++ .../com/android/server/wm/WindowTokenTests.java | 132 +++ .../src/com/android/server/wm/ZOrderingTests.java | 422 ++++++++++ 100 files changed, 11671 insertions(+), 11734 deletions(-) delete mode 100644 services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DimmerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TestIWindow.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java delete mode 100644 services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DimmerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TestIWindow.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java create mode 100644 services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index fa17b6125983..746c4530a5e1 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -143,9 +143,6 @@ - - @@ -206,12 +203,6 @@ - - - - - - diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java deleted file mode 100644 index a9071612a725..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for the {@link TaskStack} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:AnimatingAppWindowTokenRegistryTest - */ -@SmallTest -@Presubmit -@FlakyTest(detail = "Promote once confirmed non-flaky") -public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase { - - @Mock - AnimationAdapter mAdapter; - - @Mock - Runnable mMockEndDeferFinishCallback1; - @Mock - Runnable mMockEndDeferFinishCallback2; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testDeferring() { - final AppWindowToken window1 = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD, - "window2").mAppToken; - final AnimatingAppWindowTokenRegistry registry = - window1.getStack().getAnimatingAppWindowTokenRegistry(); - - window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); - window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); - assertTrue(window1.isSelfAnimating()); - assertTrue(window2.isSelfAnimating()); - - // Make sure that first animation finish is deferred, second one is not deferred, and first - // one gets cancelled. - assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1)); - assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2)); - verify(mMockEndDeferFinishCallback1).run(); - verifyZeroInteractions(mMockEndDeferFinishCallback2); - } - - @Test - public void testContainerRemoved() { - final AppWindowToken window1 = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD, - "window2").mAppToken; - final AnimatingAppWindowTokenRegistry registry = - window1.getStack().getAnimatingAppWindowTokenRegistry(); - - window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); - window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); - assertTrue(window1.isSelfAnimating()); - assertTrue(window2.isSelfAnimating()); - - // Make sure that first animation finish is deferred, and removing the second window stops - // finishes all pending deferred finishings. - registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1); - window2.setParent(null); - verify(mMockEndDeferFinishCallback1).run(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java deleted file mode 100644 index 5e12a950c560..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionControllerTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.WindowManager.TRANSIT_TASK_CLOSE; -import static android.view.WindowManager.TRANSIT_TASK_OPEN; - -import static junit.framework.Assert.assertEquals; - -import android.platform.test.annotations.Presubmit; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:AppTransitionControllerTest - */ -@SmallTest -@Presubmit -public class AppTransitionControllerTest extends WindowTestsBase { - - private AppTransitionController mAppTransitionController; - - @Before - public void setUp() throws Exception { - mAppTransitionController = new AppTransitionController(mWm, mDisplayContent); - } - - @Test - public void testTranslucentOpen() { - synchronized (mWm.mGlobalLock) { - final AppWindowToken behind = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - translucentOpening.setFillsParent(false); - translucentOpening.setHidden(true); - mDisplayContent.mOpeningApps.add(behind); - mDisplayContent.mOpeningApps.add(translucentOpening); - assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN, - mAppTransitionController.maybeUpdateTransitToTranslucentAnim( - TRANSIT_TASK_OPEN)); - } - } - - @Test - public void testTranslucentClose() { - synchronized (mWm.mGlobalLock) { - final AppWindowToken behind = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - translucentClosing.setFillsParent(false); - mDisplayContent.mClosingApps.add(translucentClosing); - assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE, - mAppTransitionController.maybeUpdateTransitToTranslucentAnim( - TRANSIT_TASK_CLOSE)); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java deleted file mode 100644 index 19d18cab931e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; -import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; -import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; - -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.Display; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Test class for {@link AppTransition}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:AppTransitionTests - */ -@SmallTest -@Presubmit -public class AppTransitionTests extends WindowTestsBase { - - private DisplayContent mDc; - - @Before - public void setUp() throws Exception { - mDc = mWm.getDefaultDisplayContentLocked(); - // For unit test, we don't need to test performSurfacePlacement to prevent some - // abnormal interaction with surfaceflinger native side. - mWm.mRoot = spy(mWm.mRoot); - doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); - } - - @Test - public void testKeyguardOverride() { - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); - } - - @Test - public void testKeyguardKeep() { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); - } - - @Test - public void testForceOverride() { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); - mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); - assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition()); - } - - @Test - public void testCrashing() { - mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition()); - } - - @Test - public void testKeepKeyguard_withCrashing() { - mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); - mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); - } - - @Test - public void testAppTransitionStateForMultiDisplay() { - // Create 2 displays & presume both display the state is ON for ready to display & animate. - final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); - final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); - - // Create 2 app window tokens to represent 2 activity window. - final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - - token1.allDrawn = true; - token1.startingDisplayed = true; - token1.startingMoved = true; - - // Simulate activity resume / finish flows to prepare app transition & set visibility, - // make sure transition is set as expected for each display. - dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); - dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition()); - // One activity window is visible for resuming & the other activity window is invisible - // for finishing in different display. - token1.setVisibility(true, false); - token2.setVisibility(false, false); - - // Make sure each display is in animating stage. - assertTrue(dc1.mOpeningApps.size() > 0); - assertTrue(dc2.mClosingApps.size() > 0); - assertTrue(dc1.isAppAnimating()); - assertTrue(dc2.isAppAnimating()); - } - - @Test - public void testCleanAppTransitionWhenTaskStackReparent() { - // Create 2 displays & presume both display the state is ON for ready to display & animate. - final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); - final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); - - final TaskStack stack1 = createTaskStackOnDisplay(dc1); - final Task task1 = createTaskInStack(stack1, 0 /* userId */); - final WindowTestUtils.TestAppWindowToken token1 = - WindowTestUtils.createTestAppWindowToken(dc1); - task1.addChild(token1, 0); - - // Simulate same app is during opening / closing transition set stage. - dc1.mClosingApps.add(token1); - assertTrue(dc1.mClosingApps.size() > 0); - - dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, - false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); - assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); - assertTrue(dc1.mAppTransition.isTransitionSet()); - - dc1.mOpeningApps.add(token1); - assertTrue(dc1.mOpeningApps.size() > 0); - - // Move stack to another display. - stack1.getController().reparent(dc2.getDisplayId(), new Rect(), true); - - // Verify if token are cleared from both pending transition list in former display. - assertFalse(dc1.mOpeningApps.contains(token1)); - assertFalse(dc1.mOpeningApps.contains(token1)); - } - -} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java deleted file mode 100644 index 4522494349a3..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.SurfaceControl.Transaction; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; - -import android.view.SurfaceControl; - -import androidx.test.filters.SmallTest; - -import com.android.server.wm.WindowTestUtils.TestAppWindowToken; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Animation related tests for the {@link AppWindowToken} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:AppWindowTokenAnimationTests - */ -@SmallTest -public class AppWindowTokenAnimationTests extends WindowTestsBase { - - private TestAppWindowToken mToken; - - @Mock - private Transaction mTransaction; - @Mock - private AnimationAdapter mSpec; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD); - mToken.setPendingTransaction(mTransaction); - } - - @Test - public void clipAfterAnim_boundsLayerIsCreated() { - mToken.mNeedsAnimationBoundsLayer = true; - - mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - verify(mTransaction).reparent(eq(mToken.getSurfaceControl()), - eq(mToken.mSurfaceAnimator.mLeash.getHandle())); - verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash), - eq(mToken.mAnimationBoundsLayer.getHandle())); - } - - @Test - public void clipAfterAnim_boundsLayerIsDestroyed() { - mToken.mNeedsAnimationBoundsLayer = true; - mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash; - final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer; - final ArgumentCaptor callbackCaptor = - ArgumentCaptor.forClass( - SurfaceAnimator.OnAnimationFinishedCallback.class); - verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); - - callbackCaptor.getValue().onAnimationFinished(mSpec); - verify(mTransaction).destroy(eq(leash)); - verify(mTransaction).destroy(eq(animationBoundsLayer)); - assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); - } - - @Test - public void clipAfterAnimCancelled_boundsLayerIsDestroyed() { - mToken.mNeedsAnimationBoundsLayer = true; - mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash; - final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer; - - mToken.mSurfaceAnimator.cancelAnimation(); - verify(mTransaction).destroy(eq(leash)); - verify(mTransaction).destroy(eq(animationBoundsLayer)); - assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); - } - - @Test - public void clipNoneAnim_boundsLayerIsNotCreated() { - mToken.mNeedsAnimationBoundsLayer = false; - - mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - verify(mTransaction).reparent(eq(mToken.getSurfaceControl()), - eq(mToken.mSurfaceAnimator.mLeash.getHandle())); - assertThat(mToken.mAnimationBoundsLayer).isNull(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java deleted file mode 100644 index 99abbf7b1f3a..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.TRANSIT_UNSET; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; - -import android.graphics.Point; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.Surface; -import android.view.WindowManager; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link AppWindowToken} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:AppWindowTokenTests - */ -@FlakyTest(bugId = 68267650) -@SmallTest -@Presubmit -public class AppWindowTokenTests extends WindowTestsBase { - - TaskStack mStack; - Task mTask; - WindowTestUtils.TestAppWindowToken mToken; - - private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); - - @Before - public void setUp() throws Exception { - mStack = createTaskStackOnDisplay(mDisplayContent); - mTask = createTaskInStack(mStack, 0 /* userId */); - mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - - mTask.addChild(mToken, 0); - } - - @Test - @Presubmit - public void testAddWindow_Order() { - assertEquals(0, mToken.getWindowsCount()); - - final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1"); - final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken, - "startingWin"); - final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin"); - final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4"); - - // Should not contain the windows that were added above. - assertEquals(4, mToken.getWindowsCount()); - assertTrue(mToken.hasWindow(win1)); - assertTrue(mToken.hasWindow(startingWin)); - assertTrue(mToken.hasWindow(baseWin)); - assertTrue(mToken.hasWindow(win4)); - - // The starting window should be on-top of all other windows. - assertEquals(startingWin, mToken.getLastChild()); - - // The base application window should be below all other windows. - assertEquals(baseWin, mToken.getFirstChild()); - mToken.removeImmediately(); - } - - @Test - @Presubmit - public void testFindMainWindow() { - assertNull(mToken.findMainWindow()); - - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1"); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11"); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12"); - assertEquals(window1, mToken.findMainWindow()); - window1.mAnimatingExit = true; - assertEquals(window1, mToken.findMainWindow()); - final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, "window2"); - assertEquals(window2, mToken.findMainWindow()); - mToken.removeImmediately(); - } - - @Test - @Presubmit - public void testGetTopFullscreenWindow() { - assertNull(mToken.getTopFullscreenWindow()); - - final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1"); - final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11"); - final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12"); - assertEquals(window12, mToken.getTopFullscreenWindow()); - window12.mAttrs.width = 500; - assertEquals(window11, mToken.getTopFullscreenWindow()); - window11.mAttrs.width = 500; - assertEquals(window1, mToken.getTopFullscreenWindow()); - mToken.removeImmediately(); - } - - @Test - public void testLandscapeSeascapeRotationByApp() { - // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; - mWm.mDisplayEnabled = true; - - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( - TYPE_BASE_APPLICATION); - attrs.setTitle("AppWindow"); - final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); - mToken.addWindow(appWindow); - - // Set initial orientation and update. - mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null, - mDisplayContent.getDisplayId()); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation()); - appWindow.resizeReported = false; - - // Update the orientation to perform 180 degree rotation and check that resize was reported. - mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); - mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null, - mDisplayContent.getDisplayId()); - mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); - assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); - assertTrue(appWindow.resizeReported); - appWindow.removeImmediately(); - } - - @Test - public void testLandscapeSeascapeRotationByPolicy() { - // Some plumbing to get the service ready for rotation updates. - mWm.mDisplayReady = true; - mWm.mDisplayEnabled = true; - - final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation()); - mDisplayContent.setDisplayRotation(spiedRotation); - - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( - TYPE_BASE_APPLICATION); - attrs.setTitle("AppWindow"); - final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); - mToken.addWindow(appWindow); - - // Set initial orientation and update. - performRotation(spiedRotation, Surface.ROTATION_90); - appWindow.resizeReported = false; - - // Update the rotation to perform 180 degree rotation and check that resize was reported. - performRotation(spiedRotation, Surface.ROTATION_270); - assertTrue(appWindow.resizeReported); - - appWindow.removeImmediately(); - } - - private void performRotation(DisplayRotation spiedRotation, int rotationToReport) { - doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt()); - int oldRotation = mDisplayContent.getRotation(); - mWm.updateRotation(false, false); - // Must manually apply here since ATM doesn't know about the display during this test - // (meaning it can't perform the normal sendNewConfiguration flow). - mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation()); - // Prevent the next rotation from being deferred by animation. - mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null); - mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); - } - - @Test - @Presubmit - public void testGetOrientation() { - mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - mToken.setFillsParent(false); - // Can specify orientation if app doesn't fill parent. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); - - mToken.setFillsParent(true); - mToken.setHidden(true); - mToken.sendingToBottom = true; - // Can not specify orientation if app isn't visible even though it fills parent. - assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); - // Can specify orientation if the current orientation candidate is orientation behind. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation(SCREEN_ORIENTATION_BEHIND)); - } - - @Test - @Presubmit - public void testKeyguardFlagsDuringRelaunch() { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( - TYPE_BASE_APPLICATION); - attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD; - attrs.setTitle("AppWindow"); - final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); - - // Add window with show when locked flag - mToken.addWindow(appWindow); - assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); - - // Start relaunching - mToken.startRelaunching(); - assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); - - // Remove window and make sure that we still report back flag - mToken.removeChild(appWindow); - assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); - - // Finish relaunching and ensure flag is now not reported - mToken.finishRelaunching(); - assertFalse(mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow()); - } - - @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") - public void testStuckExitingWindow() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); - closingWindow.mAnimatingExit = true; - closingWindow.mRemoveOnExit = true; - closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, - true /* performLayout */, false /* isVoiceInteraction */); - - // We pretended that we were running an exit animation, but that should have been cleared up - // by changing visibility of AppWindowToken - closingWindow.removeIfPossible(); - assertTrue(closingWindow.mRemoved); - } - - @Test - public void testSetOrientation() { - // Assert orientation is unspecified to start. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation()); - - mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); - - mDisplayContent.removeAppToken(mToken.token); - // Assert orientation is unset to after container is removed. - assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); - - // Reset display frozen state - mWm.mDisplayFrozen = false; - } - - @Test - public void testCreateRemoveStartingWindow() { - mToken.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - assertHasStartingWindow(mToken); - mToken.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(mToken); - } - - @Test - public void testAddRemoveRace() { - // There was once a race condition between adding and removing starting windows - for (int i = 0; i < 1000; i++) { - final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken(); - - appToken.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - appToken.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(appToken); - - appToken.getParent().getParent().removeImmediately(); - } - } - - @Test - public void testTransferStartingWindow() { - final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); - final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); - token1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - token2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(), - true, true, false, true, false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(token1); - assertHasStartingWindow(token2); - } - - @Test - public void testTransferStartingWindowWhileCreating() { - final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); - final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); - ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen( - () -> { - // Surprise, ...! Transfer window in the middle of the creation flow. - token2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, - token1.appToken.asBinder(), true, true, false, - true, false, false); - }); - token1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(token1); - assertHasStartingWindow(token2); - } - - private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() { - final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(taskStack, 0 /* userId */); - return createTestAppWindowTokenForGivenTask(task); - } - - private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) { - final WindowTestUtils.TestAppWindowToken appToken = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task.addChild(appToken, 0); - waitUntilHandlersIdle(); - return appToken; - } - - @Test - public void testTryTransferStartingWindowFromHiddenAboveToken() { - // Add two tasks on top of each other. - final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken(); - final WindowTestUtils.TestAppWindowToken tokenBottom = - createTestAppWindowTokenForGivenTask(tokenTop.getTask()); - - // Add a starting window. - tokenTop.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - - // Make the top one invisible, and try transferring the starting window from the top to the - // bottom one. - tokenTop.setVisibility(false, false); - tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded(); - - // Assert that the bottom window now has the starting window. - assertNoStartingWindow(tokenTop); - assertHasStartingWindow(tokenBottom); - } - - @Test - public void testTransitionAnimationPositionAndBounds() { - final Rect stackBounds = new Rect( - 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */); - final Rect taskBounds = new Rect( - 100/* left */, 200 /* top */, 600 /* right */, 600 /* bottom */); - mStack.setBounds(stackBounds); - mTask.setBounds(taskBounds); - - mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertTransitionAnimationPositionAndBounds(taskBounds.left, taskBounds.top, stackBounds); - - mTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - assertTransitionAnimationPositionAndBounds(stackBounds.left, stackBounds.top, stackBounds); - } - - private void assertTransitionAnimationPositionAndBounds(int expectedX, int expectedY, - Rect expectedBounds) { - final Point outPosition = new Point(); - final Rect outBounds = new Rect(); - mToken.getAnimationBounds(outPosition, outBounds); - assertEquals(expectedX, outPosition.x); - assertEquals(expectedY, outPosition.y); - assertEquals(expectedBounds, outBounds); - } - - private void assertHasStartingWindow(AppWindowToken atoken) { - assertNotNull(atoken.startingSurface); - assertNotNull(atoken.startingData); - assertNotNull(atoken.startingWindow); - } - - private void assertNoStartingWindow(AppWindowToken atoken) { - assertNull(atoken.startingSurface); - assertNull(atoken.startingWindow); - assertNull(atoken.startingData); - atoken.forAllWindows(windowState -> { - assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); - }, true); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java deleted file mode 100644 index 1c5391ed3a6c..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.platform.test.annotations.Presubmit; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.BoundsAnimationController.BoundsAnimator; -import com.android.server.wm.WindowManagerInternal.AppTransitionListener; - -import org.junit.Before; -import org.junit.Test; - -/** - * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks - * depending on the various interactions. - * - * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] - * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, - * or if a new animation starts on the same target. The tests below verifies that the target is - * notified of all the cases where it is animating and cancelled so that it can respond - * appropriately. - * - * Build/Install/Run: - * atest FrameworksServicesTests:BoundsAnimationControllerTests - */ -@SmallTest -@Presubmit -public class BoundsAnimationControllerTests extends WindowTestsBase { - - /** - * Mock value animator to simulate updates with. - */ - private static class MockValueAnimator extends ValueAnimator { - - private float mFraction; - - MockValueAnimator getWithValue(float fraction) { - mFraction = fraction; - return this; - } - - @Override - public Object getAnimatedValue() { - return mFraction; - } - } - - /** - * Mock app transition to fire notifications to the bounds animator. - */ - private static class MockAppTransition extends AppTransition { - - private AppTransitionListener mListener; - - MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) { - super(context, wm, displayContent); - } - - @Override - void registerListenerLocked(AppTransitionListener listener) { - mListener = listener; - } - - public void notifyTransitionPending() { - mListener.onAppTransitionPendingLocked(); - } - - public void notifyTransitionCancelled(int transit) { - mListener.onAppTransitionCancelledLocked(transit); - } - - public void notifyTransitionStarting(int transit) { - mListener.onAppTransitionStartingLocked(transit, null, null, 0, 0, 0); - } - - public void notifyTransitionFinished() { - mListener.onAppTransitionFinishedLocked(null); - } - } - - /** - * A test animate bounds user to track callbacks from the bounds animation. - */ - private static class TestBoundsAnimationTarget implements BoundsAnimationTarget { - - boolean mAwaitingAnimationStart; - boolean mMovedToFullscreen; - boolean mAnimationStarted; - boolean mSchedulePipModeChangedOnStart; - boolean mForcePipModeChangedCallback; - boolean mAnimationEnded; - Rect mAnimationEndFinalStackBounds; - boolean mSchedulePipModeChangedOnEnd; - boolean mBoundsUpdated; - boolean mCancelRequested; - Rect mStackBounds; - Rect mTaskBounds; - - void initialize(Rect from) { - mAwaitingAnimationStart = true; - mMovedToFullscreen = false; - mAnimationStarted = false; - mAnimationEnded = false; - mAnimationEndFinalStackBounds = null; - mForcePipModeChangedCallback = false; - mSchedulePipModeChangedOnStart = false; - mSchedulePipModeChangedOnEnd = false; - mStackBounds = from; - mTaskBounds = null; - mBoundsUpdated = false; - } - - @Override - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate) { - mAwaitingAnimationStart = false; - mAnimationStarted = true; - mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; - mForcePipModeChangedCallback = forceUpdate; - return true; - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - return true; - } - - @Override - public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { - // TODO: Once we break the runs apart, we should fail() here if this is called outside - // of onAnimationStart() and onAnimationEnd() - if (mCancelRequested) { - mCancelRequested = false; - return false; - } else { - mBoundsUpdated = true; - mStackBounds = stackBounds; - mTaskBounds = taskBounds; - return true; - } - } - - @Override - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, - boolean moveToFullscreen) { - mAnimationEnded = true; - mAnimationEndFinalStackBounds = finalStackBounds; - mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; - mMovedToFullscreen = moveToFullscreen; - mTaskBounds = null; - } - } - - /** - * Drives the animations, makes common assertions along the way. - */ - private static class BoundsAnimationDriver { - - private final BoundsAnimationController mController; - private final TestBoundsAnimationTarget mTarget; - private final MockValueAnimator mMockAnimator; - - private BoundsAnimator mAnimator; - private Rect mFrom; - private Rect mTo; - private Rect mLargerBounds; - private Rect mExpectedFinalBounds; - - BoundsAnimationDriver(BoundsAnimationController controller, - TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { - mController = controller; - mTarget = target; - mMockAnimator = mockValueAnimator; - } - - BoundsAnimationDriver start(Rect from, Rect to) { - if (mAnimator != null) { - throw new IllegalArgumentException("Call restart() to restart an animation"); - } - - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - - mTarget.initialize(from); - - // Started, not running - assertTrue(mTarget.mAwaitingAnimationStart); - assertFalse(mTarget.mAnimationStarted); - - startImpl(from, to); - - // Ensure that the animator is paused for the all windows drawn signal when animating - // to/from fullscreen - if (fromFullscreen || toFullscreen) { - assertTrue(mAnimator.isPaused()); - mController.onAllWindowsDrawn(); - } else { - assertTrue(!mAnimator.isPaused()); - } - - // Started and running - assertFalse(mTarget.mAwaitingAnimationStart); - assertTrue(mTarget.mAnimationStarted); - - return this; - } - - BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) { - if (mAnimator == null) { - throw new IllegalArgumentException("Call start() to start a new animation"); - } - - BoundsAnimator oldAnimator = mAnimator; - boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); - - // Reset the animation start state - mTarget.mAnimationStarted = false; - - // Start animation - startImpl(mTarget.mStackBounds, to); - - if (toSameBounds) { - // Same animator if same final bounds - assertSame(oldAnimator, mAnimator); - } - - if (expectStartedAndPipModeChangedCallback) { - // Replacing animation with pending pip mode changed callback, ensure we update - assertTrue(mTarget.mAnimationStarted); - assertTrue(mTarget.mSchedulePipModeChangedOnStart); - assertTrue(mTarget.mForcePipModeChangedCallback); - } else { - // No animation start for replacing animation - assertFalse(mTarget.mAnimationStarted); - } - mTarget.mAnimationStarted = true; - return this; - } - - private BoundsAnimationDriver startImpl(Rect from, Rect to) { - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - mFrom = new Rect(from); - mTo = new Rect(to); - mExpectedFinalBounds = new Rect(to); - mLargerBounds = getLargerBounds(mFrom, mTo); - - // Start animation - final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_START - : fromFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_END - : NO_PIP_MODE_CHANGED_CALLBACKS; - mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen); - - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); - } - - return this; - } - - BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { - // Callback made - assertTrue(mTarget.mAnimationStarted); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); - return this; - } - - BoundsAnimationDriver update(float t) { - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); - } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); - } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - return this; - } - - BoundsAnimationDriver cancel() { - // Cancel - mTarget.mCancelRequested = true; - mTarget.mBoundsUpdated = false; - mExpectedFinalBounds = null; - - // Update - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); - - // Not started, not running, cancel reset - assertFalse(mTarget.mCancelRequested); - - // Stack/task bounds not updated - assertFalse(mTarget.mBoundsUpdated); - - // Callback made - assertTrue(mTarget.mAnimationEnded); - assertNull(mTarget.mAnimationEndFinalStackBounds); - - return this; - } - - BoundsAnimationDriver end() { - mAnimator.end(); - - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); - - return this; - } - - BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, - boolean moveToFullscreen) { - // Callback made - assertTrue(mTarget.mAnimationEnded); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); - assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); - return this; - } - - private static Rect getLargerBounds(Rect r1, Rect r2) { - int r1Area = r1.width() * r1.height(); - int r2Area = r2.width() * r2.height(); - return (r1Area <= r2Area) ? r2 : r1; - } - } - - // Constants - private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; - private static final boolean MOVE_TO_FULLSCREEN = true; - private static final int DURATION = 100; - - // Some dummy bounds to represent fullscreen and floating bounds - private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); - private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); - private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); - - // Common - private MockAppTransition mMockAppTransition; - private TestBoundsAnimationTarget mTarget; - private BoundsAnimationController mController; - private BoundsAnimationDriver mDriver; - - // Temp - private static final Rect sTmpRect = new Rect(); - - @Before - public void setUp() throws Exception { - final Context context = getInstrumentation().getTargetContext(); - final Handler handler = new Handler(Looper.getMainLooper()); - mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent); - mTarget = new TestBoundsAnimationTarget(); - mController = new BoundsAnimationController(context, mMockAppTransition, handler, null); - final MockValueAnimator mockValueAnimator = new MockValueAnimator(); - mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator); - } - - /** BASE TRANSITIONS **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { - // When animating from fullscreen and the animation is interruped, we expect the animation - // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** MISC **/ - - @UiThreadTest - @Test - public void testBoundsAreCopied() { - Rect from = new Rect(0, 0, 100, 100); - Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to) - .update(0.25f) - .end(); - assertEquals(new Rect(0, 0, 100, 100), from); - assertEquals(new Rect(25, 25, 75, 75), to); - } - - /** - * @return whether the task and stack bounds would be the same if they were at the same offset. - */ - private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { - sTmpRect.set(taskBounds); - sTmpRect.offsetTo(stackBounds.left, stackBounds.top); - return stackBounds.equals(sTmpRect); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java deleted file mode 100644 index a6415d1f2b1f..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; -import android.view.SurfaceSession; - -import org.junit.Before; -import org.junit.Test; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:DimmerTests - */ -@Presubmit -public class DimmerTests extends WindowTestsBase { - - private static class TestWindowContainer extends WindowContainer { - final SurfaceControl mControl = mock(SurfaceControl.class); - final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); - - TestWindowContainer(WindowManagerService wm) { - super(wm); - } - - @Override - public SurfaceControl getSurfaceControl() { - return mControl; - } - - @Override - public SurfaceControl.Transaction getPendingTransaction() { - return mTransaction; - } - } - - private static class MockSurfaceBuildingContainer extends WindowContainer { - final SurfaceSession mSession = new SurfaceSession(); - final SurfaceControl mHostControl = mock(SurfaceControl.class); - final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class); - - MockSurfaceBuildingContainer(WindowManagerService wm) { - super(wm); - } - - class MockSurfaceBuilder extends SurfaceControl.Builder { - MockSurfaceBuilder(SurfaceSession ss) { - super(ss); - } - - @Override - public SurfaceControl build() { - return mock(SurfaceControl.class); - } - } - - @Override - SurfaceControl.Builder makeChildSurface(WindowContainer child) { - return new MockSurfaceBuilder(mSession); - } - - @Override - public SurfaceControl getSurfaceControl() { - return mHostControl; - } - - @Override - public SurfaceControl.Transaction getPendingTransaction() { - return mHostTransaction; - } - } - - private MockSurfaceBuildingContainer mHost; - private Dimmer mDimmer; - private SurfaceControl.Transaction mTransaction; - private Dimmer.SurfaceAnimatorStarter mSurfaceAnimatorStarter; - - private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter { - @Override - public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t, - AnimationAdapter anim, boolean hidden) { - surfaceAnimator.mAnimationFinishedCallback.run(); - } - } - - @Before - public void setUp() throws Exception { - mHost = new MockSurfaceBuildingContainer(mWm); - mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl()); - mTransaction = mock(SurfaceControl.Transaction.class); - mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter); - } - - @Test - public void testDimAboveNoChildCreatesSurface() { - final float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, alpha); - - SurfaceControl dimLayer = getDimLayer(); - - assertNotNull("Dimmer should have created a surface", dimLayer); - - verify(mTransaction).setAlpha(dimLayer, alpha); - verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE); - } - - @Test - public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() { - float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, alpha); - final SurfaceControl firstSurface = getDimLayer(); - - alpha = 0.9f; - mDimmer.dimAbove(mTransaction, alpha); - - assertEquals(firstSurface, getDimLayer()); - verify(mTransaction).setAlpha(firstSurface, 0.9f); - } - - @Test - public void testUpdateDimsAppliesCrop() { - mDimmer.dimAbove(mTransaction, 0.8f); - - int width = 100; - int height = 300; - Rect bounds = new Rect(0, 0, width, height); - mDimmer.updateDims(mTransaction, bounds); - - verify(mTransaction).setWindowCrop(getDimLayer(), width, height); - verify(mTransaction).show(getDimLayer()); - } - - @Test - public void testDimAboveNoChildNotReset() { - mDimmer.dimAbove(mTransaction, 0.8f); - SurfaceControl dimLayer = getDimLayer(); - mDimmer.resetDimStates(); - - mDimmer.updateDims(mTransaction, new Rect()); - verify(mTransaction).show(getDimLayer()); - verify(dimLayer, never()).destroy(); - } - - @Test - public void testDimAboveWithChildCreatesSurfaceAboveChild() { - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - final float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, child, alpha); - SurfaceControl dimLayer = getDimLayer(); - - assertNotNull("Dimmer should have created a surface", dimLayer); - - verify(mTransaction).setAlpha(dimLayer, alpha); - verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, 1); - } - - @Test - public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() { - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - final float alpha = 0.8f; - mDimmer.dimBelow(mTransaction, child, alpha); - SurfaceControl dimLayer = getDimLayer(); - - assertNotNull("Dimmer should have created a surface", dimLayer); - - verify(mTransaction).setAlpha(dimLayer, alpha); - verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1); - } - - @Test - public void testDimBelowWithChildSurfaceDestroyedWhenReset() { - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - final float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, child, alpha); - SurfaceControl dimLayer = getDimLayer(); - mDimmer.resetDimStates(); - - mDimmer.updateDims(mTransaction, new Rect()); - verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any( - SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); - verify(mHost.getPendingTransaction()).destroy(dimLayer); - } - - @Test - public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() { - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - final float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, child, alpha); - SurfaceControl dimLayer = getDimLayer(); - mDimmer.resetDimStates(); - mDimmer.dimAbove(mTransaction, child, alpha); - - mDimmer.updateDims(mTransaction, new Rect()); - verify(mTransaction).show(dimLayer); - verify(dimLayer, never()).destroy(); - } - - @Test - public void testDimUpdateWhileDimming() { - Rect bounds = new Rect(); - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - final float alpha = 0.8f; - mDimmer.dimAbove(mTransaction, child, alpha); - - SurfaceControl dimLayer = getDimLayer(); - bounds.set(0, 0, 10, 10); - mDimmer.updateDims(mTransaction, bounds); - verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); - verify(mTransaction, times(1)).show(dimLayer); - verify(mTransaction).setPosition(dimLayer, 0, 0); - - bounds.set(10, 10, 30, 30); - mDimmer.updateDims(mTransaction, bounds); - verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); - verify(mTransaction).setPosition(dimLayer, 10, 10); - } - - @Test - public void testRemoveDimImmediately() { - TestWindowContainer child = new TestWindowContainer(mWm); - mHost.addChild(child, 0); - - mDimmer.dimAbove(mTransaction, child, 1); - SurfaceControl dimLayer = getDimLayer(); - mDimmer.updateDims(mTransaction, new Rect()); - verify(mTransaction, times(1)).show(dimLayer); - - reset(mSurfaceAnimatorStarter); - mDimmer.dontAnimateExit(); - mDimmer.resetDimStates(); - mDimmer.updateDims(mTransaction, new Rect()); - verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any( - SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); - verify(mTransaction).destroy(dimLayer); - } - - private SurfaceControl getDimLayer() { - return mDimmer.mDimState.mDimLayer; - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java deleted file mode 100644 index 43e10f0a5f06..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; -import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; -import static android.view.DisplayCutout.fromBoundingRect; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; - -import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.annotation.SuppressLint; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.DisplayMetrics; -import android.view.DisplayCutout; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.Surface; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * Tests for the {@link DisplayContent} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:DisplayContentTests - */ -@SmallTest -@Presubmit -public class DisplayContentTests extends WindowTestsBase { - - @Test - @FlakyTest(bugId = 77772044) - public void testForAllWindows() { - final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, - mDisplayContent, "exiting app"); - final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; - exitingAppToken.mIsExiting = true; - exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken); - - assertForAllWindowsOrder(Arrays.asList( - mWallpaperWindow, - exitingAppWindow, - mChildAppWindowBelow, - mAppWindow, - mChildAppWindowAbove, - mDockedDividerWindow, - mStatusBarWindow, - mNavBarWindow, - mImeWindow, - mImeDialogWindow)); - } - - @Test - public void testForAllWindows_WithAppImeTarget() { - final WindowState imeAppTarget = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); - - mDisplayContent.mInputMethodTarget = imeAppTarget; - - assertForAllWindowsOrder(Arrays.asList( - mWallpaperWindow, - mChildAppWindowBelow, - mAppWindow, - mChildAppWindowAbove, - imeAppTarget, - mImeWindow, - mImeDialogWindow, - mDockedDividerWindow, - mStatusBarWindow, - mNavBarWindow)); - } - - @Test - public void testForAllWindows_WithChildWindowImeTarget() throws Exception { - mDisplayContent.mInputMethodTarget = mChildAppWindowAbove; - - assertForAllWindowsOrder(Arrays.asList( - mWallpaperWindow, - mChildAppWindowBelow, - mAppWindow, - mChildAppWindowAbove, - mImeWindow, - mImeDialogWindow, - mDockedDividerWindow, - mStatusBarWindow, - mNavBarWindow)); - } - - @Test - public void testForAllWindows_WithStatusBarImeTarget() throws Exception { - mDisplayContent.mInputMethodTarget = mStatusBarWindow; - - assertForAllWindowsOrder(Arrays.asList( - mWallpaperWindow, - mChildAppWindowBelow, - mAppWindow, - mChildAppWindowAbove, - mDockedDividerWindow, - mStatusBarWindow, - mImeWindow, - mImeDialogWindow, - mNavBarWindow)); - } - - @Test - public void testForAllWindows_WithInBetweenWindowToken() { - // This window is set-up to be z-ordered between some windows that go in the same token like - // the nav bar and status bar. - final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION, - mDisplayContent, "voiceInteractionWindow"); - - assertForAllWindowsOrder(Arrays.asList( - mWallpaperWindow, - mChildAppWindowBelow, - mAppWindow, - mChildAppWindowAbove, - mDockedDividerWindow, - voiceInteractionWindow, - mStatusBarWindow, - mNavBarWindow, - mImeWindow, - mImeDialogWindow)); - } - - @Test - public void testComputeImeTarget() { - // Verify that an app window can be an ime target. - final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); - appWin.setHasSurface(true); - assertTrue(appWin.canBeImeTarget()); - WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); - assertEquals(appWin, imeTarget); - appWin.mHidden = false; - - // Verify that an child window can be an ime target. - final WindowState childWin = createWindow(appWin, - TYPE_APPLICATION_ATTACHED_DIALOG, "childWin"); - childWin.setHasSurface(true); - assertTrue(childWin.canBeImeTarget()); - imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); - assertEquals(childWin, imeTarget); - } - - /** - * This tests stack movement between displays and proper stack's, task's and app token's display - * container references updates. - */ - @Test - public void testMoveStackBetweenDisplays() { - // Create a second display. - final DisplayContent dc = createNewDisplay(); - - // Add stack with activity. - final TaskStack stack = createTaskStackOnDisplay(dc); - assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId()); - assertEquals(dc, stack.getParent().getParent()); - assertEquals(dc, stack.getDisplayContent()); - - final Task task = createTaskInStack(stack, 0 /* userId */); - final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( - dc); - task.addChild(token, 0); - assertEquals(dc, task.getDisplayContent()); - assertEquals(dc, token.getDisplayContent()); - - // Move stack to first display. - mDisplayContent.moveStackToDisplay(stack, true /* onTop */); - assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId()); - assertEquals(mDisplayContent, stack.getParent().getParent()); - assertEquals(mDisplayContent, stack.getDisplayContent()); - assertEquals(mDisplayContent, task.getDisplayContent()); - assertEquals(mDisplayContent, token.getDisplayContent()); - } - - /** - * This tests override configuration updates for display content. - */ - @Test - public void testDisplayOverrideConfigUpdate() { - final Configuration currentOverrideConfig = mDisplayContent.getOverrideConfiguration(); - - // Create new, slightly changed override configuration and apply it to the display. - final Configuration newOverrideConfig = new Configuration(currentOverrideConfig); - newOverrideConfig.densityDpi += 120; - newOverrideConfig.fontScale += 0.3; - - mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent); - - // Check that override config is applied. - assertEquals(newOverrideConfig, mDisplayContent.getOverrideConfiguration()); - } - - /** - * This tests global configuration updates when default display config is updated. - */ - @Test - public void testDefaultDisplayOverrideConfigUpdate() { - DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); - final Configuration currentConfig = defaultDisplay.getConfiguration(); - - // Create new, slightly changed override configuration and apply it to the display. - final Configuration newOverrideConfig = new Configuration(currentConfig); - newOverrideConfig.densityDpi += 120; - newOverrideConfig.fontScale += 0.3; - - mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay); - - // Check that global configuration is updated, as we've updated default display's config. - Configuration globalConfig = mWm.mRoot.getConfiguration(); - assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi); - assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); - - // Return back to original values. - mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay); - globalConfig = mWm.mRoot.getConfiguration(); - assertEquals(currentConfig.densityDpi, globalConfig.densityDpi); - assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); - } - - /** - * Tests tapping on a stack in different display results in window gaining focus. - */ - @Test - public void testInputEventBringsCorrectDisplayInFocus() { - DisplayContent dc0 = mWm.getDefaultDisplayContentLocked(); - // Create a second display - final DisplayContent dc1 = createNewDisplay(); - - // Add stack with activity. - final TaskStack stack0 = createTaskStackOnDisplay(dc0); - final Task task0 = createTaskInStack(stack0, 0 /* userId */); - final WindowTestUtils.TestAppWindowToken token = - WindowTestUtils.createTestAppWindowToken(dc0); - task0.addChild(token, 0); - dc0.configureDisplayPolicy(); - assertNotNull(dc0.mTapDetector); - - final TaskStack stack1 = createTaskStackOnDisplay(dc1); - final Task task1 = createTaskInStack(stack1, 0 /* userId */); - final WindowTestUtils.TestAppWindowToken token1 = - WindowTestUtils.createTestAppWindowToken(dc0); - task1.addChild(token1, 0); - dc1.configureDisplayPolicy(); - assertNotNull(dc1.mTapDetector); - - // tap on primary display. - tapOnDisplay(dc0); - // Check focus is on primary display. - assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, - dc0.findFocusedWindow()); - - // Tap on secondary display. - tapOnDisplay(dc1); - // Check focus is on secondary. - assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, - dc1.findFocusedWindow()); - } - - @Test - public void testFocusedWindowMultipleDisplays() { - // Create a focusable window and check that focus is calculated correctly - final WindowState window1 = - createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1"); - updateFocusedWindow(); - assertTrue(window1.isFocused()); - assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); - - // Check that a new display doesn't affect focus - final DisplayContent dc = createNewDisplay(); - updateFocusedWindow(); - assertTrue(window1.isFocused()); - assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); - - // Add a window to the second display, and it should be focused - final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2"); - updateFocusedWindow(); - assertTrue(window1.isFocused()); - assertTrue(window2.isFocused()); - assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); - - // Move the first window to the to including parents, and make sure focus is updated - window1.getParent().positionChildAt(POSITION_TOP, window1, true); - updateFocusedWindow(); - assertTrue(window1.isFocused()); - assertTrue(window2.isFocused()); - assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); - } - - /** - * This tests setting the maximum ui width on a display. - */ - @Test - public void testMaxUiWidth() { - // Prevent base display metrics for test from being updated to the value of real display. - final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); - final int baseWidth = 1440; - final int baseHeight = 2560; - final int baseDensity = 300; - - displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); - - final int maxWidth = 300; - final int resultingHeight = (maxWidth * baseHeight) / baseWidth; - final int resultingDensity = (maxWidth * baseDensity) / baseWidth; - - displayContent.setMaxUiWidth(maxWidth); - verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); - - // Assert setting values again does not change; - displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); - verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); - - final int smallerWidth = 200; - final int smallerHeight = 400; - final int smallerDensity = 100; - - // Specify smaller dimension, verify that it is honored - displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity); - verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); - - // Verify that setting the max width to a greater value than the base width has no effect - displayContent.setMaxUiWidth(maxWidth); - verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); - } - - @Test - public void testDisplayCutout_rot0() { - synchronized (mWm.getWindowManagerLock()) { - final DisplayContent dc = createNewDisplay(); - dc.mInitialDisplayWidth = 200; - dc.mInitialDisplayHeight = 400; - Rect r = new Rect(80, 0, 120, 10); - final DisplayCutout cutout = new WmDisplayCutout( - fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null) - .computeSafeInsets(200, 400).getDisplayCutout(); - - dc.mInitialDisplayCutout = cutout; - dc.setRotation(Surface.ROTATION_0); - dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. - - assertEquals(cutout, dc.getDisplayInfo().displayCutout); - } - } - - @Test - public void testDisplayCutout_rot90() { - synchronized (mWm.getWindowManagerLock()) { - // Prevent mInitialDisplayCutout from being updated from real display (e.g. null - // if the device has no cutout). - final DisplayContent dc = createDisplayNoUpdateDisplayInfo(); - // Rotation may use real display info to compute bound, so here also uses the - // same width and height. - final int displayWidth = dc.mInitialDisplayWidth; - final int displayHeight = dc.mInitialDisplayHeight; - final int cutoutWidth = 40; - final int cutoutHeight = 10; - final int left = (displayWidth - cutoutWidth) / 2; - final int top = 0; - final int right = (displayWidth + cutoutWidth) / 2; - final int bottom = cutoutHeight; - - final Rect r1 = new Rect(left, top, right, bottom); - final DisplayCutout cutout = new WmDisplayCutout( - fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), - null) - .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); - - dc.mInitialDisplayCutout = cutout; - dc.setRotation(Surface.ROTATION_90); - dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. - - // ----o---------- ------------- - // | | | | | - // | ------o | o--- - // | | | | - // | | -> | | - // | | ---o - // | | | - // | | ------------- - final Rect r = new Rect(top, left, bottom, right); - assertEquals(new WmDisplayCutout( - fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null) - .computeSafeInsets(displayHeight, displayWidth) - .getDisplayCutout(), dc.getDisplayInfo().displayCutout); - } - } - - @Test - public void testLayoutSeq_assignedDuringLayout() { - synchronized (mWm.getWindowManagerLock()) { - - final DisplayContent dc = createNewDisplay(); - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); - - dc.setLayoutNeeded(); - dc.performLayout(true /* initial */, false /* updateImeWindows */); - - assertThat(win.mLayoutSeq, is(dc.mLayoutSeq)); - } - } - - @Test - @SuppressLint("InlinedApi") - public void testOrientationDefinedByKeyguard() { - final DisplayContent dc = createNewDisplay(); - // Create a window that requests landscape orientation. It will define device orientation - // by default. - final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); - window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard"); - keyguard.mHasSurface = true; - keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; - - assertEquals("Screen orientation must be defined by the app window by default", - SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); - - keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; - assertEquals("Visible keyguard must influence device orientation", - SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); - - mWm.setKeyguardGoingAway(true); - assertEquals("Keyguard that is going away must not influence device orientation", - SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); - } - - @Test - public void testDisableDisplayInfoOverrideFromWindowManager() { - final DisplayContent dc = createNewDisplay(); - - assertTrue(dc.mShouldOverrideDisplayConfiguration); - mWm.dontOverrideDisplayInfo(dc.getDisplayId()); - - assertFalse(dc.mShouldOverrideDisplayConfiguration); - verify(mWm.mDisplayManagerInternal, times(1)) - .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null); - } - - @Test - public void testClearLastFocusWhenReparentingFocusedWindow() { - final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked(); - final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, - defaultDisplay, "window"); - defaultDisplay.mLastFocus = window; - mDisplayContent.mCurrentFocus = window; - mDisplayContent.reParentWindowToken(window.mToken); - - assertNull(defaultDisplay.mLastFocus); - } - - @Test - public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() { - final DisplayContent portraitDisplay = createNewDisplay(); - portraitDisplay.mInitialDisplayHeight = 2000; - portraitDisplay.mInitialDisplayWidth = 1000; - - portraitDisplay.setRotation(Surface.ROTATION_0); - assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); - portraitDisplay.setRotation(Surface.ROTATION_90); - assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); - - final DisplayContent landscapeDisplay = createNewDisplay(); - landscapeDisplay.mInitialDisplayHeight = 1000; - landscapeDisplay.mInitialDisplayWidth = 2000; - - landscapeDisplay.setRotation(Surface.ROTATION_0); - assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); - landscapeDisplay.setRotation(Surface.ROTATION_90); - assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); - } - - @Test - public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() { - final DisplayContent newDisplay = createNewDisplay(); - - final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); - final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); - appWin.setHasSurface(true); - appWin1.setHasSurface(true); - - // Set current input method window on default display, make sure the input method target - // is appWin & null on the other display. - mDisplayContent.setInputMethodWindowLocked(mImeWindow); - newDisplay.setInputMethodWindowLocked(null); - assertTrue("appWin should be IME target window", - appWin.equals(mDisplayContent.mInputMethodTarget)); - assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget); - - // Switch input method window on new display & make sure the input method target also - // switched as expected. - newDisplay.setInputMethodWindowLocked(mImeWindow); - mDisplayContent.setInputMethodWindowLocked(null); - assertTrue("appWin1 should be IME target window", - appWin1.equals(newDisplay.mInputMethodTarget)); - assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget); - } - - private boolean isOptionsPanelAtRight(int displayId) { - return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; - } - - private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, - int expectedBaseHeight, int expectedBaseDensity) { - assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth); - assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight); - assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity); - } - - private void updateFocusedWindow() { - synchronized (mWm.mGlobalLock) { - mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false); - } - } - - /** - * Create DisplayContent that does not update display base/initial values from device to keep - * the values set by test. - */ - private DisplayContent createDisplayNoUpdateDisplayInfo() { - final DisplayContent displayContent = spy(createNewDisplay()); - doNothing().when(displayContent).updateDisplayInfo(); - return displayContent; - } - - private void assertForAllWindowsOrder(List expectedWindowsBottomToTop) { - final LinkedList actualWindows = new LinkedList<>(); - - // Test forward traversal. - mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */); - assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop)); - - actualWindows.clear(); - - // Test backward traversal. - mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */); - assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); - } - - private static List reverseList(List list) { - final ArrayList result = new ArrayList<>(list); - Collections.reverse(result); - return result; - } - - private void tapOnDisplay(final DisplayContent dc) { - final DisplayMetrics dm = dc.getDisplayMetrics(); - final float x = dm.widthPixels / 2; - final float y = dm.heightPixels / 2; - final long downTime = SystemClock.uptimeMillis(); - final long eventTime = SystemClock.uptimeMillis() + 100; - // sending ACTION_DOWN - final MotionEvent downEvent = MotionEvent.obtain( - downTime, - downTime, - MotionEvent.ACTION_DOWN, - x, - y, - 0 /*metaState*/); - downEvent.setDisplayId(dc.getDisplayId()); - dc.mTapDetector.onPointerEvent(downEvent); - - // sending ACTION_UP - final MotionEvent upEvent = MotionEvent.obtain( - downTime, - eventTime, - MotionEvent.ACTION_UP, - x, - y, - 0 /*metaState*/); - upEvent.setDisplayId(dc.getDisplayId()); - dc.mTapDetector.onPointerEvent(upEvent); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java deleted file mode 100644 index 18bd2e45acd4..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyInsetsTests.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; - -import static org.hamcrest.Matchers.equalTo; - -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.DisplayInfo; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ErrorCollector; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase { - - @Rule - public final ErrorCollector mErrorCollector = new ErrorCollector(); - - @Test - public void portrait() { - final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */); - - verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); - verifyConsistency(di); - } - - @Test - public void portrait_withCutout() { - final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */); - - verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT); - verifyConsistency(di); - } - - @Test - public void landscape() { - final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */); - - verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0); - verifyConsistency(di); - } - - @Test - public void landscape_withCutout() { - final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */); - - verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0); - verifyConsistency(di); - } - - @Test - public void seascape() { - final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */); - - verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); - verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0); - verifyConsistency(di); - } - - @Test - public void seascape_withCutout() { - final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */); - - verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); - verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0); - verifyConsistency(di); - } - - @Test - public void upsideDown() { - final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */); - - verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); - verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); - verifyConsistency(di); - } - - @Test - public void upsideDown_withCutout() { - final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */); - - verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); - verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); - verifyConsistency(di); - } - - private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) { - mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect( - left, top, right, bottom))); - } - - private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) { - mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect( - left, top, right, bottom))); - } - - private void verifyConsistency(DisplayInfo di) { - verifyConsistency("configDisplay", di, getStableInsetsLw(di), - getConfigDisplayWidth(di), getConfigDisplayHeight(di)); - verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di), - getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di)); - } - - private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width, - int height) { - mErrorCollector.checkThat(what + ":width", width, - equalTo(di.logicalWidth - insets.left - insets.right)); - mErrorCollector.checkThat(what + ":height", height, - equalTo(di.logicalHeight - insets.top - insets.bottom)); - } - - private Rect getStableInsetsLw(DisplayInfo di) { - Rect result = new Rect(); - mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, - di.displayCutout, result); - return result; - } - - private Rect getNonDecorInsetsLw(DisplayInfo di) { - Rect result = new Rect(); - mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, - di.displayCutout, result); - return result; - } - - private int getNonDecorDisplayWidth(DisplayInfo di) { - return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, - di.rotation, 0 /* ui */, di.displayCutout); - } - - private int getNonDecorDisplayHeight(DisplayInfo di) { - return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, - di.rotation, 0 /* ui */, di.displayCutout); - } - - private int getConfigDisplayWidth(DisplayInfo di) { - return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, - di.rotation, 0 /* ui */, di.displayCutout); - } - - private int getConfigDisplayHeight(DisplayInfo di) { - return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, - di.rotation, 0 /* ui */, di.displayCutout); - } - - private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { - return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java deleted file mode 100644 index a91c5e79ccfc..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; -import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; -import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.spy; - -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.util.Pair; -import android.view.DisplayCutout; -import android.view.DisplayInfo; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { - - private DisplayFrames mFrames; - private WindowState mWindow; - private int mRotation = ROTATION_0; - private boolean mHasDisplayCutout; - - @Before - public void setUp() throws Exception { - updateDisplayFrames(); - - mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); - // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from - // changing those frames. - doNothing().when(mWindow).computeFrameLw(); - - final WindowManager.LayoutParams attrs = mWindow.mAttrs; - attrs.width = MATCH_PARENT; - attrs.height = MATCH_PARENT; - attrs.flags = - FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - attrs.format = PixelFormat.TRANSLUCENT; - } - - public void setRotation(int rotation) { - mRotation = rotation; - updateDisplayFrames(); - } - - public void addDisplayCutout() { - mHasDisplayCutout = true; - updateDisplayFrames(); - } - - private void updateDisplayFrames() { - final Pair info = displayInfoAndCutoutForRotation(mRotation, - mHasDisplayCutout); - mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second); - } - - @Test - public void layoutWindowLw_appDrawsBars() { - mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_appWontDrawBars() { - mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); - } - - @Test - public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception { - mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); - } - - @Test - public void addingWindow_doesNotTamperWithSysuiFlags() { - mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - addWindow(mWindow); - - assertEquals(0, mWindow.mAttrs.systemUiVisibility); - assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility); - } - - @Test - public void layoutWindowLw_withDisplayCutout() { - addDisplayCutout(); - - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_never() { - addDisplayCutout(); - - mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { - addDisplayCutout(); - - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreen() { - addDisplayCutout(); - - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { - addDisplayCutout(); - - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; - mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); - } - - - @Test - public void layoutWindowLw_withDisplayCutout_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_seascape() { - addDisplayCutout(); - setRotation(ROTATION_270); - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); - assertInsetBy(mWindow.getContentFrameLw(), - NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); - assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - } - - @Test - public void layoutWindowLw_withDisplayCutout_floatingInScreen() { - addDisplayCutout(); - - mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN; - mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; - mWindow.mAttrs.width = DISPLAY_WIDTH; - mWindow.mAttrs.height = DISPLAY_HEIGHT; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); - assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); - } - - @Test - public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { - addDisplayCutout(); - setRotation(ROTATION_90); - - mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - addWindow(mWindow); - - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); - - assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); - assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getContentFrameLw(), - DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); - assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); - } - - @Test - public void layoutHint_appWindow() { - // Initialize DisplayFrames - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames, - false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, - outDisplayCutout); - - assertThat(outFrame, is(mFrames.mUnrestricted)); - assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); - assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } - - @Test - public void layoutHint_appWindowInTask() { - // Initialize DisplayFrames - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - final Rect taskBounds = new Rect(100, 100, 200, 200); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, - false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, - outDisplayCutout); - - assertThat(outFrame, is(taskBounds)); - assertThat(outContentInsets, is(new Rect())); - assertThat(outStableInsets, is(new Rect())); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } - - @Test - public void layoutHint_appWindowInTask_outsideContentFrame() { - // Initialize DisplayFrames - mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); - - // Task is in the nav bar area (usually does not happen, but this is similar enough to the - // possible overlap with the IME) - final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1, - 200, mFrames.mContent.bottom + 10); - - final Rect outFrame = new Rect(); - final Rect outContentInsets = new Rect(); - final Rect outStableInsets = new Rect(); - final Rect outOutsets = new Rect(); - final DisplayCutout.ParcelableWrapper outDisplayCutout = - new DisplayCutout.ParcelableWrapper(); - - mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, - true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, - outDisplayCutout); - - assertThat(outFrame, is(taskBounds)); - assertThat(outContentInsets, is(new Rect())); - assertThat(outStableInsets, is(new Rect())); - assertThat(outOutsets, is(new Rect())); - assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); - } - - /** - * Asserts that {@code actual} is inset by the given amounts from the full display rect. - * - * Convenience wrapper for when only the top and bottom inset are non-zero. - */ - private void assertInsetByTopBottom(Rect actual, int expectedInsetTop, - int expectedInsetBottom) { - assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); - } - - /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ - private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, - int expectedInsetRight, int expectedInsetBottom) { - assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, - mFrames.mDisplayWidth - expectedInsetRight, - mFrames.mDisplayHeight - expectedInsetBottom), actual); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java deleted file mode 100644 index 07d5fea334cd..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTests.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; -import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; - -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.graphics.PixelFormat; -import android.platform.test.annotations.Presubmit; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class DisplayPolicyTests extends WindowTestsBase { - - private WindowState createOpaqueFullscreen(boolean hasLightNavBar) { - final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen"); - final WindowManager.LayoutParams attrs = win.mAttrs; - attrs.width = MATCH_PARENT; - attrs.height = MATCH_PARENT; - attrs.flags = - FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - attrs.format = PixelFormat.OPAQUE; - attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = - hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; - return win; - } - - private WindowState createDimmingDialogWindow(boolean canBeImTarget) { - final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog")); - final WindowManager.LayoutParams attrs = win.mAttrs; - attrs.width = WRAP_CONTENT; - attrs.height = WRAP_CONTENT; - attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM); - attrs.format = PixelFormat.TRANSLUCENT; - when(win.isDimming()).thenReturn(true); - return win; - } - - private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar, - boolean hasLightNavBar) { - final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod"); - final WindowManager.LayoutParams attrs = win.mAttrs; - attrs.width = MATCH_PARENT; - attrs.height = MATCH_PARENT; - attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN - | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0); - attrs.format = PixelFormat.TRANSPARENT; - attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = - hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; - win.mHasSurface = visible; - return win; - } - - @Test - public void testChooseNavigationColorWindowLw() { - final WindowState opaque = createOpaqueFullscreen(false); - - final WindowState dimmingImTarget = createDimmingDialogWindow(true); - final WindowState dimmingNonImTarget = createDimmingDialogWindow(false); - - final WindowState visibleIme = createInputMethodWindow(true, true, false); - final WindowState invisibleIme = createInputMethodWindow(false, true, false); - final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); - - // If everything is null, return null - assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( - null, null, null, NAV_BAR_BOTTOM)); - - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, null, NAV_BAR_BOTTOM)); - assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, null, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM)); - - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - null, null, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); - - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, visibleIme, NAV_BAR_RIGHT)); - - // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color - // window. - assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( - opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); - } - - @Test - public void testUpdateLightNavigationBarLw() { - final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false); - final WindowState opaqueLightNavBar = createOpaqueFullscreen(true); - - final WindowState dimming = createDimmingDialogWindow(false); - - final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false); - final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true); - - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, - null, null)); - - // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag. - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar)); - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null, - opaqueDarkNavBar)); - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar, - opaqueLightNavBar, null, opaqueLightNavBar)); - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar)); - - // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - 0, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - 0, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming)); - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming)); - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar, - dimming)); - - // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar, - imeDrawDarkNavBar)); - - // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins. - assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( - SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar, - imeDrawDarkNavBar, imeDrawDarkNavBar)); - - // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. - assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, - DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar, - opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java deleted file mode 100644 index 1d63c57e6cfe..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.util.DisplayMetrics.DENSITY_DEFAULT; -import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; -import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; -import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; -import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; - -import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Matrix; -import android.graphics.RectF; -import android.os.IBinder; -import android.testing.TestableResources; -import android.util.Pair; -import android.view.DisplayCutout; -import android.view.DisplayInfo; -import android.view.Gravity; -import android.view.View; -import android.view.WindowManagerGlobal; - -import com.android.internal.R; -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Before; - -public class DisplayPolicyTestsBase extends WindowTestsBase { - - static final int DISPLAY_WIDTH = 500; - static final int DISPLAY_HEIGHT = 1000; - static final int DISPLAY_DENSITY = 320; - - static final int STATUS_BAR_HEIGHT = 10; - static final int NAV_BAR_HEIGHT = 15; - static final int DISPLAY_CUTOUT_HEIGHT = 8; - - DisplayPolicy mDisplayPolicy; - - @Before - public void setUpBase() { - super.setUpBase(); - mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy()); - - final TestContextWrapper context = - new TestContextWrapper(mDisplayPolicy.getSystemUiContext()); - final TestableResources resources = context.getResourceMocker(); - resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT); - resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT); - resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); - resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); - resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); - when(mDisplayPolicy.getSystemUiContext()).thenReturn(context); - when(mDisplayPolicy.hasNavigationBar()).thenReturn(true); - - final int shortSizeDp = - Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; - final int longSizeDp = - Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; - mDisplayContent.getDisplayRotation().configure( - DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp); - mDisplayPolicy.configure(DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp); - mDisplayPolicy.onConfigurationChanged(); - - mStatusBarWindow.mAttrs.gravity = Gravity.TOP; - addWindow(mStatusBarWindow); - mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT; - - mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; - addWindow(mNavBarWindow); - mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; - } - - void addWindow(WindowState win) { - mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */); - assertEquals(WindowManagerGlobal.ADD_OKAY, - mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs)); - win.mHasSurface = true; - } - - static Pair displayInfoAndCutoutForRotation(int rotation, - boolean withDisplayCutout) { - final DisplayInfo info = new DisplayInfo(); - WmDisplayCutout cutout = null; - - final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; - info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; - info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; - info.rotation = rotation; - if (withDisplayCutout) { - cutout = WmDisplayCutout.computeSafeInsets( - displayCutoutForRotation(rotation), info.logicalWidth, - info.logicalHeight); - info.displayCutout = cutout.getDisplayCutout(); - } else { - info.displayCutout = null; - } - return Pair.create(info, cutout); - } - - private static DisplayCutout displayCutoutForRotation(int rotation) { - final RectF rectF = - new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); - - final Matrix m = new Matrix(); - transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); - m.mapRect(rectF); - - int pos = -1; - switch (rotation) { - case ROTATION_0: - pos = BOUNDS_POSITION_TOP; - break; - case ROTATION_90: - pos = BOUNDS_POSITION_LEFT; - break; - case ROTATION_180: - pos = BOUNDS_POSITION_BOTTOM; - break; - case ROTATION_270: - pos = BOUNDS_POSITION_RIGHT; - break; - } - - return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top, - (int) rectF.right, (int) rectF.bottom, pos); - } - - static class TestContextWrapper extends ContextWrapper { - private final TestableResources mResourceMocker; - - TestContextWrapper(Context targetContext) { - super(targetContext); - mResourceMocker = new TestableResources(targetContext.getResources()); - } - - @Override - public int checkPermission(String permission, int pid, int uid) { - return PackageManager.PERMISSION_GRANTED; - } - - @Override - public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { - return PackageManager.PERMISSION_GRANTED; - } - - @Override - public Resources getResources() { - return mResourceMocker.getResources(); - } - - TestableResources getResourceMocker() { - return mResourceMocker; - } - } - -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java deleted file mode 100644 index 7f390a55232d..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ /dev/null @@ -1,445 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; -import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -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.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.WindowConfiguration; -import android.platform.test.annotations.Presubmit; -import android.view.Display; -import android.view.DisplayInfo; -import android.view.Surface; - -import androidx.test.filters.SmallTest; - -import com.android.server.policy.WindowManagerPolicy; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.io.File; - -/** - * Tests for the {@link DisplayWindowSettings} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:DisplayWindowSettingsTests - */ -@SmallTest -@Presubmit -public class DisplayWindowSettingsTests extends WindowTestsBase { - - private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); - private DisplayWindowSettings mTarget; - - DisplayInfo mPrivateDisplayInfo; - - private DisplayContent mPrimaryDisplay; - private DisplayContent mSecondaryDisplay; - private DisplayContent mPrivateDisplay; - - @Before - public void setUp() throws Exception { - deleteRecursively(TEST_FOLDER); - - mWm.setSupportsFreeformWindowManagement(false); - mWm.setIsPc(false); - mWm.setForceDesktopModeOnExternalDisplays(false); - - mTarget = new DisplayWindowSettings(mWm, TEST_FOLDER); - - mPrimaryDisplay = mWm.getDefaultDisplayContentLocked(); - mSecondaryDisplay = mDisplayContent; - assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId()); - - mPrivateDisplayInfo = new DisplayInfo(mDisplayInfo); - mPrivateDisplayInfo.flags |= Display.FLAG_PRIVATE; - mPrivateDisplay = createNewDisplay(mPrivateDisplayInfo); - assertNotEquals(Display.DEFAULT_DISPLAY, mPrivateDisplay.getDisplayId()); - assertNotEquals(mSecondaryDisplay.getDisplayId(), mPrivateDisplay.getDisplayId()); - } - - @After - public void tearDown() { - deleteRecursively(TEST_FOLDER); - } - - @Test - public void testPrimaryDisplayDefaultToFullscreen_NoFreeformSupport() { - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, - mPrimaryDisplay.getWindowingMode()); - } - - @Test - public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() { - mWm.setSupportsFreeformWindowManagement(true); - - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, - mPrimaryDisplay.getWindowingMode()); - } - - @Test - public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() { - mWm.setSupportsFreeformWindowManagement(true); - mWm.setForceDesktopModeOnExternalDisplays(true); - - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, - mPrimaryDisplay.getWindowingMode()); - } - - @Test - public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() { - mWm.setSupportsFreeformWindowManagement(true); - mWm.setIsPc(true); - - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, - mPrimaryDisplay.getWindowingMode()); - } - - @Test - public void testSecondaryDisplayDefaultToFullscreen_NoFreeformSupport() { - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, - mSecondaryDisplay.getWindowingMode()); - } - - @Test - public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() { - mWm.setSupportsFreeformWindowManagement(true); - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, - mSecondaryDisplay.getWindowingMode()); - } - - @Test - public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() { - mWm.setSupportsFreeformWindowManagement(true); - mWm.setForceDesktopModeOnExternalDisplays(true); - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, - mSecondaryDisplay.getWindowingMode()); - } - - @Test - public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() { - mWm.setSupportsFreeformWindowManagement(true); - mWm.setIsPc(true); - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, - mSecondaryDisplay.getWindowingMode()); - } - - @Test - public void testDefaultToOriginalMetrics() { - final int originalWidth = mSecondaryDisplay.mBaseDisplayWidth; - final int originalHeight = mSecondaryDisplay.mBaseDisplayHeight; - final int originalDensity = mSecondaryDisplay.mBaseDisplayDensity; - final boolean originalScalingDisabled = mSecondaryDisplay.mDisplayScalingDisabled; - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(originalWidth, mSecondaryDisplay.mBaseDisplayWidth); - assertEquals(originalHeight, mSecondaryDisplay.mBaseDisplayHeight); - assertEquals(originalDensity, mSecondaryDisplay.mBaseDisplayDensity); - assertEquals(originalScalingDisabled, mSecondaryDisplay.mDisplayScalingDisabled); - } - - @Test - public void testSetForcedSize() { - final DisplayInfo originalInfo = new DisplayInfo(mSecondaryDisplay.getDisplayInfo()); - // Provides the orginal display info to avoid changing initial display size. - doAnswer(invocation -> { - ((DisplayInfo) invocation.getArguments()[1]).copyFrom(originalInfo); - return null; - }).when(mWm.mDisplayManagerInternal).getNonOverrideDisplayInfo(anyInt(), any()); - - mTarget.setForcedSize(mSecondaryDisplay, 1000 /* width */, 2000 /* height */); - applySettingsToDisplayByNewInstance(mSecondaryDisplay); - - assertEquals(1000 /* width */, mSecondaryDisplay.mBaseDisplayWidth); - assertEquals(2000 /* height */, mSecondaryDisplay.mBaseDisplayHeight); - - mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId()); - assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth); - assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight); - } - - @Test - public void testSetForcedDensity() { - mTarget.setForcedDensity(mSecondaryDisplay, 600 /* density */, 0 /* userId */); - applySettingsToDisplayByNewInstance(mSecondaryDisplay); - - assertEquals(600 /* density */, mSecondaryDisplay.mBaseDisplayDensity); - - mWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */); - assertEquals(mSecondaryDisplay.mInitialDisplayDensity, - mSecondaryDisplay.mBaseDisplayDensity); - } - - @Test - public void testSetForcedScalingMode() { - mTarget.setForcedScalingMode(mSecondaryDisplay, DisplayContent.FORCE_SCALING_MODE_DISABLED); - applySettingsToDisplayByNewInstance(mSecondaryDisplay); - - assertTrue(mSecondaryDisplay.mDisplayScalingDisabled); - - mWm.setForcedDisplayScalingMode(mSecondaryDisplay.getDisplayId(), - DisplayContent.FORCE_SCALING_MODE_AUTO); - assertFalse(mSecondaryDisplay.mDisplayScalingDisabled); - } - - @Test - public void testDefaultToZeroOverscan() { - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertOverscan(mPrimaryDisplay, 0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */); - } - - @Test - public void testPersistOverscanInSameInstance() { - final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); - mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); - - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); - } - - @Test - public void testPersistOverscanAcrossInstances() { - final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); - mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); - - applySettingsToDisplayByNewInstance(mPrimaryDisplay); - - assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); - } - - @Test - public void testDefaultToFreeUserRotation() { - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); - assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, rotation.getUserRotationMode()); - assertFalse(rotation.isRotationFrozen()); - } - - @Test - public void testDefaultTo0DegRotation() { - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(Surface.ROTATION_0, mSecondaryDisplay.getDisplayRotation().getUserRotation()); - } - - @Test - public void testPrivateDisplayDefaultToDestroyContent() { - assertEquals(REMOVE_CONTENT_MODE_DESTROY, - mTarget.getRemoveContentModeLocked(mPrivateDisplay)); - } - - @Test - public void testPublicDisplayDefaultToMoveToPrimary() { - assertEquals(REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY, - mTarget.getRemoveContentModeLocked(mSecondaryDisplay)); - } - - @Test - public void testDefaultToNotShowWithInsecureKeyguard() { - assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mPrivateDisplay)); - assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mSecondaryDisplay)); - } - - @Test - public void testPublicDisplayNotAllowSetShouldShowWithInsecureKeyguard() { - mTarget.setShouldShowWithInsecureKeyguardLocked(mSecondaryDisplay, true); - - assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mSecondaryDisplay)); - } - - @Test - public void testPrivateDisplayAllowSetShouldShowWithInsecureKeyguard() { - mTarget.setShouldShowWithInsecureKeyguardLocked(mPrivateDisplay, true); - - assertTrue(mTarget.shouldShowWithInsecureKeyguardLocked(mPrivateDisplay)); - } - - @Test - public void testPrimaryDisplayShouldShowSystemDecors() { - assertTrue(mTarget.shouldShowSystemDecorsLocked(mPrimaryDisplay)); - - mTarget.setShouldShowSystemDecorsLocked(mPrimaryDisplay, false); - - // Default display should show system decors - assertTrue(mTarget.shouldShowSystemDecorsLocked(mPrimaryDisplay)); - } - - @Test - public void testSecondaryDisplayDefaultToNotShowSystemDecors() { - assertFalse(mTarget.shouldShowSystemDecorsLocked(mSecondaryDisplay)); - } - - @Test - public void testPrimaryDisplayShouldShowIme() { - assertTrue(mTarget.shouldShowImeLocked(mPrimaryDisplay)); - - mTarget.setShouldShowImeLocked(mPrimaryDisplay, false); - - assertTrue(mTarget.shouldShowImeLocked(mPrimaryDisplay)); - } - - @Test - public void testSecondaryDisplayDefaultToNotShowIme() { - assertFalse(mTarget.shouldShowImeLocked(mSecondaryDisplay)); - } - - @Test - public void testPersistUserRotationModeInSameInstance() { - mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, - Surface.ROTATION_90); - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); - assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode()); - assertTrue(rotation.isRotationFrozen()); - } - - @Test - public void testPersistUserRotationInSameInstance() { - mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, - Surface.ROTATION_90); - - mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); - - assertEquals(Surface.ROTATION_90, mSecondaryDisplay.getDisplayRotation().getUserRotation()); - } - - @Test - public void testPersistUserRotationModeAcrossInstances() { - mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, - Surface.ROTATION_270); - - applySettingsToDisplayByNewInstance(mSecondaryDisplay); - - final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); - assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode()); - assertTrue(rotation.isRotationFrozen()); - } - - @Test - public void testPersistUserRotationAcrossInstances() { - mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, - Surface.ROTATION_270); - - applySettingsToDisplayByNewInstance(mSecondaryDisplay); - - assertEquals(Surface.ROTATION_270, - mSecondaryDisplay.getDisplayRotation().getUserRotation()); - } - - @Test - public void testNotFixedToUserRotationByDefault() { - mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, - Surface.ROTATION_0); - - final DisplayRotation displayRotation = mock(DisplayRotation.class); - mPrimaryDisplay = spy(mPrimaryDisplay); - when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); - - mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - - verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false)); - } - - @Test - public void testSetFixedToUserRotation() { - mTarget.setFixedToUserRotation(mPrimaryDisplay, true); - - final DisplayRotation displayRotation = mock(DisplayRotation.class); - mPrimaryDisplay = spy(mPrimaryDisplay); - when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); - - applySettingsToDisplayByNewInstance(mPrimaryDisplay); - - verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true)); - } - - private static void assertOverscan(DisplayContent display, int left, int top, int right, - int bottom) { - final DisplayInfo info = display.getDisplayInfo(); - - assertEquals(left, info.overscanLeft); - assertEquals(top, info.overscanTop); - assertEquals(right, info.overscanRight); - assertEquals(bottom, info.overscanBottom); - } - - /** - * This method helps to ensure read and write persistent settings successfully because the - * constructor of {@link DisplayWindowSettings} should read the persistent file from the given - * path that also means the previous state must be written correctly. - */ - private void applySettingsToDisplayByNewInstance(DisplayContent display) { - new DisplayWindowSettings(mWm, TEST_FOLDER).applySettingsToDisplayLocked(display); - } - - private static boolean deleteRecursively(File file) { - boolean fullyDeleted = true; - if (file.isFile()) { - return file.delete(); - } else if (file.isDirectory()) { - final File[] files = file.listFiles(); - for (File child : files) { - fullyDeleted &= deleteRecursively(child); - } - fullyDeleted &= file.delete(); - } - return fullyDeleted; - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java deleted file mode 100644 index a04bf16eaf1c..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DockedStackDividerControllerTests.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class DockedStackDividerControllerTests { - - @Test - public void testIsDockSideAllowedDockTop() { - // Docked top is always allowed - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedDockBottom() { - // Cannot dock bottom - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarMovable() { - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_LEFT, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_RIGHT, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_RIGHT, true /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_LEFT, true /* navigationBarCanMove */)); - } - - @Test - public void testIsDockSideAllowedNavigationBarNotMovable() { - // Navigation bar is not movable such as tablets - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, - NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java deleted file mode 100644 index ad293d963f3e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/DragDropControllerTests.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.ClipData; -import android.graphics.PixelFormat; -import android.os.IBinder; -import android.os.Looper; -import android.os.UserHandle; -import android.os.UserManagerInternal; -import android.platform.test.annotations.Presubmit; -import android.view.InputChannel; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.server.LocalServices; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Tests for the {@link DragDropController} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:DragDropControllerTests - */ -@SmallTest -@Presubmit -public class DragDropControllerTests extends WindowTestsBase { - private static final int TIMEOUT_MS = 3000; - private TestDragDropController mTarget; - private WindowState mWindow; - private IBinder mToken; - - static class TestDragDropController extends DragDropController { - private Runnable mCloseCallback; - - TestDragDropController(WindowManagerService service, Looper looper) { - super(service, looper); - } - - void setOnClosedCallbackLocked(Runnable runnable) { - assertTrue(dragDropActiveLocked()); - mCloseCallback = runnable; - } - - @Override - void onDragStateClosedLocked(DragState dragState) { - super.onDragStateClosedLocked(dragState); - if (mCloseCallback != null) { - mCloseCallback.run(); - mCloseCallback = null; - } - } - } - - /** - * Creates a window state which can be used as a drop target. - */ - private WindowState createDropTargetWindow(String name, int ownerId) { - final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( - mDisplayContent); - final TaskStack stack = createStackControllerOnStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; - final Task task = createTaskInStack(stack, ownerId); - task.addChild(token, 0); - - final WindowState window = createWindow( - null, TYPE_BASE_APPLICATION, token, name, ownerId, false); - window.mInputChannel = new InputChannel(); - window.mHasSurface = true; - return window; - } - - @BeforeClass - public static void setUpOnce() { - final UserManagerInternal userManager = mock(UserManagerInternal.class); - LocalServices.addService(UserManagerInternal.class, userManager); - } - - @AfterClass - public static void tearDownOnce() { - LocalServices.removeServiceForTest(UserManagerInternal.class); - } - - @Before - public void setUp() throws Exception { - mTarget = new TestDragDropController(mWm, mWm.mH.getLooper()); - mDisplayContent = spy(mDisplayContent); - mWindow = createDropTargetWindow("Drag test window", 0); - doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0); - when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true); - - synchronized (mWm.mGlobalLock) { - mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); - } - } - - @After - public void tearDown() throws Exception { - final CountDownLatch latch; - synchronized (mWm.mGlobalLock) { - if (!mTarget.dragDropActiveLocked()) { - return; - } - if (mToken != null) { - mTarget.cancelDragAndDrop(mToken); - } - latch = new CountDownLatch(1); - mTarget.setOnClosedCallbackLocked(latch::countDown); - } - assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - } - - @Test - public void testDragFlow() { - dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0); - } - - @Test - public void testPerformDrag_NullDataWithGrantUri() { - dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0); - } - - @Test - public void testPerformDrag_NullDataToOtherUser() { - final WindowState otherUsersWindow = - createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE); - doReturn(otherUsersWindow).when(mDisplayContent).getTouchableWinAtPointLocked(10, 10); - - dragFlow(0, null, 10, 10); - } - - private void dragFlow(int flag, ClipData data, float dropX, float dropY) { - final SurfaceSession appSession = new SurfaceSession(); - try { - final SurfaceControl surface = new SurfaceControl.Builder(appSession) - .setName("drag surface") - .setBufferSize(100, 100) - .setFormat(PixelFormat.TRANSLUCENT) - .build(); - - assertTrue(mWm.mInputManager.transferTouchFocus(null, null)); - mToken = mTarget.performDrag( - new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, - data); - assertNotNull(mToken); - - mTarget.handleMotionEvent(false, dropX, dropY); - mToken = mWindow.mClient.asBinder(); - } finally { - appSession.kill(); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java deleted file mode 100644 index 241b987e58ba..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.InsetsState.TYPE_TOP_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static org.junit.Assert.assertEquals; - -import android.graphics.Insets; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.InsetsSource; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@FlakyTest(detail = "Promote once confirmed non-flaky") -@Presubmit -public class InsetsSourceProviderTest extends WindowTestsBase { - - private InsetsSourceProvider mProvider = new InsetsSourceProvider( - new InsetsSource(TYPE_TOP_BAR)); - - @Test - public void testPostLayout() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - topBar.getFrameLw().set(0, 0, 500, 100); - topBar.mHasSurface = true; - mProvider.setWindow(topBar, null); - mProvider.onPostLayout(); - assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame()); - assertEquals(Insets.of(0, 100, 0, 0), - mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), - false /* ignoreVisibility */)); - } - - @Test - public void testPostLayout_invisible() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - topBar.getFrameLw().set(0, 0, 500, 100); - mProvider.setWindow(topBar, null); - mProvider.onPostLayout(); - assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), - false /* ignoreVisibility */)); - } - - @Test - public void testPostLayout_frameProvider() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - topBar.getFrameLw().set(0, 0, 500, 100); - mProvider.setWindow(topBar, - (displayFrames, windowState, rect) -> { - rect.set(10, 10, 20, 20); - }); - mProvider.onPostLayout(); - assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java deleted file mode 100644 index 7505db103bbf..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/InsetsStateControllerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.InsetsState.TYPE_IME; -import static android.view.InsetsState.TYPE_NAVIGATION_BAR; -import static android.view.InsetsState.TYPE_TOP_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import android.platform.test.annotations.Presubmit; -import android.view.InsetsState; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@FlakyTest(detail = "Promote once confirmed non-flaky") -@Presubmit -public class InsetsStateControllerTest extends WindowTestsBase { - - @Test - public void testStripForDispatch_notOwn() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) - .setWindow(topBar, null); - topBar.setInsetProvider( - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); - assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app) - .getSource(TYPE_TOP_BAR)); - } - - @Test - public void testStripForDispatch_own() { - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) - .setWindow(topBar, null); - topBar.setInsetProvider( - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); - assertEquals(new InsetsState(), - mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar)); - } - - @Test - public void testStripForDispatch_navBar() { - final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow"); - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) - .setWindow(topBar, null); - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR) - .setWindow(navBar, null); - mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME) - .setWindow(ime, null); - assertEquals(new InsetsState(), - mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java deleted file mode 100644 index 1fae317f3af8..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/PinnedStackControllerTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.Display.DEFAULT_DISPLAY; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; -import android.view.IPinnedStackListener; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:PinnedStackControllerTest - */ -@SmallTest -@Presubmit -public class PinnedStackControllerTest extends WindowTestsBase { - - @Mock private IPinnedStackListener mIPinnedStackListener; - @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub); - } - - @Test - public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException { - mWm.mSupportsPictureInPicture = true; - mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener); - - verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0); - verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0); - verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), - eq(false), anyInt()); - verify(mIPinnedStackListener).onActionsChanged(any()); - verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean()); - - reset(mIPinnedStackListener); - - final int SHELF_HEIGHT = 300; - - mWm.setShelfHeight(true, SHELF_HEIGHT); - verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT); - verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), - eq(true), anyInt()); - verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java deleted file mode 100644 index ee3bba73cd1e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; -import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.os.Binder; -import android.os.IInterface; -import android.platform.test.annotations.Presubmit; -import android.util.SparseBooleanArray; -import android.view.IRecentsAnimationRunner; -import android.view.SurfaceControl; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:RecentsAnimationControllerTest - */ -@SmallTest -@Presubmit -public class RecentsAnimationControllerTest extends WindowTestsBase { - - @Mock SurfaceControl mMockLeash; - @Mock SurfaceControl.Transaction mMockTransaction; - @Mock OnAnimationFinishedCallback mFinishedCallback; - @Mock IRecentsAnimationRunner mMockRunner; - @Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks; - private RecentsAnimationController mController; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mMockRunner.asBinder()).thenReturn(new Binder()); - mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, - DEFAULT_DISPLAY); - } - - @Test - public void testRemovedBeforeStarted_expectCanceled() throws Exception { - final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(), - false /* isRecentTaskInvisible */); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - - // Remove the app window so that the animation target can not be created - appWindow.removeImmediately(); - mController.startAnimation(); - - // Verify that the finish callback to reparent the leash is called - verify(mFinishedCallback).onAnimationFinished(eq(adapter)); - // Verify the animation canceled callback to the app was made - verify(mMockRunner).onAnimationCanceled(); - verifyNoMoreInteractionsExceptAsBinder(mMockRunner); - } - - @Test - public void testCancelAfterRemove_expectIgnored() { - final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(), - false /* isRecentTaskInvisible */); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - - // Remove the app window so that the animation target can not be created - appWindow.removeImmediately(); - mController.startAnimation(); - mController.cleanupAnimation(REORDER_KEEP_IN_PLACE); - try { - mController.cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "test"); - } catch (Exception e) { - fail("Unexpected failure when canceling animation after finishing it"); - } - } - - @FlakyTest(bugId = 117117823) - @Test - public void testIncludedApps_expectTargetAndVisible() { - mWm.setRecentsAnimationController(mController); - final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); - final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - hiddenAppWindow.setHidden(true); - mDisplayContent.getConfiguration().windowConfiguration.setRotation( - mDisplayContent.getRotation()); - mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray()); - - // Ensure that we are animating the target activity as well - assertTrue(mController.isAnimatingTask(homeAppWindow.getTask())); - assertTrue(mController.isAnimatingTask(appWindow.getTask())); - assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask())); - } - - private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { - verify(binder, atLeast(0)).asBinder(); - verifyNoMoreInteractions(binder); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java deleted file mode 100644 index fa5379576e8b..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.graphics.Point; -import android.graphics.Rect; -import android.os.Binder; -import android.os.IInterface; -import android.platform.test.annotations.Presubmit; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; - -import androidx.test.filters.SmallTest; - -import com.android.server.testutils.OffsettableClock; -import com.android.server.testutils.TestHandler; -import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Build/Install/Run: - * atest FrameworksServicesTests:RemoteAnimationControllerTest - */ -@SmallTest -@Presubmit -public class RemoteAnimationControllerTest extends WindowTestsBase { - - @Mock SurfaceControl mMockLeash; - @Mock Transaction mMockTransaction; - @Mock OnAnimationFinishedCallback mFinishedCallback; - @Mock IRemoteAnimationRunner mMockRunner; - private RemoteAnimationAdapter mAdapter; - private RemoteAnimationController mController; - private final OffsettableClock mClock = new OffsettableClock.Stopped(); - private TestHandler mHandler; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mMockRunner.asBinder()).thenReturn(new Binder()); - mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50); - mAdapter.setCallingPid(123); - mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0); - mController = new RemoteAnimationController(mWm, mAdapter, mHandler); - } - - @Test - public void testRun() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mDisplayContent.mOpeningApps.add(win.mAppToken); - try { - final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - mController.goodToGo(); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture()); - assertEquals(1, appsCaptor.getValue().length); - final RemoteAnimationTarget app = appsCaptor.getValue()[0]; - assertEquals(new Point(50, 100), app.position); - assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); - assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex); - assertEquals(win.mAppToken.getTask().mTaskId, app.taskId); - assertEquals(mMockLeash, app.leash); - assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect); - assertEquals(false, app.isTranslucent); - verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex); - verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y); - verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50)); - - finishedCaptor.getValue().onAnimationFinished(); - verify(mFinishedCallback).onAnimationFinished(eq(adapter)); - } finally { - mDisplayContent.mOpeningApps.clear(); - } - } - - @Test - public void testCancel() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - mController.goodToGo(); - - adapter.onAnimationCancelled(mMockLeash); - verify(mMockRunner).onAnimationCancelled(); - } - - @Test - public void testTimeout() throws Exception { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - mController.goodToGo(); - - mClock.fastForward(2500); - mHandler.timeAdvance(); - - verify(mMockRunner).onAnimationCancelled(); - verify(mFinishedCallback).onAnimationFinished(eq(adapter)); - } - - @Test - public void testTimeout_scaled() throws Exception { - mWm.setAnimationScale(2, 5.0f); - try{ - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - mController.goodToGo(); - - mClock.fastForward(2500); - mHandler.timeAdvance(); - - verify(mMockRunner, never()).onAnimationCancelled(); - - mClock.fastForward(10000); - mHandler.timeAdvance(); - - verify(mMockRunner).onAnimationCancelled(); - verify(mFinishedCallback).onAnimationFinished(eq(adapter)); - } finally { - mWm.setAnimationScale(2, 1.0f); - } - - } - - @Test - public void testZeroAnimations() { - mController.goodToGo(); - verifyNoMoreInteractionsExceptAsBinder(mMockRunner); - } - - @Test - public void testNotReallyStarted() { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - mController.goodToGo(); - verifyNoMoreInteractionsExceptAsBinder(mMockRunner); - } - - @Test - public void testOneNotStarted() throws Exception { - final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1"); - final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2"); - mController.createAnimationAdapter(win1.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - mController.goodToGo(); - mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); - final ArgumentCaptor appsCaptor = - ArgumentCaptor.forClass(RemoteAnimationTarget[].class); - final ArgumentCaptor finishedCaptor = - ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); - verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture()); - assertEquals(1, appsCaptor.getValue().length); - assertEquals(mMockLeash, appsCaptor.getValue()[0].leash); - } - - @Test - public void testRemovedBeforeStarted() { - final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); - final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, - new Point(50, 100), new Rect(50, 100, 150, 150)); - adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); - win.mAppToken.removeImmediately(); - mController.goodToGo(); - verifyNoMoreInteractionsExceptAsBinder(mMockRunner); - verify(mFinishedCallback).onAnimationFinished(eq(adapter)); - } - - private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { - verify(binder, atLeast(0)).asBinder(); - verifyNoMoreInteractions(binder); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java deleted file mode 100644 index ce5b13cab1a7..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/StackWindowControllerTests.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Test class for {@link StackWindowController}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:StackWindowControllerTests - */ -@SmallTest -@Presubmit -public class StackWindowControllerTests extends WindowTestsBase { - @Test - public void testRemoveContainer() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - - final TaskStack stack = stackController.mContainer; - final Task task = taskController.mContainer; - assertNotNull(stack); - assertNotNull(task); - stackController.removeContainer(); - // Assert that the container was removed. - assertNull(stackController.mContainer); - assertNull(taskController.mContainer); - assertNull(stack.getDisplayContent()); - assertNull(task.getDisplayContent()); - assertNull(task.mStack); - } - - @Test - public void testRemoveContainer_deferRemoval() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - - final TaskStack stack = stackController.mContainer; - final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; - // Stack removal is deferred if one of its child is animating. - task.setLocalIsAnimating(true); - - stackController.removeContainer(); - // For the case of deferred removal the stack controller will no longer be connected to the - // container, but the task controller will still be connected to the its container until - // the stack window container is removed. - assertNull(stackController.mContainer); - assertNull(stack.getController()); - assertNotNull(taskController.mContainer); - assertNotNull(task.getController()); - - stack.removeImmediately(); - assertNull(taskController.mContainer); - assertNull(task.getController()); - } - - @Test - public void testReparent() { - // Create first stack on primary display. - final StackWindowController stack1Controller = - createStackControllerOnDisplay(mDisplayContent); - final TaskStack stack1 = stack1Controller.mContainer; - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); - final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; - task1.mOnDisplayChangedCalled = false; - - // Create second display and put second stack on it. - final DisplayContent dc = createNewDisplay(); - final StackWindowController stack2Controller = - createStackControllerOnDisplay(dc); - final TaskStack stack2 = stack2Controller.mContainer; - - // Reparent - stack1Controller.reparent(dc.getDisplayId(), new Rect(), true /* onTop */); - assertEquals(dc, stack1.getDisplayContent()); - final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); - final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); - assertEquals(stack1PositionInParent, stack2PositionInParent + 1); - assertTrue(task1.mOnDisplayChangedCalled); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java deleted file mode 100644 index 584f2695891b..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import static java.util.concurrent.TimeUnit.SECONDS; - -import android.animation.AnimationHandler.AnimationFrameCallbackProvider; -import android.animation.ValueAnimator; -import android.graphics.Matrix; -import android.graphics.Point; -import android.os.PowerManagerInternal; -import android.platform.test.annotations.Presubmit; -import android.view.Choreographer; -import android.view.Choreographer.FrameCallback; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; -import android.view.animation.Animation; -import android.view.animation.TranslateAnimation; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.concurrent.CountDownLatch; - -/** - * Test class for {@link SurfaceAnimationRunner}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:SurfaceAnimationRunnerTest - */ -@SmallTest -@Presubmit -public class SurfaceAnimationRunnerTest extends WindowTestsBase { - - @Mock SurfaceControl mMockSurface; - @Mock Transaction mMockTransaction; - @Mock AnimationSpec mMockAnimationSpec; - @Mock PowerManagerInternal mMockPowerManager; - @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); - - private SurfaceAnimationRunner mSurfaceAnimationRunner; - private CountDownLatch mFinishCallbackLatch; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mFinishCallbackLatch = new CountDownLatch(1); - mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null, - mMockTransaction, mMockPowerManager); - } - - private void finishedCallback() { - mFinishCallbackLatch.countDown(); - } - - @Test - public void testAnimation() throws Exception { - mSurfaceAnimationRunner - .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, - this::finishedCallback); - - // Ensure that the initial transformation has been applied. - final Matrix m = new Matrix(); - m.setTranslate(-10, 0); - verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); - verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f)); - - mFinishCallbackLatch.await(1, SECONDS); - assertFinishCallbackCalled(); - - m.setTranslate(10, 0); - verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); - - // At least 3 times: After initialization, first frame, last frame. - verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f)); - } - - @Test - public void testCancel_notStarted() { - mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, - mMockTransaction, mMockPowerManager); - mSurfaceAnimationRunner - .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, - this::finishedCallback); - mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); - waitUntilHandlersIdle(); - assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty()); - assertFinishCallbackNotCalled(); - } - - @Test - public void testCancel_running() throws Exception { - mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, - mMockTransaction, mMockPowerManager); - mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, - mMockTransaction, this::finishedCallback); - waitUntilNextFrame(); - assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); - mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); - assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); - waitUntilHandlersIdle(); - assertFinishCallbackNotCalled(); - } - - @FlakyTest(bugId = 71719744) - @Test - public void testCancel_sneakyCancelBeforeUpdate() throws Exception { - mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() { - { - setFloatValues(0f, 1f); - } - - @Override - public void addUpdateListener(AnimatorUpdateListener listener) { - super.addUpdateListener(animation -> { - // Sneaky test cancels animation just before applying frame to simulate - // interleaving of multiple threads. Muahahaha - if (animation.getCurrentPlayTime() > 0) { - mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); - } - listener.onAnimationUpdate(animation); - }); - } - }, mMockTransaction, mMockPowerManager); - when(mMockAnimationSpec.getDuration()).thenReturn(200L); - mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction, - this::finishedCallback); - - // We need to wait for two frames: The first frame starts the animation, the second frame - // actually cancels the animation. - waitUntilNextFrame(); - waitUntilNextFrame(); - assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); - verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L)); - } - - @FlakyTest(bugId = 74780584) - @Test - public void testDeferStartingAnimations() throws Exception { - mSurfaceAnimationRunner.deferStartingAnimations(); - mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, - mMockTransaction, this::finishedCallback); - waitUntilNextFrame(); - assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); - mSurfaceAnimationRunner.continueStartingAnimations(); - waitUntilNextFrame(); - assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); - mFinishCallbackLatch.await(1, SECONDS); - assertFinishCallbackCalled(); - } - - @Test - public void testPowerHint() throws Exception { - mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, - mMockTransaction, mMockPowerManager); - mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, - mMockTransaction, this::finishedCallback); - waitUntilNextFrame(); - - // TODO: For some reason we don't have access to PowerHint definition from the tests. For - // now let's just verify that we got some kind of hint. - verify(mMockPowerManager).powerHint(anyInt(), anyInt()); - } - - private void waitUntilNextFrame() throws Exception { - final CountDownLatch latch = new CountDownLatch(1); - mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, - latch::countDown, null /* token */); - latch.await(); - } - - private void assertFinishCallbackCalled() { - assertEquals(0, mFinishCallbackLatch.getCount()); - } - - private void assertFinishCallbackNotCalled() { - assertEquals(1, mFinishCallbackLatch.getCount()); - } - - private AnimationSpec createTranslateAnimation() { - final Animation a = new TranslateAnimation(-10, 10, 0, 0); - a.initialize(0, 0, 0, 0); - a.setDuration(50); - return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */); - } - - /** - * Callback provider that doesn't animate at all. - */ - private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider { - - @Override - public void postFrameCallback(FrameCallback callback) { - } - - @Override - public void postCommitCallback(Runnable runnable) { - } - - @Override - public long getFrameTime() { - return 0; - } - - @Override - public long getFrameDelay() { - return 0; - } - - @Override - public void setFrameDelay(long delay) { - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java deleted file mode 100644 index e638a6accf7c..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Builder; -import android.view.SurfaceControl.Transaction; -import android.view.SurfaceSession; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.SurfaceAnimator.Animatable; -import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Test class for {@link SurfaceAnimatorTest}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:SurfaceAnimatorTest - */ -@SmallTest -@Presubmit -public class SurfaceAnimatorTest extends WindowTestsBase { - - @Mock AnimationAdapter mSpec; - @Mock AnimationAdapter mSpec2; - @Mock Transaction mTransaction; - - private SurfaceSession mSession = new SurfaceSession(); - private MyAnimatable mAnimatable; - private MyAnimatable mAnimatable2; - private DeferFinishAnimatable mDeferFinishAnimatable; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mAnimatable = new MyAnimatable(mWm, mSession, mTransaction); - mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction); - mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction); - } - - @After - public void tearDown() { - mAnimatable = null; - mAnimatable2 = null; - mDeferFinishAnimatable = null; - mSession.kill(); - mSession = null; - } - - @Test - public void testRunAnimation() { - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( - OnAnimationFinishedCallback.class); - assertAnimating(mAnimatable); - verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle())); - verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); - - callbackCaptor.getValue().onAnimationFinished(mSpec); - assertNotAnimating(mAnimatable); - assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).destroy(eq(mAnimatable.mLeash)); - // TODO: Verify reparenting once we use mPendingTransaction to reparent it back - } - - @Test - public void testOverrideAnimation() { - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - final SurfaceControl firstLeash = mAnimatable.mLeash; - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */); - - verify(mTransaction).destroy(eq(firstLeash)); - assertFalse(mAnimatable.mFinishedCallbackCalled); - - final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( - OnAnimationFinishedCallback.class); - assertAnimating(mAnimatable); - verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); - - // First animation was finished, but this shouldn't cancel the second animation - callbackCaptor.getValue().onAnimationFinished(mSpec); - assertTrue(mAnimatable.mSurfaceAnimator.isAnimating()); - - // Second animation was finished - verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture()); - callbackCaptor.getValue().onAnimationFinished(mSpec2); - assertNotAnimating(mAnimatable); - assertTrue(mAnimatable.mFinishedCallbackCalled); - } - - @Test - public void testCancelAnimation() { - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - assertAnimating(mAnimatable); - mAnimatable.mSurfaceAnimator.cancelAnimation(); - assertNotAnimating(mAnimatable); - verify(mSpec).onAnimationCancelled(any()); - assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).destroy(eq(mAnimatable.mLeash)); - } - - @Test - public void testDelayingAnimationStart() { - mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - verifyZeroInteractions(mSpec); - assertAnimating(mAnimatable); - assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed()); - mAnimatable.mSurfaceAnimator.endDelayingAnimationStart(); - verify(mSpec).startAnimation(any(), any(), any()); - } - - @Test - public void testDelayingAnimationStartAndCancelled() { - mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - mAnimatable.mSurfaceAnimator.cancelAnimation(); - verifyZeroInteractions(mSpec); - assertNotAnimating(mAnimatable); - assertTrue(mAnimatable.mFinishedCallbackCalled); - verify(mTransaction).destroy(eq(mAnimatable.mLeash)); - } - - @Test - public void testTransferAnimation() { - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); - - final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( - OnAnimationFinishedCallback.class); - verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); - final SurfaceControl leash = mAnimatable.mLeash; - - mAnimatable2.mSurfaceAnimator.transferAnimation(mAnimatable.mSurfaceAnimator); - assertNotAnimating(mAnimatable); - assertAnimating(mAnimatable2); - assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash); - verify(mTransaction, never()).destroy(eq(leash)); - callbackCaptor.getValue().onAnimationFinished(mSpec); - assertNotAnimating(mAnimatable2); - assertTrue(mAnimatable2.mFinishedCallbackCalled); - verify(mTransaction).destroy(eq(leash)); - } - - @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") - public void testDeferFinish() { - - // Start animation - mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, - true /* hidden */); - final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( - OnAnimationFinishedCallback.class); - assertAnimating(mDeferFinishAnimatable); - verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); - - // Finish the animation but then make sure we are deferring. - callbackCaptor.getValue().onAnimationFinished(mSpec); - assertAnimating(mDeferFinishAnimatable); - - // Now end defer finishing. - mDeferFinishAnimatable.mEndDeferFinishCallback.run(); - assertNotAnimating(mAnimatable2); - assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled); - verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash)); - } - - private void assertAnimating(MyAnimatable animatable) { - assertTrue(animatable.mSurfaceAnimator.isAnimating()); - assertNotNull(animatable.mSurfaceAnimator.getAnimation()); - } - - private void assertNotAnimating(MyAnimatable animatable) { - assertFalse(animatable.mSurfaceAnimator.isAnimating()); - assertNull(animatable.mSurfaceAnimator.getAnimation()); - } - - private static class MyAnimatable implements Animatable { - - private final SurfaceSession mSession; - private final Transaction mTransaction; - final SurfaceControl mParent; - final SurfaceControl mSurface; - final SurfaceAnimator mSurfaceAnimator; - SurfaceControl mLeash; - boolean mFinishedCallbackCalled; - - MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) { - mSession = session; - mTransaction = transaction; - mParent = wm.makeSurfaceBuilder(mSession) - .setName("test surface parent") - .build(); - mSurface = wm.makeSurfaceBuilder(mSession) - .setName("test surface") - .build(); - mFinishedCallbackCalled = false; - mLeash = null; - mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, wm); - } - - @Override - public Transaction getPendingTransaction() { - return mTransaction; - } - - @Override - public void commitPendingTransaction() { - } - - @Override - public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { - } - - @Override - public void onAnimationLeashDestroyed(Transaction t) { - } - - @Override - public Builder makeAnimationLeash() { - return new SurfaceControl.Builder(mSession) { - - @Override - public SurfaceControl build() { - mLeash = super.build(); - return mLeash; - } - }.setParent(mParent); - } - - @Override - public SurfaceControl getAnimationLeashParent() { - return mParent; - } - - @Override - public SurfaceControl getSurfaceControl() { - return mSurface; - } - - @Override - public SurfaceControl getParentSurfaceControl() { - return mParent; - } - - @Override - public int getSurfaceWidth() { - return 1; - } - - @Override - public int getSurfaceHeight() { - return 1; - } - - private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true; - } - - private static class DeferFinishAnimatable extends MyAnimatable { - - Runnable mEndDeferFinishCallback; - - DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session, - Transaction transaction) { - super(wm, session, transaction); - } - - @Override - public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { - mEndDeferFinishCallback = endDeferFinishCallback; - return true; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java deleted file mode 100644 index 785b955863db..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositionerTests.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - -import static com.android.server.wm.TaskPositioner.MIN_ASPECT; -import static com.android.server.wm.WindowManagerService.dipToPixel; -import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; -import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.app.IActivityTaskManager; -import android.graphics.Rect; -import android.util.DisplayMetrics; -import android.util.Log; -import android.view.Display; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -/** - * Tests for the {@link TaskPositioner} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskPositionerTests - */ -@SmallTest -public class TaskPositionerTests extends WindowTestsBase { - - private static final boolean DEBUGGING = false; - private static final String TAG = "TaskPositionerTest"; - - private final static int MOUSE_DELTA_X = 5; - private final static int MOUSE_DELTA_Y = 5; - - private int mMinVisibleWidth; - private int mMinVisibleHeight; - private TaskPositioner mPositioner; - private WindowState mWindow; - private Rect mDimBounds = new Rect(); - - @Before - public void setUp() throws Exception { - TaskPositioner.setFactory(null); - - final Display display = mDisplayContent.getDisplay(); - final DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); - - // This should be the same calculation as the TaskPositioner uses. - mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm); - mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm); - - mPositioner = new TaskPositioner(mWm, Mockito.mock(IActivityTaskManager.class)); - mPositioner.register(mDisplayContent); - - mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window")); - final Task task = Mockito.spy(mWindow.getTask()); - Mockito.when(mWindow.getTask()).thenReturn(task); - - Mockito.doAnswer(invocation -> { - final Rect rect = (Rect) invocation.getArguments()[0]; - rect.set(mDimBounds); - return (Void) null; - }).when(task).getDimBounds(Mockito.any(Rect.class)); - - mWindow.getStack().setWindowingMode(WINDOWING_MODE_FREEFORM); - } - - @Test - public void testOverrideFactory() { - final boolean[] created = new boolean[1]; - created[0] = false; - TaskPositioner.setFactory(new TaskPositioner.Factory() { - @Override - public TaskPositioner create(WindowManagerService service) { - created[0] = true; - return null; - } - }); - - assertNull(TaskPositioner.create(mWm)); - assertTrue(created[0]); - } - - /** - * This tests that free resizing will allow to change the orientation as well - * as does some basic tests (e.g. dragging in Y only will keep X stable). - */ - @Test - public void testBasicFreeWindowResizing() { - final Rect r = new Rect(100, 220, 700, 520); - final int midY = (r.top + r.bottom) / 2; - mDimBounds.set(r); - - // Start a drag resize starting upper left. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); - assertBoundsEquals(r, mPositioner.getWindowDragBounds()); - - // Drag to a good landscape size. - mPositioner.resizeDrag(0.0f, 0.0f); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a good portrait size. - mPositioner.resizeDrag(400.0f, 0.0f); - assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a too small size for the width. - mPositioner.resizeDrag(2000.0f, r.top); - assertBoundsEquals( - new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a too small size for the height. - mPositioner.resizeDrag(r.left, 2000.0f); - assertBoundsEquals( - new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Start a drag resize left and see that only the left coord changes.. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); - - // Drag to the left. - mPositioner.resizeDrag(0.0f, midY); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the right. - mPositioner.resizeDrag(200.0f, midY); - assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the top - mPositioner.resizeDrag(r.left, 0.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the bottom - mPositioner.resizeDrag(r.left, 1000.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored. - */ - @Test - public void testFreeWindowResizingTestAllEdges() { - final Rect r = new Rect(100, 220, 700, 520); - final int midX = (r.left + r.right) / 2; - final int midY = (r.top + r.bottom) / 2; - mDimBounds.set(r); - - // Drag upper left. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); - mPositioner.resizeDrag(0.0f, 0.0f); - assertTrue(r.left != mPositioner.getWindowDragBounds().left); - assertEquals(r.right, mPositioner.getWindowDragBounds().right); - assertTrue(r.top != mPositioner.getWindowDragBounds().top); - assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); - - // Drag upper. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); - mPositioner.resizeDrag(0.0f, 0.0f); - assertEquals(r.left, mPositioner.getWindowDragBounds().left); - assertEquals(r.right, mPositioner.getWindowDragBounds().right); - assertTrue(r.top != mPositioner.getWindowDragBounds().top); - assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); - - // Drag upper right. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); - mPositioner.resizeDrag(r.right + 100, 0.0f); - assertEquals(r.left, mPositioner.getWindowDragBounds().left); - assertTrue(r.right != mPositioner.getWindowDragBounds().right); - assertTrue(r.top != mPositioner.getWindowDragBounds().top); - assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); - - // Drag right. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, midY); - mPositioner.resizeDrag(r.right + 100, 0.0f); - assertEquals(r.left, mPositioner.getWindowDragBounds().left); - assertTrue(r.right != mPositioner.getWindowDragBounds().right); - assertEquals(r.top, mPositioner.getWindowDragBounds().top); - assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); - - // Drag bottom right. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, - r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y); - mPositioner.resizeDrag(r.right + 100, r.bottom + 100); - assertEquals(r.left, mPositioner.getWindowDragBounds().left); - assertTrue(r.right != mPositioner.getWindowDragBounds().right); - assertEquals(r.top, mPositioner.getWindowDragBounds().top); - assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); - - // Drag bottom. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, midX, r.bottom + MOUSE_DELTA_Y); - mPositioner.resizeDrag(r.right + 100, r.bottom + 100); - assertEquals(r.left, mPositioner.getWindowDragBounds().left); - assertEquals(r.right, mPositioner.getWindowDragBounds().right); - assertEquals(r.top, mPositioner.getWindowDragBounds().top); - assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); - - // Drag bottom left. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y); - mPositioner.resizeDrag(0.0f, r.bottom + 100); - assertTrue(r.left != mPositioner.getWindowDragBounds().left); - assertEquals(r.right, mPositioner.getWindowDragBounds().right); - assertEquals(r.top, mPositioner.getWindowDragBounds().top); - assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); - - // Drag left. - mPositioner.startDrag(mWindow, true /*resizing*/, - false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midX); - mPositioner.resizeDrag(0.0f, r.bottom + 100); - assertTrue(r.left != mPositioner.getWindowDragBounds().left); - assertEquals(r.right, mPositioner.getWindowDragBounds().right); - assertEquals(r.top, mPositioner.getWindowDragBounds().top); - assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); - } - - /** - * This tests that a constrained landscape window will keep the aspect and do the - * right things upon resizing when dragged from the top left corner. - */ - @Test - public void testLandscapePreservedWindowResizingDragTopLeft() { - final Rect r = new Rect(100, 220, 700, 520); - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); - assertBoundsEquals(r, mPositioner.getWindowDragBounds()); - - // Drag to a good landscape size. - mPositioner.resizeDrag(0.0f, 0.0f); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a good portrait size. - mPositioner.resizeDrag(400.0f, 0.0f); - int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT); - assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a too small size for the width. - mPositioner.resizeDrag(2000.0f, r.top); - final int w = mMinVisibleWidth; - final int h = Math.round(w / MIN_ASPECT); - assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a too small size for the height. - mPositioner.resizeDrag(r.left, 2000.0f); - assertBoundsEquals( - new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that a constrained landscape window will keep the aspect and do the - * right things upon resizing when dragged from the left corner. - */ - @Test - public void testLandscapePreservedWindowResizingDragLeft() { - final Rect r = new Rect(100, 220, 700, 520); - final int midY = (r.top + r.bottom) / 2; - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); - - // Drag to the left. - mPositioner.resizeDrag(0.0f, midY); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the right. - mPositioner.resizeDrag(200.0f, midY); - assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag all the way to the right and see the height also shrinking. - mPositioner.resizeDrag(2000.0f, midY); - final int w = mMinVisibleWidth; - final int h = Math.round((float)w / MIN_ASPECT); - assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), - mPositioner.getWindowDragBounds()); - - // Drag to the top. - mPositioner.resizeDrag(r.left, 0.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the bottom. - mPositioner.resizeDrag(r.left, 1000.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that a constrained landscape window will keep the aspect and do the - * right things upon resizing when dragged from the top corner. - */ - @Test - public void testLandscapePreservedWindowResizingDragTop() { - final Rect r = new Rect(100, 220, 700, 520); - final int midX = (r.left + r.right) / 2; - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); - - // Drag to the left (no change). - mPositioner.resizeDrag(0.0f, r.top); - assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the right (no change). - mPositioner.resizeDrag(2000.0f, r.top); - assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the top. - mPositioner.resizeDrag(300.0f, 0.0f); - int h = r.bottom - MOUSE_DELTA_Y; - int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT)); - assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the bottom. - mPositioner.resizeDrag(r.left, 1000.0f); - h = mMinVisibleHeight; - assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that a constrained portrait window will keep the aspect and do the - * right things upon resizing when dragged from the top left corner. - */ - @Test - public void testPortraitPreservedWindowResizingDragTopLeft() { - final Rect r = new Rect(330, 100, 630, 600); - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); - assertBoundsEquals(r, mPositioner.getWindowDragBounds()); - - // Drag to a good landscape size. - mPositioner.resizeDrag(0.0f, 0.0f); - int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a good portrait size. - mPositioner.resizeDrag(400.0f, 0.0f); - assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to a too small size for the height and the the width shrinking. - mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f); - final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT)); - final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT)); - assertBoundsEquals( - new Rect(r.right - w, r.bottom - h, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that a constrained portrait window will keep the aspect and do the - * right things upon resizing when dragged from the left corner. - */ - @Test - public void testPortraitPreservedWindowResizingDragLeft() { - final Rect r = new Rect(330, 100, 630, 600); - final int midY = (r.top + r.bottom) / 2; - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); - - // Drag to the left. - mPositioner.resizeDrag(0.0f, midY); - int w = r.right - MOUSE_DELTA_X; - int h = Math.round(w * MIN_ASPECT); - assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h), - mPositioner.getWindowDragBounds()); - - // Drag to the right. - mPositioner.resizeDrag(450.0f, midY); - assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag all the way to the right. - mPositioner.resizeDrag(2000.0f, midY); - w = mMinVisibleWidth; - h = Math.max(Math.round((float)w * MIN_ASPECT), r.height()); - assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), - mPositioner.getWindowDragBounds()); - - // Drag to the top. - mPositioner.resizeDrag(r.left, 0.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the bottom. - mPositioner.resizeDrag(r.left, 1000.0f); - assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - } - - /** - * This tests that a constrained portrait window will keep the aspect and do the - * right things upon resizing when dragged from the top corner. - */ - @Test - public void testPortraitPreservedWindowResizingDragTop() { - final Rect r = new Rect(330, 100, 630, 600); - final int midX = (r.left + r.right) / 2; - mDimBounds.set(r); - - mPositioner.startDrag(mWindow, true /*resizing*/, - true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); - - // Drag to the left (no change). - mPositioner.resizeDrag(0.0f, r.top); - assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the right (no change). - mPositioner.resizeDrag(2000.0f, r.top); - assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the top. - mPositioner.resizeDrag(300.0f, 0.0f); - int h = r.bottom - MOUSE_DELTA_Y; - int w = Math.min(r.width(), Math.round(h / MIN_ASPECT)); - assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom), - mPositioner.getWindowDragBounds()); - - // Drag to the bottom. - mPositioner.resizeDrag(r.left, 1000.0f); - h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT)); - w = Math.round(h / MIN_ASPECT); - assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom), - mPositioner.getWindowDragBounds()); - } - - private static void assertBoundsEquals(Rect expected, Rect actual) { - if (DEBUGGING) { - if (!expected.equals(actual)) { - Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString() - + ") " + Log.getStackTraceString(new Throwable())); - } - } - assertEquals("left", expected.left, actual.left); - assertEquals("right", expected.right, actual.right); - assertEquals("top", expected.top, actual.top); - assertEquals("bottom", expected.bottom, actual.bottom); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java deleted file mode 100644 index 00b462954bd9..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.platform.test.annotations.Presubmit; -import android.view.InputChannel; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link TaskPositioningController} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskPositioningControllerTests - */ -@FlakyTest(bugId = 117924387) -@SmallTest -@Presubmit -public class TaskPositioningControllerTests extends WindowTestsBase { - private static final int TIMEOUT_MS = 1000; - - private TaskPositioningController mTarget; - private WindowState mWindow; - - @Before - public void setUp() throws Exception { - assertNotNull(mWm.mTaskPositioningController); - mTarget = mWm.mTaskPositioningController; - - when(mWm.mInputManager.transferTouchFocus( - any(InputChannel.class), - any(InputChannel.class))).thenReturn(true); - - mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); - mWindow.mInputChannel = new InputChannel(); - synchronized (mWm.mGlobalLock) { - mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); - } - } - - @Test - public void testStartAndFinishPositioning() { - synchronized (mWm.mGlobalLock) { - assertFalse(mTarget.isPositioningLocked()); - assertNull(mTarget.getDragWindowHandleLocked()); - } - - assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0)); - - synchronized (mWm.mGlobalLock) { - assertTrue(mTarget.isPositioningLocked()); - assertNotNull(mTarget.getDragWindowHandleLocked()); - } - - mTarget.finishTaskPositioning(); - // Wait until the looper processes finishTaskPositioning. - assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); - - assertFalse(mTarget.isPositioningLocked()); - assertNull(mTarget.getDragWindowHandleLocked()); - } - - @Test - public void testHandleTapOutsideTask() { - synchronized (mWm.mGlobalLock) { - assertFalse(mTarget.isPositioningLocked()); - assertNull(mTarget.getDragWindowHandleLocked()); - } - - final DisplayContent content = mock(DisplayContent.class); - when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask()); - assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow()); - - mTarget.handleTapOutsideTask(content, 0, 0); - // Wait until the looper processes finishTaskPositioning. - assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); - - synchronized (mWm.mGlobalLock) { - assertTrue(mTarget.isPositioningLocked()); - assertNotNull(mTarget.getDragWindowHandleLocked()); - } - - mTarget.finishTaskPositioning(); - // Wait until the looper processes finishTaskPositioning. - assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); - - assertFalse(mTarget.isPositioningLocked()); - assertNull(mTarget.getDragWindowHandleLocked()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java deleted file mode 100644 index 1c6afd545b1f..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; - -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Test class for {@link TaskSnapshotCache}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotCacheTest - */ -@SmallTest -@Presubmit -public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { - - private TaskSnapshotCache mCache; - - @Override - @Before - public void setUp() { - super.setUp(); - - mCache = new TaskSnapshotCache(mWm, mLoader); - } - - @Test - public void testAppRemoved() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); - mCache.putSnapshot(window.getTask(), createSnapshot()); - assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - mCache.onAppRemoved(window.mAppToken); - assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - } - - @Test - public void testAppDied() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); - mCache.putSnapshot(window.getTask(), createSnapshot()); - assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - mCache.onAppDied(window.mAppToken); - assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - } - - @Test - public void testTaskRemoved() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); - mCache.putSnapshot(window.getTask(), createSnapshot()); - assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - mCache.onTaskRemoved(window.getTask().mTaskId); - assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, - false /* restoreFromDisk */, false /* reducedResolution */)); - } - - @Test - public void testReduced_notCached() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); - mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, - false /* restoreFromDisk */, false /* reducedResolution */)); - - // Load it from disk - assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, - true /* restoreFromDisk */, true /* reducedResolution */)); - - // Make sure it's not in the cache now. - assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, - false /* restoreFromDisk */, false /* reducedResolution */)); - } - - @Test - public void testRestoreFromDisk() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); - mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, - false /* restoreFromDisk */, false /* reducedResolution */)); - - // Load it from disk - assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, - true /* restoreFromDisk */, false /* reducedResolution */)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java deleted file mode 100644 index 792e8a6f7582..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_SECURE; -import static android.view.WindowManager.TRANSIT_UNSET; - -import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME; -import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL; - -import static junit.framework.Assert.assertEquals; - -import android.platform.test.annotations.Presubmit; -import android.util.ArraySet; - -import androidx.test.filters.SmallTest; - -import com.google.android.collect.Sets; - -import org.junit.Test; - -/** - * Test class for {@link TaskSnapshotController}. - * - * Build/Install/Run: - * * atest FrameworksServicesTests:TaskSnapshotControllerTest - */ -@SmallTest -@Presubmit -public class TaskSnapshotControllerTest extends WindowTestsBase { - - @Test - public void testGetClosingApps_closing() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); - closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, - true /* performLayout */, false /* isVoiceInteraction */); - final ArraySet closingApps = new ArraySet<>(); - closingApps.add(closingWindow.mAppToken); - final ArraySet closingTasks = new ArraySet<>(); - mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); - assertEquals(1, closingTasks.size()); - assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0)); - } - - @Test - public void testGetClosingApps_notClosing() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); - final WindowState openingWindow = createAppWindow(closingWindow.getTask(), - FIRST_APPLICATION_WINDOW, "openingWindow"); - closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, - true /* performLayout */, false /* isVoiceInteraction */); - openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET, - true /* performLayout */, false /* isVoiceInteraction */); - final ArraySet closingApps = new ArraySet<>(); - closingApps.add(closingWindow.mAppToken); - final ArraySet closingTasks = new ArraySet<>(); - mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); - assertEquals(0, closingTasks.size()); - } - - @Test - public void testGetClosingApps_skipClosingAppsSnapshotTasks() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); - closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, - true /* performLayout */, false /* isVoiceInteraction */); - final ArraySet closingApps = new ArraySet<>(); - closingApps.add(closingWindow.mAppToken); - final ArraySet closingTasks = new ArraySet<>(); - mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks( - Sets.newArraySet(closingWindow.mAppToken.getTask())); - mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); - assertEquals(0, closingTasks.size()); - } - - @Test - public void testGetSnapshotMode() { - final WindowState disabledWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); - disabledWindow.mAppToken.setDisablePreviewScreenshots(true); - assertEquals(SNAPSHOT_MODE_APP_THEME, - mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask())); - - final WindowState normalWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); - assertEquals(SNAPSHOT_MODE_REAL, - mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask())); - - final WindowState secureWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow"); - secureWindow.mAttrs.flags |= FLAG_SECURE; - assertEquals(SNAPSHOT_MODE_APP_THEME, - mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask())); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java deleted file mode 100644 index b0eafeeae043..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.app.ActivityManager.TaskSnapshot; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.ArraySet; -import android.view.View; - -import androidx.test.filters.MediumTest; - -import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; - -import org.junit.Test; - -import java.io.File; -import java.util.function.Predicate; - -/** - * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader} - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest - */ -@MediumTest -@Presubmit -public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { - - private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); - - @Test - public void testPersistAndLoadSnapshot() { - mPersister.persistSnapshot(1 , mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; - assertTrueForFiles(files, File::exists, " must exist"); - final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* reduced */); - assertNotNull(snapshot); - assertEquals(TEST_INSETS, snapshot.getContentInsets()); - assertNotNull(snapshot.getSnapshot()); - assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); - } - - private static void assertTrueForFiles(File[] files, Predicate predicate, - String message) { - for (File file : files) { - assertTrue(file.getName() + message, predicate.test(file)); - } - } - - @Test - public void testTaskRemovedFromRecents() { - mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.onTaskRemovedFromRecents(1, mTestUserId); - mPersister.waitForQueueEmpty(); - assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists()); - assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists()); - assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists()); - } - - /** - * Tests that persisting a couple of snapshots is being throttled. - */ - @Test - public void testThrottling() { - long ms = SystemClock.elapsedRealtime(); - mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); - mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); - mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); - mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); - mPersister.waitForQueueEmpty(); - assertTrue(SystemClock.elapsedRealtime() - ms > 500); - } - - /** - * Tests that too many store write queue items are being purged. - */ - @Test - public void testPurging() { - mPersister.persistSnapshot(100, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - mPersister.setPaused(true); - mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); - mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - mPersister.persistSnapshot(3, mTestUserId, createSnapshot()); - mPersister.persistSnapshot(4, mTestUserId, createSnapshot()); - mPersister.setPaused(false); - mPersister.waitForQueueEmpty(); - - // Make sure 1,2 were purged but removeObsoleteFiles wasn't. - final File[] existsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/3.proto"), - new File(FILES_DIR.getPath() + "/snapshots/4.proto")}; - final File[] nonExistsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/100.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.proto")}; - assertTrueForFiles(existsFiles, File::exists, " must exist"); - assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - } - - @Test - public void testGetTaskId() { - RemoveObsoleteFilesQueueItem removeObsoleteFilesQueueItem = - mPersister.new RemoveObsoleteFilesQueueItem(new ArraySet<>(), new int[] {}); - assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("blablablulp")); - assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("nothing.err")); - assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("/invalid/")); - assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.jpg")); - assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.proto")); - assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1.jpg")); - assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1_reduced.jpg")); - } - - @Test - public void testLowResolutionPersistAndLoadSnapshot() { - TaskSnapshot a = createSnapshot(0.5f /* reducedResolution */); - assertTrue(a.isReducedResolution()); - mPersister.persistSnapshot(1 , mTestUserId, a); - mPersister.waitForQueueEmpty(); - final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; - final File[] nonExistsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - }; - assertTrueForFiles(files, File::exists, " must exist"); - assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* reduced */); - assertNotNull(snapshot); - assertEquals(TEST_INSETS, snapshot.getContentInsets()); - assertNotNull(snapshot.getSnapshot()); - assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); - - final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId, false /* reduced */); - assertNull(snapshotNotExist); - } - - @Test - public void testIsRealSnapshotPersistAndLoadSnapshot() { - TaskSnapshot a = new TaskSnapshotBuilder() - .setIsRealSnapshot(true) - .build(); - TaskSnapshot b = new TaskSnapshotBuilder() - .setIsRealSnapshot(false) - .build(); - assertTrue(a.isRealSnapshot()); - assertFalse(b.isRealSnapshot()); - mPersister.persistSnapshot(1, mTestUserId, a); - mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); - final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); - final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); - assertNotNull(snapshotA); - assertNotNull(snapshotB); - assertTrue(snapshotA.isRealSnapshot()); - assertFalse(snapshotB.isRealSnapshot()); - } - - @Test - public void testWindowingModePersistAndLoadSnapshot() { - TaskSnapshot a = new TaskSnapshotBuilder() - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .build(); - TaskSnapshot b = new TaskSnapshotBuilder() - .setWindowingMode(WINDOWING_MODE_PINNED) - .build(); - assertEquals(WINDOWING_MODE_FULLSCREEN, a.getWindowingMode()); - assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode()); - mPersister.persistSnapshot(1, mTestUserId, a); - mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); - final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); - final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); - assertNotNull(snapshotA); - assertNotNull(snapshotB); - assertEquals(WINDOWING_MODE_FULLSCREEN, snapshotA.getWindowingMode()); - assertEquals(WINDOWING_MODE_PINNED, snapshotB.getWindowingMode()); - } - - @Test - public void testIsTranslucentPersistAndLoadSnapshot() { - TaskSnapshot a = new TaskSnapshotBuilder() - .setIsTranslucent(true) - .build(); - TaskSnapshot b = new TaskSnapshotBuilder() - .setIsTranslucent(false) - .build(); - assertTrue(a.isTranslucent()); - assertFalse(b.isTranslucent()); - mPersister.persistSnapshot(1, mTestUserId, a); - mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); - final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); - final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); - assertNotNull(snapshotA); - assertNotNull(snapshotB); - assertTrue(snapshotA.isTranslucent()); - assertFalse(snapshotB.isTranslucent()); - } - - @Test - public void testSystemUiVisibilityPersistAndLoadSnapshot() { - final int lightBarFlags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - TaskSnapshot a = new TaskSnapshotBuilder() - .setSystemUiVisibility(0) - .build(); - TaskSnapshot b = new TaskSnapshotBuilder() - .setSystemUiVisibility(lightBarFlags) - .build(); - assertEquals(0, a.getSystemUiVisibility()); - assertEquals(lightBarFlags, b.getSystemUiVisibility()); - mPersister.persistSnapshot(1, mTestUserId, a); - mPersister.persistSnapshot(2, mTestUserId, b); - mPersister.waitForQueueEmpty(); - final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); - final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); - assertNotNull(snapshotA); - assertNotNull(snapshotB); - assertEquals(0, snapshotA.getSystemUiVisibility()); - assertEquals(lightBarFlags, snapshotB.getSystemUiVisibility()); - } - - @Test - public void testRemoveObsoleteFiles() { - mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - final ArraySet taskIds = new ArraySet<>(); - taskIds.add(1); - mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId }); - mPersister.waitForQueueEmpty(); - final File[] existsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg") }; - final File[] nonExistsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; - assertTrueForFiles(existsFiles, File::exists, " must exist"); - assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - } - - @Test - public void testRemoveObsoleteFiles_addedOneInTheMeantime() { - mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); - final ArraySet taskIds = new ArraySet<>(); - taskIds.add(1); - mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId }); - mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); - mPersister.waitForQueueEmpty(); - final File[] existsFiles = new File[] { - new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2.proto"), - new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), - new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; - assertTrueForFiles(existsFiles, File::exists, " must exist"); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java deleted file mode 100644 index 946ffb60c759..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; -import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import android.app.ActivityManager.TaskSnapshot; -import android.content.ComponentName; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.GraphicBuffer; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.UserManager; - -import org.junit.After; -import org.junit.Before; - -import java.io.File; - -/** - * Base class for tests that use a {@link TaskSnapshotPersister}. - */ -class TaskSnapshotPersisterTestBase extends WindowTestsBase { - - private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); - static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir(); - - TaskSnapshotPersister mPersister; - TaskSnapshotLoader mLoader; - int mTestUserId; - - @Before - public void setUp() { - final UserManager um = UserManager.get(getInstrumentation().getTargetContext()); - mTestUserId = um.getUserHandle(); - mPersister = new TaskSnapshotPersister(userId -> FILES_DIR); - mLoader = new TaskSnapshotLoader(mPersister); - mPersister.start(); - } - - @After - public void tearDown() { - cleanDirectory(); - } - - private void cleanDirectory() { - final File[] files = new File(FILES_DIR, "snapshots").listFiles(); - if (files == null) { - return; - } - for (File file : files) { - if (!file.isDirectory()) { - file.delete(); - } - } - } - - TaskSnapshot createSnapshot() { - return createSnapshot(1f /* scale */); - } - - TaskSnapshot createSnapshot(float scale) { - return new TaskSnapshotBuilder() - .setScale(scale) - .build(); - } - - /** - * Builds a TaskSnapshot. - */ - static class TaskSnapshotBuilder { - - private float mScale = 1f; - private boolean mIsRealSnapshot = true; - private boolean mIsTranslucent = false; - private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; - private int mSystemUiVisibility = 0; - - TaskSnapshotBuilder setScale(float scale) { - mScale = scale; - return this; - } - - TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) { - mIsRealSnapshot = isRealSnapshot; - return this; - } - - TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) { - mIsTranslucent = isTranslucent; - return this; - } - - TaskSnapshotBuilder setWindowingMode(int windowingMode) { - mWindowingMode = windowingMode; - return this; - } - - TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) { - mSystemUiVisibility = systemUiVisibility; - return this; - } - - TaskSnapshot build() { - final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888, - USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); - Canvas c = buffer.lockCanvas(); - c.drawColor(Color.RED); - buffer.unlockCanvasAndPost(c); - return new TaskSnapshot(new ComponentName("", ""), buffer, ORIENTATION_PORTRAIT, - TEST_INSETS, mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot, - mWindowingMode, mSystemUiVisibility, mIsTranslucent); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java deleted file mode 100644 index a569b9e6dd6f..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager.TaskSnapshot; -import android.content.ComponentName; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.GraphicBuffer; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.Surface; - -import androidx.test.filters.SmallTest; - -import com.android.server.wm.TaskSnapshotSurface.Window; - -import org.junit.Test; - -/** - * Test class for {@link TaskSnapshotSurface}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskSnapshotSurfaceTest - */ -@SmallTest -@Presubmit -public class TaskSnapshotSurfaceTest extends WindowTestsBase { - - private TaskSnapshotSurface mSurface; - - private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis, - int windowFlags, Rect taskBounds) { - final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, - GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); - final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer, - ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */, - WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); - mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test", - Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds, - ORIENTATION_PORTRAIT); - } - - private void setupSurface(int width, int height) { - setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - new Rect(0, 0, width, height)); - } - - @Test - public void fillEmptyBackground_fillHorizontally() { - setupSurface(200, 100); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(200); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); - verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); - } - - @Test - public void fillEmptyBackground_fillVertically() { - setupSurface(100, 200); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(200); - mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); - verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); - } - - @Test - public void fillEmptyBackground_fillBoth() { - setupSurface(200, 200); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(200); - when(mockCanvas.getHeight()).thenReturn(200); - mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); - verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); - verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); - } - - @Test - public void fillEmptyBackground_dontFill_sameSize() { - setupSurface(100, 100); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); - verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); - } - - @Test - public void fillEmptyBackground_dontFill_bitmapLarger() { - setupSurface(100, 100); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); - verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); - } - - @Test - public void testCalculateSnapshotCrop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop()); - } - - @Test - public void testCalculateSnapshotCrop_taskNotOnTop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); - assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); - } - - @Test - public void testCalculateSnapshotCrop_navBarLeft() { - setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop()); - } - - @Test - public void testCalculateSnapshotCrop_navBarRight() { - setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop()); - } - - @Test - public void testCalculateSnapshotFrame() { - setupSurface(100, 100); - final Rect insets = new Rect(0, 10, 0, 10); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - assertEquals(new Rect(0, -10, 100, 70), - mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); - } - - @Test - public void testCalculateSnapshotFrame_navBarLeft() { - setupSurface(100, 100); - final Rect insets = new Rect(10, 10, 0, 0); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - assertEquals(new Rect(0, -10, 90, 80), - mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); - } - - @Test - public void testDrawStatusBarBackground() { - setupSurface(100, 100); - final Rect insets = new Rect(0, 10, 10, 0); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( - mockCanvas, new Rect(0, 0, 50, 100), 10); - verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); - } - - @Test - public void testDrawStatusBarBackground_nullFrame() { - setupSurface(100, 100); - final Rect insets = new Rect(0, 10, 10, 0); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( - mockCanvas, null, 10); - verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); - } - - @Test - public void testDrawStatusBarBackground_nope() { - setupSurface(100, 100); - final Rect insets = new Rect(0, 10, 10, 0); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( - mockCanvas, new Rect(0, 0, 100, 100), 10); - verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); - } - - @Test - public void testDrawNavigationBarBackground() { - final Rect insets = new Rect(0, 10, 0, 10); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - new Rect(0, 0, 100, 100)); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); - verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); - } - - @Test - public void testDrawNavigationBarBackground_left() { - final Rect insets = new Rect(10, 10, 0, 0); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - new Rect(0, 0, 100, 100)); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); - verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); - } - - @Test - public void testDrawNavigationBarBackground_right() { - final Rect insets = new Rect(0, 10, 10, 0); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - new Rect(0, 0, 100, 100)); - mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); - final Canvas mockCanvas = mock(Canvas.class); - when(mockCanvas.getWidth()).thenReturn(100); - when(mockCanvas.getHeight()).thenReturn(100); - mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); - verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java deleted file mode 100644 index f01e9f0662c9..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskStackContainersTests - */ -@SmallTest -@Presubmit -public class TaskStackContainersTests extends WindowTestsBase { - - private TaskStack mPinnedStack; - - @Before - public void setUp() throws Exception { - mPinnedStack = createStackControllerOnStackOnDisplay( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; - // Stack should contain visible app window to be considered visible. - final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); - assertFalse(mPinnedStack.isVisible()); - final WindowTestUtils.TestAppWindowToken pinnedApp = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - pinnedTask.addChild(pinnedApp, 0 /* addPos */); - assertTrue(mPinnedStack.isVisible()); - } - - @After - public void tearDown() throws Exception { - mPinnedStack.removeImmediately(); - } - - @Test - public void testStackPositionChildAt() { - // Test that always-on-top stack can't be moved to position other than top. - final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); - final TaskStack stack2 = createTaskStackOnDisplay(mDisplayContent); - - final WindowContainer taskStackContainer = stack1.getParent(); - - final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); - final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); - assertThat(pinnedStackPos).isGreaterThan(stack2Pos); - assertThat(stack2Pos).isGreaterThan(stack1Pos); - - taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false); - assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); - assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); - - taskStackContainer.positionChildAt(1, mPinnedStack, false); - assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); - assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); - } - - @Test - public void testStackPositionBelowPinnedStack() { - // Test that no stack can be above pinned stack. - final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); - - final WindowContainer taskStackContainer = stack1.getParent(); - - final int stackPos = taskStackContainer.mChildren.indexOf(stack1); - final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); - assertThat(pinnedStackPos).isGreaterThan(stackPos); - - taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); - assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); - - taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); - assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); - assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); - } - - @Test - public void testDisplayPositionWithPinnedStack() { - // The display contains pinned stack that was added in {@link #setUp}. - final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - - // Add another display at top. - mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), - false /* includingParents */); - - // Move the task of {@code mDisplayContent} to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); - final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent); - - assertEquals("The testing DisplayContent should be moved to top with task", - mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java deleted file mode 100644 index 7ac331829fb1..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskStackTests.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Tests for the {@link TaskStack} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskStackTests - */ -@SmallTest -@Presubmit -public class TaskStackTests extends WindowTestsBase { - - @Test - public void testStackPositionChildAt() { - final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task1 = createTaskInStack(stack, 0 /* userId */); - final Task task2 = createTaskInStack(stack, 1 /* userId */); - - // Current user task should be moved to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */); - assertEquals(stack.mChildren.get(0), task2); - assertEquals(stack.mChildren.get(1), task1); - - // Non-current user won't be moved to top. - stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); - assertEquals(stack.mChildren.get(0), task2); - assertEquals(stack.mChildren.get(1), task1); - } - - @Test - public void testClosingAppDifferentStackOrientation() { - final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task1 = createTaskInStack(stack, 0 /* userId */); - WindowTestUtils.TestAppWindowToken appWindowToken1 = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task1.addChild(appWindowToken1, 0); - appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - final Task task2 = createTaskInStack(stack, 1 /* userId */); - WindowTestUtils.TestAppWindowToken appWindowToken2 = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task2.addChild(appWindowToken2, 0); - appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - - assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation()); - mDisplayContent.mClosingApps.add(appWindowToken2); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation()); - } - - @Test - public void testMoveTaskToBackDifferentStackOrientation() { - final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task1 = createTaskInStack(stack, 0 /* userId */); - WindowTestUtils.TestAppWindowToken appWindowToken1 = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task1.addChild(appWindowToken1, 0); - appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - - final Task task2 = createTaskInStack(stack, 1 /* userId */); - WindowTestUtils.TestAppWindowToken appWindowToken2 = - WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task2.addChild(appWindowToken2, 0); - appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); - - assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation()); - task2.setSendingToBottom(true); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation()); - } - - @Test - public void testStackRemoveImmediately() { - final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final Task task = createTaskInStack(stack, 0 /* userId */); - assertEquals(stack, task.mStack); - - // Remove stack and check if its child is also removed. - stack.removeImmediately(); - assertNull(stack.getDisplayContent()); - assertNull(task.mStack); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java deleted file mode 100644 index bbf508dd1630..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Test class for {@link TaskWindowContainerController}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:TaskWindowContainerControllerTests - */ -@SmallTest -@Presubmit -public class TaskWindowContainerControllerTests extends WindowTestsBase { - - /* Comment out due to removal of AppWindowContainerController - @Test - public void testRemoveContainer() { - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(this); - final WindowTestUtils.TestAppWindowContainerController appController = - new WindowTestUtils.TestAppWindowContainerController(taskController); - - taskController.removeContainer(); - // Assert that the container was removed. - assertNull(taskController.mContainer); - assertNull(appController.mContainer); - } - */ - - /* Comment out due to removal of AppWindowContainerController - @Test - public void testRemoveContainer_deferRemoval() { - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(this); - final WindowTestUtils.TestAppWindowContainerController appController = - new WindowTestUtils.TestAppWindowContainerController(taskController); - - final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; - final AppWindowToken app = appController.mContainer; - task.mShouldDeferRemoval = true; - - taskController.removeContainer(); - // For the case of deferred removal the task controller will no longer be connected to the - // container, but the app controller will still be connected to the its container until - // the task window container is removed. - assertNull(taskController.mContainer); - assertNull(task.getController()); - assertNotNull(appController.mContainer); - assertNotNull(app.getController()); - - task.removeImmediately(); - assertNull(appController.mContainer); - assertNull(app.getController()); - } - */ - - @Test - public void testReparent() { - final StackWindowController stackController1 = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(stackController1); - final StackWindowController stackController2 = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController2 = - new WindowTestUtils.TestTaskWindowContainerController(stackController2); - - boolean gotException = false; - try { - taskController.reparent(stackController1, 0, false/* moveParents */); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue("Should not be able to reparent to the same parent", gotException); - - final StackWindowController stackController3 = - createStackControllerOnDisplay(mDisplayContent); - stackController3.setContainer(null); - gotException = false; - try { - taskController.reparent(stackController3, 0, false/* moveParents */); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue("Should not be able to reparent to a stack that doesn't have a container", - gotException); - - taskController.reparent(stackController2, 0, false/* moveParents */); - assertEquals(stackController2.mContainer, taskController.mContainer.getParent()); - assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent()); - assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent()); - } - - @Test - public void testReparent_BetweenDisplays() { - // Create first stack on primary display. - final StackWindowController stack1Controller = - createStackControllerOnDisplay(mDisplayContent); - final TaskStack stack1 = stack1Controller.mContainer; - final WindowTestUtils.TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); - final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; - task1.mOnDisplayChangedCalled = false; - assertEquals(mDisplayContent, stack1.getDisplayContent()); - - // Create second display and put second stack on it. - final DisplayContent dc = createNewDisplay(); - final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); - final TaskStack stack2 = stack2Controller.mContainer; - final WindowTestUtils.TestTaskWindowContainerController taskController2 = - new WindowTestUtils.TestTaskWindowContainerController(stack2Controller); - final WindowTestUtils.TestTask task2 = - (WindowTestUtils.TestTask) taskController2.mContainer; - - // Reparent and check state - taskController.reparent(stack2Controller, 0, false /* moveParents */); - assertEquals(stack2, task1.getParent()); - assertEquals(0, task1.positionInParent()); - assertEquals(1, task2.positionInParent()); - assertTrue(task1.mOnDisplayChangedCalled); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java deleted file mode 100644 index 432af0d7a469..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import android.graphics.Rect; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.util.MergedConfiguration; -import android.view.DisplayCutout; -import android.view.DragEvent; -import android.view.IWindow; -import android.view.InsetsState; - -import com.android.internal.os.IResultReceiver; - -public class TestIWindow extends IWindow.Stub { - @Override - public void executeCommand(String command, String parameters, - ParcelFileDescriptor descriptor) throws RemoteException { - } - - @Override - public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, - Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig, - Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, - DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException { - } - @Override - public void insetsChanged(InsetsState insetsState) throws RemoteException { - } - - @Override - public void moved(int newX, int newY) throws RemoteException { - } - - @Override - public void dispatchAppVisibility(boolean visible) throws RemoteException { - } - - @Override - public void dispatchGetNewSurface() throws RemoteException { - } - - @Override - public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) - throws RemoteException { - } - - @Override - public void closeSystemDialogs(String reason) throws RemoteException { - } - - @Override - public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) - throws RemoteException { - } - - @Override - public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, - boolean sync) throws RemoteException { - } - - @Override - public void dispatchDragEvent(DragEvent event) throws RemoteException { - } - - @Override - public void updatePointerIcon(float x, float y) throws RemoteException { - } - - @Override - public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, - int localChanges) throws RemoteException { - } - - @Override - public void dispatchWindowShown() throws RemoteException { - } - - @Override - public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) - throws RemoteException { - } - - @Override - public void dispatchPointerCaptureChanged(boolean hasCapture) { - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java deleted file mode 100644 index 7b542cb4f2f7..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.proto.ProtoOutputStream; -import android.view.IWindow; -import android.view.IWindowManager; -import android.view.KeyEvent; -import android.view.WindowManager; -import android.view.animation.Animation; - -import com.android.internal.policy.IKeyguardDismissCallback; -import com.android.internal.policy.IShortcutService; -import com.android.server.policy.WindowManagerPolicy; - -import java.io.PrintWriter; -import java.util.function.Supplier; - -class TestWindowManagerPolicy implements WindowManagerPolicy { - private final Supplier mWmSupplier; - - boolean keyguardShowingAndNotOccluded = false; - - private Runnable mRunnableWhenAddingSplashScreen; - - TestWindowManagerPolicy(Supplier wmSupplier) { - mWmSupplier = wmSupplier; - } - - @Override - public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) - throws RemoteException { - } - - @Override - public void init(Context context, IWindowManager windowManager, - WindowManagerFuncs windowManagerFuncs) { - } - - public void setDefaultDisplay(DisplayContentInfo displayContentInfo) { - } - - @Override - public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) { - return 0; - } - - @Override - public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) { - return false; - } - - @Override - public void adjustConfigurationLw(Configuration config, int keyboardPresence, - int navigationPresence) { - } - - @Override - public int getMaxWallpaperLayer() { - return 0; - } - - @Override - public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { - return attrs.type == TYPE_STATUS_BAR; - } - - @Override - public boolean canBeHiddenByKeyguardLw(WindowState win) { - return false; - } - - /** - * Sets a runnable to run when adding a splash screen which gets executed after the window has - * been added but before returning the surface. - */ - void setRunnableWhenAddingSplashScreen(Runnable r) { - mRunnableWhenAddingSplashScreen = r; - } - - @Override - public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, - CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, - int logo, int windowFlags, Configuration overrideConfig, int displayId) { - final com.android.server.wm.WindowState window; - final AppWindowToken atoken; - final WindowManagerService wm = mWmSupplier.get(); - synchronized (wm.mGlobalLock) { - atoken = wm.mRoot.getAppWindowToken(appToken); - IWindow iWindow = mock(IWindow.class); - doReturn(mock(IBinder.class)).when(iWindow).asBinder(); - window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken, - "Starting window", 0 /* ownerId */, false /* internalWindows */, wm, - mock(Session.class), iWindow); - atoken.startingWindow = window; - } - if (mRunnableWhenAddingSplashScreen != null) { - mRunnableWhenAddingSplashScreen.run(); - mRunnableWhenAddingSplashScreen = null; - } - return () -> { - synchronized (wm.mGlobalLock) { - atoken.removeChild(window); - atoken.startingWindow = null; - } - }; - } - - @Override - public void setKeyguardCandidateLw(WindowState win) { - } - - @Override - public Animation createHiddenByKeyguardExit(boolean onWallpaper, - boolean goingToNotificationShade) { - return null; - } - - @Override - public Animation createKeyguardWallpaperExit(boolean goingToNotificationShade) { - return null; - } - - @Override - public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { - return 0; - } - - @Override - public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { - return 0; - } - - @Override - public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { - return 0; - } - - @Override - public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) { - return null; - } - - @Override - public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { - } - - @Override - public void setAllowLockscreenWhenOn(int displayId, boolean allow) { - } - - @Override - public void startedWakingUp() { - } - - @Override - public void finishedWakingUp() { - } - - @Override - public void startedGoingToSleep(int why) { - } - - @Override - public void finishedGoingToSleep(int why) { - } - - @Override - public void screenTurningOn(ScreenOnListener screenOnListener) { - } - - @Override - public void screenTurnedOn() { - } - - @Override - public void screenTurningOff(ScreenOffListener screenOffListener) { - } - - @Override - public void screenTurnedOff() { - } - - @Override - public boolean isScreenOn() { - return true; - } - - @Override - public boolean okToAnimate() { - return true; - } - - @Override - public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { - } - - @Override - public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) { - } - - @Override - public void enableKeyguard(boolean enabled) { - } - - @Override - public void exitKeyguardSecurely(OnKeyguardExitResult callback) { - } - - @Override - public boolean isKeyguardLocked() { - return keyguardShowingAndNotOccluded; - } - - @Override - public boolean isKeyguardSecure(int userId) { - return false; - } - - @Override - public boolean isKeyguardOccluded() { - return false; - } - - @Override - public boolean isKeyguardTrustedLw() { - return false; - } - - @Override - public boolean isKeyguardShowingAndNotOccluded() { - return keyguardShowingAndNotOccluded; - } - - @Override - public boolean inKeyguardRestrictedKeyInputMode() { - return false; - } - - @Override - public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback, - CharSequence message) { - } - - @Override - public boolean isKeyguardDrawnLw() { - return false; - } - - @Override - public void onKeyguardOccludedChangedLw(boolean occluded) { - } - - public void setSafeMode(boolean safeMode) { - } - - @Override - public void systemReady() { - } - - @Override - public void systemBooted() { - } - - @Override - public void showBootMessage(CharSequence msg, boolean always) { - } - - @Override - public void hideBootMessages() { - } - - @Override - public void userActivity() { - } - - @Override - public void enableScreenAfterBoot() { - } - - @Override - public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always, - String reason) { - return false; - } - - @Override - public void keepScreenOnStartedLw() { - } - - @Override - public void keepScreenOnStoppedLw() { - } - - @Override - public boolean hasNavigationBar() { - return false; - } - - @Override - public void lockNow(Bundle options) { - } - - @Override - public void showRecentApps() { - } - - @Override - public void showGlobalActions() { - } - - @Override - public boolean isUserSetupComplete() { - return false; - } - - @Override - public int getUiMode() { - return 0; - } - - @Override - public void setCurrentUserLw(int newUserId) { - } - - @Override - public void setSwitchingUser(boolean switching) { - } - - @Override - public void writeToProto(ProtoOutputStream proto, long fieldId) { - } - - @Override - public void dump(String prefix, PrintWriter writer, String[] args) { - } - - @Override - public boolean isTopLevelWindow(int windowType) { - return false; - } - - @Override - public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { - } - - @Override - public void setPipVisibilityLw(boolean visible) { - } - - @Override - public void setRecentsVisibilityLw(boolean visible) { - } - - @Override - public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) { - } - - @Override - public void onSystemUiStarted() { - } - - @Override - public boolean canDismissBootAnimation() { - return true; - } - - @Override - public void requestUserActivityNotification() { - } - - @Override - public boolean setAodShowing(boolean aodShowing) { - return false; - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java deleted file mode 100644 index 9e22c0a86d96..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static junit.framework.Assert.assertTrue; - -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Test class for {@link AppTransition}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:UnknownAppVisibilityControllerTest - */ -@SmallTest -@Presubmit -public class UnknownAppVisibilityControllerTest extends WindowTestsBase { - - @Before - public void setUp() throws Exception { - mDisplayContent.mUnknownAppVisibilityController.clear(); - } - - @Test - public void testFlow() { - final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); - mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token); - mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token); - - // Make sure our handler processed the message. - SystemClock.sleep(100); - assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); - } - - @Test - public void testMultiple() { - final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1); - mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); - mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token2); - mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token1); - mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token2); - mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2); - - // Make sure our handler processed the message. - SystemClock.sleep(100); - assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); - } - - @Test - public void testClear() { - final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); - mDisplayContent.mUnknownAppVisibilityController.clear();; - assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); - } - - @Test - public void testAppRemoved() { - final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); - mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token); - assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java deleted file mode 100644 index 4ea6b3901f03..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WallpaperControllerTests.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; - -import static junit.framework.TestCase.assertNotNull; - -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.graphics.Bitmap; -import android.os.IBinder; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Tests for the {@link WallpaperController} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WallpaperControllerTests - */ -@SmallTest -@Presubmit -public class WallpaperControllerTests extends WindowTestsBase { - @Test - public void testWallpaperScreenshot() { - WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class); - - synchronized (mWm.mGlobalLock) { - // No wallpaper - final DisplayContent dc = createNewDisplay(); - Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); - - // No wallpaper WSA Surface - WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), - true, dc, true /* ownerCanManageAppTokens */); - WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, - wallpaperWindowToken, "wallpaperWindow"); - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); - - // Wallpaper with not visible WSA surface. - wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController; - wallpaperWindow.mWinAnimator.mLastAlpha = 1; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); - - when(windowSurfaceController.getShown()).thenReturn(true); - - // Wallpaper with WSA alpha set to 0. - wallpaperWindow.mWinAnimator.mLastAlpha = 0; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); - - // Wallpaper window with WSA Surface - wallpaperWindow.mWinAnimator.mLastAlpha = 1; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNotNull(wallpaperBitmap); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java deleted file mode 100644 index 3643457f061f..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowConfigurationTests.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP; -import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; -import static android.app.WindowConfiguration.WINDOW_CONFIG_ROTATION; -import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE; -import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import android.app.WindowConfiguration; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.DisplayInfo; -import android.view.Surface; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; - -/** - * Test class to for {@link android.app.WindowConfiguration}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowConfigurationTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class WindowConfigurationTests extends WindowTestsBase { - private Rect mParentBounds; - - @Before - public void setUp() throws Exception { - mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); - } - - /** Tests {@link android.app.WindowConfiguration#diff(WindowConfiguration, boolean)}. */ - @Test - public void testDiff() { - final Configuration config1 = new Configuration(); - final WindowConfiguration winConfig1 = config1.windowConfiguration; - final Configuration config2 = new Configuration(); - final WindowConfiguration winConfig2 = config2.windowConfiguration; - final Configuration config3 = new Configuration(); - final WindowConfiguration winConfig3 = config3.windowConfiguration; - final Configuration config4 = new Configuration(); - final WindowConfiguration winConfig4 = config4.windowConfiguration; - - winConfig1.setAppBounds(0, 1, 1, 0); - winConfig2.setAppBounds(1, 2, 2, 1); - winConfig3.setAppBounds(winConfig1.getAppBounds()); - winConfig4.setRotation(Surface.ROTATION_90); - - assertEquals(CONFIG_WINDOW_CONFIGURATION, config1.diff(config2)); - assertEquals(0, config1.diffPublicOnly(config2)); - assertEquals(WINDOW_CONFIG_APP_BOUNDS, - winConfig1.diff(winConfig2, false /* compareUndefined */)); - - winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE, - winConfig1.diff(winConfig2, false /* compareUndefined */)); - - winConfig2.setAlwaysOnTop(true); - assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE - | WINDOW_CONFIG_ALWAYS_ON_TOP, - winConfig1.diff(winConfig2, false /* compareUndefined */)); - - assertEquals(WINDOW_CONFIG_ROTATION, - winConfig1.diff(winConfig4, false /* compareUndefined */)); - - assertEquals(0, config1.diff(config3)); - assertEquals(0, config1.diffPublicOnly(config3)); - assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */)); - } - - /** Tests {@link android.app.WindowConfiguration#compareTo(WindowConfiguration)}. */ - @Test - public void testConfigurationCompareTo() { - final Configuration blankConfig = new Configuration(); - final WindowConfiguration blankWinConfig = new WindowConfiguration(); - - final Configuration config1 = new Configuration(); - final WindowConfiguration winConfig1 = config1.windowConfiguration; - winConfig1.setAppBounds(1, 2, 3, 4); - - final Configuration config2 = new Configuration(config1); - final WindowConfiguration winConfig2 = config2.windowConfiguration; - - assertEquals(config1.compareTo(config2), 0); - assertEquals(winConfig1.compareTo(winConfig2), 0); - - // Different windowing mode - winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertNotEquals(config1.compareTo(config2), 0); - assertNotEquals(winConfig1.compareTo(winConfig2), 0); - winConfig2.setWindowingMode(winConfig1.getWindowingMode()); - - // Different always on top state - winConfig2.setAlwaysOnTop(true); - assertNotEquals(config1.compareTo(config2), 0); - assertNotEquals(winConfig1.compareTo(winConfig2), 0); - winConfig2.setAlwaysOnTop(winConfig1.isAlwaysOnTop()); - - // Different bounds - winConfig2.setAppBounds(0, 2, 3, 4); - assertNotEquals(config1.compareTo(config2), 0); - assertNotEquals(winConfig1.compareTo(winConfig2), 0); - winConfig2.setAppBounds(winConfig1.getAppBounds()); - - // No bounds - assertEquals(config1.compareTo(blankConfig), -1); - assertEquals(winConfig1.compareTo(blankWinConfig), -1); - - // Different rotation - winConfig2.setRotation(Surface.ROTATION_180); - assertNotEquals(config1.compareTo(config2), 0); - assertNotEquals(winConfig1.compareTo(winConfig2), 0); - winConfig2.setRotation(winConfig1.getRotation()); - - assertEquals(blankConfig.compareTo(config1), 1); - assertEquals(blankWinConfig.compareTo(winConfig1), 1); - } - - @Test - public void testSetActivityType() { - final WindowConfiguration config = new WindowConfiguration(); - config.setActivityType(ACTIVITY_TYPE_HOME); - assertEquals(ACTIVITY_TYPE_HOME, config.getActivityType()); - - // Allowed to change from app process. - config.setActivityType(ACTIVITY_TYPE_STANDARD); - assertEquals(ACTIVITY_TYPE_STANDARD, config.getActivityType()); - } - - /** Ensures the configuration app bounds at the root level match the app dimensions. */ - @Test - public void testAppBounds_RootConfigurationBounds() { - final DisplayInfo info = mDisplayContent.getDisplayInfo(); - info.appWidth = 1024; - info.appHeight = 768; - - final Rect appBounds = mWm.computeNewConfiguration( - mDisplayContent.getDisplayId()).windowConfiguration.getAppBounds(); - // The bounds should always be positioned in the top left. - assertEquals(appBounds.left, 0); - assertEquals(appBounds.top, 0); - - // The bounds should equal the defined app width and height - assertEquals(appBounds.width(), info.appWidth); - assertEquals(appBounds.height(), info.appHeight); - } - - /** Ensures that bounds are clipped to their parent. */ - @Test - public void testAppBounds_BoundsClipping() { - final Rect shiftedBounds = new Rect(mParentBounds); - shiftedBounds.offset(10, 10); - final Rect expectedBounds = new Rect(mParentBounds); - expectedBounds.intersect(shiftedBounds); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds, - expectedBounds); - } - - /** Ensures that empty bounds are not propagated to the configuration. */ - @Test - public void testAppBounds_EmptyBounds() { - final Rect emptyBounds = new Rect(); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds, - null /*ExpectedBounds*/); - } - - /** Ensures that bounds on freeform stacks are not clipped. */ - @Test - public void testAppBounds_FreeFormBounds() { - final Rect freeFormBounds = new Rect(mParentBounds); - freeFormBounds.offset(10, 10); - testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds, - freeFormBounds); - } - - /** Ensures that fully contained bounds are not clipped. */ - @Test - public void testAppBounds_ContainedBounds() { - final Rect insetBounds = new Rect(mParentBounds); - insetBounds.inset(5, 5, 5, 5); - testStackBoundsConfiguration( - WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds); - } - - /** Ensures that full screen free form bounds are clipped */ - @Test - public void testAppBounds_FullScreenFreeFormBounds() { - final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth, - mDisplayInfo.logicalHeight); - testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds, - mParentBounds); - } - - private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, - Rect expectedConfigBounds) { - final StackWindowController stackController = createStackControllerOnStackOnDisplay( - windowingMode, ACTIVITY_TYPE_STANDARD, mDisplayContent); - - final Configuration parentConfig = mDisplayContent.getConfiguration(); - parentConfig.windowConfiguration.setAppBounds(parentBounds); - - final Configuration config = new Configuration(); - final WindowConfiguration winConfig = config.windowConfiguration; - stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/, - new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/, - false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig, - windowingMode); - // Assert that both expected and actual are null or are equal to each other - - assertEquals(expectedConfigBounds, winConfig.getAppBounds()); - } - -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java deleted file mode 100644 index 7592f1c1fca0..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerControllerTests.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.content.res.Configuration.EMPTY; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.content.res.Configuration; -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Test class for {@link WindowContainerController}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerControllerTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class WindowContainerControllerTests extends WindowTestsBase { - - @Test - public void testCreation() { - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - final WindowContainer container = new WindowContainer(mWm); - - container.setController(controller); - assertEquals(controller, container.getController()); - assertEquals(controller.mContainer, container); - } - - @Test - public void testSetContainer() { - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - final WindowContainer container = new WindowContainer(mWm); - - controller.setContainer(container); - assertEquals(controller.mContainer, container); - - // Assert we can't change the container to another one once set - boolean gotException = false; - try { - controller.setContainer(new WindowContainer(mWm)); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - - // Assert that we can set the container to null. - controller.setContainer(null); - assertNull(controller.mContainer); - } - - @Test - public void testRemoveContainer() { - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - final WindowContainer container = new WindowContainer(mWm); - - controller.setContainer(container); - assertEquals(controller.mContainer, container); - - controller.removeContainer(); - assertNull(controller.mContainer); - } - - @Test - public void testOnOverrideConfigurationChanged() { - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - final WindowContainer container = new WindowContainer(mWm); - - controller.setContainer(container); - assertEquals(controller.mContainer, container); - assertEquals(EMPTY, container.getOverrideConfiguration()); - - final Configuration config = new Configuration(); - config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - config.windowConfiguration.setAppBounds(10, 10, 10, 10); - - // Assert that the config change through the controller is propagated to the container. - controller.onOverrideConfigurationChanged(config); - assertEquals(config, container.getOverrideConfiguration()); - - // Assert the container configuration isn't changed after removal from the controller. - controller.removeContainer(); - controller.onOverrideConfigurationChanged(EMPTY); - assertEquals(config, container.getOverrideConfiguration()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java deleted file mode 100644 index e59afd656420..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; -import static com.android.server.wm.WindowContainer.POSITION_TOP; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; -import android.view.SurfaceSession; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -import java.util.Comparator; - -/** - * Test class for {@link WindowContainer}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerTests - */ -@SmallTest -@Presubmit -@FlakyTest(bugId = 74078662) -public class WindowContainerTests extends WindowTestsBase { - - @Test - public void testCreation() { - final TestWindowContainer w = new TestWindowContainerBuilder(mWm).setLayer(0).build(); - assertNull("window must have no parent", w.getParentWindow()); - assertEquals("window must have no children", 0, w.getChildrenCount()); - } - - @Test - public void testAdd() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1)); - final TestWindowContainer secondLayer1 = root.addChildWindow(builder.setLayer(1)); - final TestWindowContainer layer2 = root.addChildWindow(builder.setLayer(2)); - final TestWindowContainer layerNeg1 = root.addChildWindow(builder.setLayer(-1)); - final TestWindowContainer layerNeg2 = root.addChildWindow(builder.setLayer(-2)); - final TestWindowContainer secondLayerNeg1 = root.addChildWindow(builder.setLayer(-1)); - final TestWindowContainer layer0 = root.addChildWindow(builder.setLayer(0)); - - assertEquals(7, root.getChildrenCount()); - - assertEquals(root, layer1.getParentWindow()); - assertEquals(root, secondLayer1.getParentWindow()); - assertEquals(root, layer2.getParentWindow()); - assertEquals(root, layerNeg1.getParentWindow()); - assertEquals(root, layerNeg2.getParentWindow()); - assertEquals(root, secondLayerNeg1.getParentWindow()); - assertEquals(root, layer0.getParentWindow()); - - assertEquals(layerNeg2, root.getChildAt(0)); - assertEquals(secondLayerNeg1, root.getChildAt(1)); - assertEquals(layerNeg1, root.getChildAt(2)); - assertEquals(layer0, root.getChildAt(3)); - assertEquals(layer1, root.getChildAt(4)); - assertEquals(secondLayer1, root.getChildAt(5)); - assertEquals(layer2, root.getChildAt(6)); - - assertTrue(layer1.mOnParentSetCalled); - assertTrue(secondLayer1.mOnParentSetCalled); - assertTrue(layer2.mOnParentSetCalled); - assertTrue(layerNeg1.mOnParentSetCalled); - assertTrue(layerNeg2.mOnParentSetCalled); - assertTrue(secondLayerNeg1.mOnParentSetCalled); - assertTrue(layer0.mOnParentSetCalled); - } - - @Test - public void testAddChildSetsSurfacePosition() { - try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) { - - final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class); - mWm.mTransactionFactory = () -> transaction; - - WindowContainer child = new WindowContainer(mWm); - child.setBounds(1, 1, 10, 10); - - verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat()); - top.addChild(child, 0); - verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f)); - } - } - - @Test - public void testAdd_AlreadyHasParent() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - - boolean gotException = false; - try { - child1.addChildWindow(child2); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - - gotException = false; - try { - root.addChildWindow(child2); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - } - - @Test - public void testHasChild() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - final TestWindowContainer child21 = child2.addChildWindow(); - - assertEquals(2, root.getChildrenCount()); - assertEquals(2, child1.getChildrenCount()); - assertEquals(1, child2.getChildrenCount()); - - assertTrue(root.hasChild(child1)); - assertTrue(root.hasChild(child2)); - assertTrue(root.hasChild(child11)); - assertTrue(root.hasChild(child12)); - assertTrue(root.hasChild(child21)); - - assertTrue(child1.hasChild(child11)); - assertTrue(child1.hasChild(child12)); - assertFalse(child1.hasChild(child21)); - - assertTrue(child2.hasChild(child21)); - assertFalse(child2.hasChild(child11)); - assertFalse(child2.hasChild(child12)); - } - - @Test - public void testRemoveImmediately() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - final TestWindowContainer child21 = child2.addChildWindow(); - - assertNotNull(child12.getParentWindow()); - child12.removeImmediately(); - assertNull(child12.getParentWindow()); - assertEquals(1, child1.getChildrenCount()); - assertFalse(child1.hasChild(child12)); - assertFalse(root.hasChild(child12)); - - assertTrue(root.hasChild(child2)); - assertNotNull(child2.getParentWindow()); - child2.removeImmediately(); - assertNull(child2.getParentWindow()); - assertNull(child21.getParentWindow()); - assertEquals(0, child2.getChildrenCount()); - assertEquals(1, root.getChildrenCount()); - assertFalse(root.hasChild(child2)); - assertFalse(root.hasChild(child21)); - - assertTrue(root.hasChild(child1)); - assertTrue(root.hasChild(child11)); - - root.removeImmediately(); - assertEquals(0, root.getChildrenCount()); - } - - @Test - public void testRemoveImmediately_WithController() { - final WindowContainer container = new WindowContainer(mWm); - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - - container.setController(controller); - assertEquals(controller, container.getController()); - assertEquals(container, controller.mContainer); - - container.removeImmediately(); - assertNull(container.getController()); - assertNull(controller.mContainer); - } - - @Test - public void testSetController() { - final WindowContainerController controller = new WindowContainerController<>(null, mWm); - final WindowContainer container = new WindowContainer(mWm); - - container.setController(controller); - assertEquals(controller, container.getController()); - assertEquals(container, controller.mContainer); - - // Assert we can't change the controller to another one once set - boolean gotException = false; - try { - container.setController(new WindowContainerController<>(null, mWm)); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - - // Assert that we can set the controller to null. - container.setController(null); - assertNull(container.getController()); - assertNull(controller.mContainer); - } - - @Test - public void testAddChildByIndex() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child = root.addChildWindow(); - - final TestWindowContainer child2 = builder.setLayer(1).build(); - final TestWindowContainer child3 = builder.setLayer(2).build(); - final TestWindowContainer child4 = builder.setLayer(3).build(); - - // Test adding at top. - root.addChild(child2, POSITION_TOP); - assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1)); - - // Test adding at bottom. - root.addChild(child3, POSITION_BOTTOM); - assertEquals(child3, root.getChildAt(0)); - - // Test adding in the middle. - root.addChild(child4, 1); - assertEquals(child3, root.getChildAt(0)); - assertEquals(child4, root.getChildAt(1)); - assertEquals(child, root.getChildAt(2)); - assertEquals(child2, root.getChildAt(3)); - } - - @Test - public void testPositionChildAt() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child3 = root.addChildWindow(); - - // Test position at top. - root.positionChildAt(POSITION_TOP, child1, false /* includingParents */); - assertEquals(child1, root.getChildAt(root.getChildrenCount() - 1)); - - // Test position at bottom. - root.positionChildAt(POSITION_BOTTOM, child1, false /* includingParents */); - assertEquals(child1, root.getChildAt(0)); - - // Test position in the middle. - root.positionChildAt(1, child3, false /* includingParents */); - assertEquals(child1, root.getChildAt(0)); - assertEquals(child3, root.getChildAt(1)); - assertEquals(child2, root.getChildAt(2)); - } - - @Test - public void testPositionChildAtIncludeParents() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - final TestWindowContainer child13 = child1.addChildWindow(); - final TestWindowContainer child21 = child2.addChildWindow(); - final TestWindowContainer child22 = child2.addChildWindow(); - final TestWindowContainer child23 = child2.addChildWindow(); - - // Test moving to top. - child1.positionChildAt(POSITION_TOP, child11, true /* includingParents */); - assertEquals(child12, child1.getChildAt(0)); - assertEquals(child13, child1.getChildAt(1)); - assertEquals(child11, child1.getChildAt(2)); - assertEquals(child2, root.getChildAt(0)); - assertEquals(child1, root.getChildAt(1)); - - // Test moving to bottom. - child1.positionChildAt(POSITION_BOTTOM, child11, true /* includingParents */); - assertEquals(child11, child1.getChildAt(0)); - assertEquals(child12, child1.getChildAt(1)); - assertEquals(child13, child1.getChildAt(2)); - assertEquals(child1, root.getChildAt(0)); - assertEquals(child2, root.getChildAt(1)); - - // Test moving to middle, includeParents shouldn't do anything. - child2.positionChildAt(1, child21, true /* includingParents */); - assertEquals(child11, child1.getChildAt(0)); - assertEquals(child12, child1.getChildAt(1)); - assertEquals(child13, child1.getChildAt(2)); - assertEquals(child22, child2.getChildAt(0)); - assertEquals(child21, child2.getChildAt(1)); - assertEquals(child23, child2.getChildAt(2)); - assertEquals(child1, root.getChildAt(0)); - assertEquals(child2, root.getChildAt(1)); - } - - @Test - public void testPositionChildAtInvalid() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - - boolean gotException = false; - try { - // Check response to negative position. - root.positionChildAt(-1, child1, false /* includingParents */); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - - gotException = false; - try { - // Check response to position that's bigger than child number. - root.positionChildAt(3, child1, false /* includingParents */); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - } - - @Test - public void testIsAnimating() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true)); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true)); - final TestWindowContainer child21 = child2.addChildWindow(); - - assertFalse(root.isAnimating()); - assertTrue(child1.isAnimating()); - assertTrue(child11.isAnimating()); - assertTrue(child12.isAnimating()); - assertFalse(child2.isAnimating()); - assertFalse(child21.isAnimating()); - - assertTrue(root.isSelfOrChildAnimating()); - assertTrue(child1.isSelfOrChildAnimating()); - assertFalse(child11.isSelfOrChildAnimating()); - assertTrue(child12.isSelfOrChildAnimating()); - assertFalse(child2.isSelfOrChildAnimating()); - assertFalse(child21.isSelfOrChildAnimating()); - } - - @Test - public void testIsVisible() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true)); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true)); - final TestWindowContainer child21 = child2.addChildWindow(); - - assertFalse(root.isVisible()); - assertTrue(child1.isVisible()); - assertFalse(child11.isVisible()); - assertTrue(child12.isVisible()); - assertFalse(child2.isVisible()); - assertFalse(child21.isVisible()); - } - - @Test - public void testOverrideConfigurationAncestorNotification() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer grandparent = builder.setLayer(0).build(); - - final TestWindowContainer parent = grandparent.addChildWindow(); - final TestWindowContainer child = parent.addChildWindow(); - child.onOverrideConfigurationChanged(new Configuration()); - - assertTrue(grandparent.mOnDescendantOverrideCalled); - } - - @Test - public void testRemoveChild() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child21 = child2.addChildWindow(); - - assertTrue(root.hasChild(child2)); - assertTrue(root.hasChild(child21)); - root.removeChild(child2); - assertFalse(root.hasChild(child2)); - assertFalse(root.hasChild(child21)); - assertNull(child2.getParentWindow()); - - boolean gotException = false; - assertTrue(root.hasChild(child11)); - try { - // Can only detach our direct children. - root.removeChild(child11); - } catch (IllegalArgumentException e) { - gotException = true; - } - assertTrue(gotException); - } - - @Test - public void testGetOrientation_childSpecified() { - testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE, - SCREEN_ORIENTATION_LANDSCAPE); - testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET, - SCREEN_ORIENTATION_UNSPECIFIED); - } - - private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation, - int expectedOrientation) { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - root.setFillsParent(true); - - builder.setIsVisible(childVisible); - - if (childOrientation != SCREEN_ORIENTATION_UNSET) { - builder.setOrientation(childOrientation); - } - - final TestWindowContainer child1 = root.addChildWindow(builder); - child1.setFillsParent(true); - - assertEquals(expectedOrientation, root.getOrientation()); - } - - @Test - public void testGetOrientation_Unset() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); - // Unspecified well because we didn't specify anything... - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation()); - } - - @Test - public void testGetOrientation_InvisibleParentUnsetVisibleChildren() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); - - builder.setIsVisible(false).setLayer(-1); - final TestWindowContainer invisible = root.addChildWindow(builder); - builder.setIsVisible(true).setLayer(-2); - final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder); - invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); - // Landscape well because the container is visible and that is what we set on it above. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation()); - // Landscape because even though the container isn't visible it has a child that is - // specifying it can influence the orientation by being visible. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisible.getOrientation()); - // Landscape because the grandchild is visible and therefore can participate. - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); - - builder.setIsVisible(true).setLayer(-3); - final TestWindowContainer visibleUnset = root.addChildWindow(builder); - visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); - assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation()); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); - } - - @Test - public void testGetOrientation_setBehind() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); - - builder.setIsVisible(true).setLayer(-1); - final TestWindowContainer visibleUnset = root.addChildWindow(builder); - visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); - - builder.setIsVisible(true).setLayer(-2); - final TestWindowContainer visibleUnsetChild1VisibleSetBehind = - visibleUnset.addChildWindow(builder); - visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND); - // Setting to visible behind will be used by the parents if there isn't another other - // container behind this one that has an orientation set. - assertEquals(SCREEN_ORIENTATION_BEHIND, - visibleUnsetChild1VisibleSetBehind.getOrientation()); - assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation()); - assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); - } - - @Test - public void testGetOrientation_fillsParent() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); - - builder.setIsVisible(true).setLayer(-1); - final TestWindowContainer visibleUnset = root.addChildWindow(builder); - visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND); - - builder.setLayer(1).setIsVisible(true); - final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder); - visibleUnspecifiedRootChild.setFillsParent(false); - visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); - // Unset because the child doesn't fill the parent. May as well be invisible... - assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); - // The parent uses whatever orientation is set behind this container since it doesn't fill - // the parent. - assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); - - // Test case of child filling its parent, but its parent isn't filling its own parent. - builder.setLayer(2).setIsVisible(true); - final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent = - visibleUnspecifiedRootChild.addChildWindow(builder); - visibleUnspecifiedRootChildChildFillsParent.setOrientation( - SCREEN_ORIENTATION_PORTRAIT); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, - visibleUnspecifiedRootChildChildFillsParent.getOrientation()); - assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); - assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); - - - visibleUnspecifiedRootChild.setFillsParent(true); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation()); - assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation()); - } - - @Test - public void testCompareTo() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.setLayer(0).build(); - - final TestWindowContainer child1 = root.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - - final TestWindowContainer child2 = root.addChildWindow(); - final TestWindowContainer child21 = child2.addChildWindow(); - final TestWindowContainer child22 = child2.addChildWindow(); - final TestWindowContainer child222 = child22.addChildWindow(); - final TestWindowContainer child223 = child22.addChildWindow(); - final TestWindowContainer child2221 = child222.addChildWindow(); - final TestWindowContainer child2222 = child222.addChildWindow(); - final TestWindowContainer child2223 = child222.addChildWindow(); - - final TestWindowContainer root2 = builder.setLayer(0).build(); - - assertEquals(0, root.compareTo(root)); - assertEquals(-1, child1.compareTo(child2)); - assertEquals(1, child2.compareTo(child1)); - - boolean inTheSameTree = true; - try { - root.compareTo(root2); - } catch (IllegalArgumentException e) { - inTheSameTree = false; - } - assertFalse(inTheSameTree); - - assertEquals(-1, child1.compareTo(child11)); - assertEquals(1, child21.compareTo(root)); - assertEquals(1, child21.compareTo(child12)); - assertEquals(-1, child11.compareTo(child2)); - assertEquals(1, child2221.compareTo(child11)); - assertEquals(-1, child2222.compareTo(child223)); - assertEquals(1, child2223.compareTo(child21)); - } - - @Test - public void testPrefixOrderIndex() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.build(); - - final TestWindowContainer child1 = root.addChildWindow(); - - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - - final TestWindowContainer child2 = root.addChildWindow(); - - final TestWindowContainer child21 = child2.addChildWindow(); - final TestWindowContainer child22 = child2.addChildWindow(); - - final TestWindowContainer child221 = child22.addChildWindow(); - final TestWindowContainer child222 = child22.addChildWindow(); - final TestWindowContainer child223 = child22.addChildWindow(); - - final TestWindowContainer child23 = child2.addChildWindow(); - - assertEquals(0, root.getPrefixOrderIndex()); - assertEquals(1, child1.getPrefixOrderIndex()); - assertEquals(2, child11.getPrefixOrderIndex()); - assertEquals(3, child12.getPrefixOrderIndex()); - assertEquals(4, child2.getPrefixOrderIndex()); - assertEquals(5, child21.getPrefixOrderIndex()); - assertEquals(6, child22.getPrefixOrderIndex()); - assertEquals(7, child221.getPrefixOrderIndex()); - assertEquals(8, child222.getPrefixOrderIndex()); - assertEquals(9, child223.getPrefixOrderIndex()); - assertEquals(10, child23.getPrefixOrderIndex()); - } - - @Test - public void testPrefixOrder_addEntireSubtree() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.build(); - final TestWindowContainer subtree = builder.build(); - final TestWindowContainer subtree2 = builder.build(); - - final TestWindowContainer child1 = subtree.addChildWindow(); - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child2 = subtree2.addChildWindow(); - final TestWindowContainer child3 = subtree2.addChildWindow(); - subtree.addChild(subtree2, 1); - root.addChild(subtree, 0); - - assertEquals(0, root.getPrefixOrderIndex()); - assertEquals(1, subtree.getPrefixOrderIndex()); - assertEquals(2, child1.getPrefixOrderIndex()); - assertEquals(3, child11.getPrefixOrderIndex()); - assertEquals(4, subtree2.getPrefixOrderIndex()); - assertEquals(5, child2.getPrefixOrderIndex()); - assertEquals(6, child3.getPrefixOrderIndex()); - } - - @Test - public void testPrefixOrder_remove() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.build(); - - final TestWindowContainer child1 = root.addChildWindow(); - - final TestWindowContainer child11 = child1.addChildWindow(); - final TestWindowContainer child12 = child1.addChildWindow(); - - final TestWindowContainer child2 = root.addChildWindow(); - - assertEquals(0, root.getPrefixOrderIndex()); - assertEquals(1, child1.getPrefixOrderIndex()); - assertEquals(2, child11.getPrefixOrderIndex()); - assertEquals(3, child12.getPrefixOrderIndex()); - assertEquals(4, child2.getPrefixOrderIndex()); - - root.removeChild(child1); - - assertEquals(1, child2.getPrefixOrderIndex()); - } - - /** - * Ensure children of a {@link WindowContainer} do not have - * {@link WindowContainer#onParentResize()} called when {@link WindowContainer#onParentResize()} - * is invoked with overridden bounds. - */ - @Test - public void testOnParentResizePropagation() { - final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); - final TestWindowContainer root = builder.build(); - - final TestWindowContainer child = root.addChildWindow(); - child.setBounds(new Rect(1,1,2,2)); - - final TestWindowContainer grandChild = mock(TestWindowContainer.class); - - child.addChildWindow(grandChild); - root.onResize(); - - // Make sure the child does not propagate resize through onParentResize when bounds are set. - verify(grandChild, never()).onParentResize(); - - child.removeChild(grandChild); - - child.setBounds(null); - child.addChildWindow(grandChild); - root.onResize(); - - // Make sure the child propagates resize through onParentResize when no bounds set. - verify(grandChild, times(1)).onParentResize(); - } - - /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ - private static class TestWindowContainer extends WindowContainer { - private final int mLayer; - private boolean mIsAnimating; - private boolean mIsVisible; - private boolean mFillsParent; - private Integer mOrientation; - - private boolean mOnParentSetCalled; - private boolean mOnDescendantOverrideCalled; - - /** - * Compares 2 window layers and returns -1 if the first is lesser than the second in terms - * of z-order and 1 otherwise. - */ - private static final Comparator SUBLAYER_COMPARATOR = (w1, w2) -> { - final int layer1 = w1.mLayer; - final int layer2 = w2.mLayer; - if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0 )) { - // We insert the child window into the list ordered by the mLayer. For same layers, - // the negative one should go below others; the positive one should go above others. - return -1; - } - if (layer1 == layer2) return 0; - return 1; - }; - - TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating, - boolean isVisible, Integer orientation) { - super(wm); - - mLayer = layer; - mIsAnimating = isAnimating; - mIsVisible = isVisible; - mFillsParent = true; - mOrientation = orientation; - } - - TestWindowContainer getParentWindow() { - return (TestWindowContainer) getParent(); - } - - int getChildrenCount() { - return mChildren.size(); - } - - TestWindowContainer addChildWindow(TestWindowContainer child) { - addChild(child, SUBLAYER_COMPARATOR); - return child; - } - - TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) { - TestWindowContainer child = childBuilder.build(); - addChild(child, SUBLAYER_COMPARATOR); - return child; - } - - TestWindowContainer addChildWindow() { - return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1)); - } - - @Override - void onParentSet() { - mOnParentSetCalled = true; - } - - @Override - void onDescendantOverrideConfigurationChanged() { - mOnDescendantOverrideCalled = true; - super.onDescendantOverrideConfigurationChanged(); - } - - @Override - boolean isSelfAnimating() { - return mIsAnimating; - } - - @Override - boolean isVisible() { - return mIsVisible; - } - - @Override - int getOrientation(int candidate) { - return mOrientation != null ? mOrientation : super.getOrientation(candidate); - } - - @Override - int getOrientation() { - return getOrientation(super.mOrientation); - } - - @Override - boolean fillsParent() { - return mFillsParent; - } - - void setFillsParent(boolean fillsParent) { - mFillsParent = fillsParent; - } - } - - private static class TestWindowContainerBuilder { - private final WindowManagerService mWm; - private int mLayer; - private boolean mIsAnimating; - private boolean mIsVisible; - private Integer mOrientation; - - TestWindowContainerBuilder(WindowManagerService wm) { - mWm = wm; - mLayer = 0; - mIsAnimating = false; - mIsVisible = false; - mOrientation = null; - } - - TestWindowContainerBuilder setLayer(int layer) { - mLayer = layer; - return this; - } - - TestWindowContainerBuilder setIsAnimating(boolean isAnimating) { - mIsAnimating = isAnimating; - return this; - } - - TestWindowContainerBuilder setIsVisible(boolean isVisible) { - mIsVisible = isVisible; - return this; - } - - TestWindowContainerBuilder setOrientation(int orientation) { - mOrientation = orientation; - return this; - } - - TestWindowContainer build() { - return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation); - } - } - - private static class MockSurfaceBuildingContainer extends WindowContainer - implements AutoCloseable { - private final SurfaceSession mSession = new SurfaceSession(); - - MockSurfaceBuildingContainer(WindowManagerService wm) { - super(wm); - } - - static class MockSurfaceBuilder extends SurfaceControl.Builder { - MockSurfaceBuilder(SurfaceSession ss) { - super(ss); - } - - @Override - public SurfaceControl build() { - return mock(SurfaceControl.class); - } - } - - @Override - SurfaceControl.Builder makeChildSurface(WindowContainer child) { - return new MockSurfaceBuilder(mSession); - } - - @Override - public void close() { - mSession.kill(); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java deleted file mode 100644 index fcde08e18a6f..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTraversalTests.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -import java.util.function.Consumer; - -/** - * Tests for {@link WindowContainer#forAllWindows} and various implementations. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowContainerTraversalTests - */ -@SmallTest -@Presubmit -public class WindowContainerTraversalTests extends WindowTestsBase { - - @Test - public void testDockedDividerPosition() { - final WindowState splitScreenWindow = createWindowOnStack(null, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "splitScreenWindow"); - final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, - TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); - - mDisplayContent.mInputMethodTarget = splitScreenWindow; - - Consumer c = mock(Consumer.class); - mDisplayContent.forAllWindows(c, false); - - verify(c).accept(eq(mDockedDividerWindow)); - verify(c).accept(eq(mImeWindow)); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java deleted file mode 100644 index 227eb00be7f7..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; -import static android.view.DisplayCutout.fromBoundingRect; -import static android.view.WindowManager.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; - -import static org.junit.Assert.assertEquals; - -import android.app.ActivityManager.TaskDescription; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.view.DisplayInfo; -import android.view.Gravity; -import android.view.IWindow; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowFrameTests - */ -@SmallTest -@Presubmit -public class WindowFrameTests extends WindowTestsBase { - - private WindowToken mWindowToken; - private final IWindow mIWindow = new TestIWindow(); - private final Rect mEmptyRect = new Rect(); - - static class WindowStateWithTask extends WindowState { - final Task mTask; - boolean mDockedResizingForTest = false; - WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken, - WindowManager.LayoutParams attrs, Task t) { - super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - mTask = t; - } - - @Override - Task getTask() { - return mTask; - } - - @Override - boolean isDockedResizing() { - return mDockedResizingForTest; - } - } - - private static class TaskWithBounds extends Task { - final Rect mBounds; - final Rect mInsetBounds = new Rect(); - boolean mFullscreenForTest = true; - - TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) { - super(0, stack, 0, wm, 0, false, new TaskDescription(), null); - mBounds = bounds; - setBounds(bounds); - } - - @Override - public Rect getBounds() { - return mBounds; - } - - @Override - public void getBounds(Rect out) { - out.set(mBounds); - } - - @Override - public void getOverrideBounds(Rect outBounds) { - outBounds.set(mBounds); - } - @Override - void getTempInsetBounds(Rect outBounds) { - outBounds.set(mInsetBounds); - } - @Override - boolean isFullscreen() { - return mFullscreenForTest; - } - } - - TaskStack mStubStack; - - @Before - public void setUp() throws Exception { - mWindowToken = WindowTestUtils.createTestAppWindowToken( - mWm.getDefaultDisplayContentLocked()); - mStubStack = new TaskStack(mWm, 0, null); - } - - // Do not use this function directly in the tests below. Instead, use more explicit function - // such as assertFlame(). - private void assertRect(Rect rect, int left, int top, int right, int bottom) { - assertEquals(left, rect.left); - assertEquals(top, rect.top); - assertEquals(right, rect.right); - assertEquals(bottom, rect.bottom); - } - - private void assertContentInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getContentInsets(), left, top, right, bottom); - } - - private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getVisibleInsets(), left, top, right, bottom); - } - - private void assertStableInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getStableInsets(), left, top, right, bottom); - } - - private void assertFrame(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.getFrameLw(), left, top, right, bottom); - } - - private void assertContentFrame(WindowState w, Rect expectedRect) { - assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, - expectedRect.bottom); - } - - private void assertVisibleFrame(WindowState w, Rect expectedRect) { - assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, - expectedRect.bottom); - } - - private void assertStableFrame(WindowState w, Rect expectedRect) { - assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, - expectedRect.bottom); - } - - private void assertPolicyCrop(WindowStateWithTask w, int left, int top, int right, int bottom) { - Rect policyCrop = new Rect(); - w.calculatePolicyCrop(policyCrop); - assertRect(policyCrop, left, top, right, bottom); - } - - @Test - public void testLayoutInFullscreenTaskInsets() { - // fullscreen task doesn't use bounds for computeFrame - final Task task = new TaskWithBounds(mStubStack, mWm, null); - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final int bottomContentInset = 100; - final int topContentInset = 50; - final int bottomVisibleInset = 30; - final int topVisibleInset = 70; - final int leftStableInset = 20; - final int rightStableInset = 90; - - // With no insets or system decor all the frames incoming from PhoneWindowManager - // are identical. - final Rect pf = new Rect(0, 0, 1000, 1000); - final Rect df = pf; - final Rect of = df; - final Rect cf = new Rect(pf); - // Produce some insets - cf.top += 50; - cf.bottom -= 100; - final Rect vf = new Rect(pf); - vf.top += topVisibleInset; - vf.bottom -= bottomVisibleInset; - final Rect sf = new Rect(pf); - sf.left += leftStableInset; - sf.right -= rightStableInset; - - final Rect dcf = pf; - // When mFrame extends past cf, the content insets are - // the difference between mFrame and ContentFrame. Visible - // and stable frames work the same way. - w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); - w.computeFrameLw(); - assertFrame(w, 0, 0, 1000, 1000); - assertContentInset(w, 0, topContentInset, 0, bottomContentInset); - assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset); - assertStableInset(w, leftStableInset, 0, rightStableInset, 0); - assertContentFrame(w, cf); - assertVisibleFrame(w, vf); - assertStableFrame(w, sf); - // On the other hand getFrame() doesn't extend past cf we won't get any insets - w.mAttrs.x = 100; - w.mAttrs.y = 100; - w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT - w.mRequestedWidth = 100; - w.mRequestedHeight = 100; - w.computeFrameLw(); - assertFrame(w, 100, 100, 200, 200); - assertContentInset(w, 0, 0, 0, 0); - // In this case the frames are shrunk to the window frame. - assertContentFrame(w, w.getFrameLw()); - assertVisibleFrame(w, w.getFrameLw()); - assertStableFrame(w, w.getFrameLw()); - } - - @Test - public void testLayoutInFullscreenTaskNoInsets() { - // fullscreen task doesn't use bounds for computeFrame - final Task task = new TaskWithBounds(mStubStack, mWm, null); - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - // With no insets or system decor all the frames incoming from PhoneWindowManager - // are identical. - final Rect pf = new Rect(0, 0, 1000, 1000); - - // Here the window has FILL_PARENT, FILL_PARENT - // so we expect it to fill the entire available frame. - w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf); - w.computeFrameLw(); - assertFrame(w, 0, 0, 1000, 1000); - - // It can select various widths and heights within the bounds. - // Strangely the window attribute width is ignored for normal windows - // and we use mRequestedWidth/mRequestedHeight - w.mAttrs.width = 300; - w.mAttrs.height = 300; - w.computeFrameLw(); - // Explicit width and height without requested width/height - // gets us nothing. - assertFrame(w, 0, 0, 0, 0); - - w.mRequestedWidth = 300; - w.mRequestedHeight = 300; - w.computeFrameLw(); - // With requestedWidth/Height we can freely choose our size within the - // parent bounds. - assertFrame(w, 0, 0, 300, 300); - - // With FLAG_SCALED though, requestedWidth/height is used to control - // the unscaled surface size, and mAttrs.width/height becomes the - // layout controller. - w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED; - w.mRequestedHeight = -1; - w.mRequestedWidth = -1; - w.mAttrs.width = 100; - w.mAttrs.height = 100; - w.computeFrameLw(); - assertFrame(w, 0, 0, 100, 100); - w.mAttrs.flags = 0; - - // But sizes too large will be clipped to the containing frame - w.mRequestedWidth = 1200; - w.mRequestedHeight = 1200; - w.computeFrameLw(); - assertFrame(w, 0, 0, 1000, 1000); - - // Before they are clipped though windows will be shifted - w.mAttrs.x = 300; - w.mAttrs.y = 300; - w.mRequestedWidth = 1000; - w.mRequestedHeight = 1000; - w.computeFrameLw(); - assertFrame(w, 0, 0, 1000, 1000); - - // If there is room to move around in the parent frame the window will be shifted according - // to gravity. - w.mAttrs.x = 0; - w.mAttrs.y = 0; - w.mRequestedWidth = 300; - w.mRequestedHeight = 300; - w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; - w.computeFrameLw(); - assertFrame(w, 700, 0, 1000, 300); - w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; - w.computeFrameLw(); - assertFrame(w, 700, 700, 1000, 1000); - // Window specified x and y are interpreted as offsets in the opposite - // direction of gravity - w.mAttrs.x = 100; - w.mAttrs.y = 100; - w.computeFrameLw(); - assertFrame(w, 600, 600, 900, 900); - } - - @Test - public void testLayoutNonfullscreenTask() { - final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo(); - final int logicalWidth = displayInfo.logicalWidth; - final int logicalHeight = displayInfo.logicalHeight; - - final int taskLeft = logicalWidth / 4; - final int taskTop = logicalHeight / 4; - final int taskRight = logicalWidth / 4 * 3; - final int taskBottom = logicalHeight / 4 * 3; - final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); - final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds); - task.mFullscreenForTest = false; - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); - w.computeFrameLw(); - // For non fullscreen tasks the containing frame is based off the - // task bounds not the parent frame. - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - assertContentFrame(w, taskBounds); - assertContentInset(w, 0, 0, 0, 0); - - pf.set(0, 0, logicalWidth, logicalHeight); - // We still produce insets against the containing frame the same way. - final int cfRight = logicalWidth / 2; - final int cfBottom = logicalHeight / 2; - final Rect cf = new Rect(0, 0, cfRight, cfBottom); - windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(); - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - int contentInsetRight = taskRight - cfRight; - int contentInsetBottom = taskBottom - cfBottom; - assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); - assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, - taskBottom - contentInsetBottom)); - - pf.set(0, 0, logicalWidth, logicalHeight); - // However if we set temp inset bounds, the insets will be computed - // as if our window was laid out there, but it will be laid out according to - // the task bounds. - final int insetLeft = logicalWidth / 5; - final int insetTop = logicalHeight / 5; - final int insetRight = insetLeft + (taskRight - taskLeft); - final int insetBottom = insetTop + (taskBottom - taskTop); - task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom); - windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(); - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - contentInsetRight = insetRight - cfRight; - contentInsetBottom = insetBottom - cfBottom; - assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); - assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, - taskBottom - contentInsetBottom)); - } - - @Test - public void testCalculatePolicyCrop() { - final WindowStateWithTask w = createWindow( - new TaskWithBounds(mStubStack, mWm, null), MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); - final int logicalWidth = displayInfo.logicalWidth; - final int logicalHeight = displayInfo.logicalHeight; - final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); - final Rect df = pf; - final Rect of = df; - final Rect cf = new Rect(pf); - // Produce some insets - cf.top += displayInfo.logicalWidth / 10; - cf.bottom -= displayInfo.logicalWidth / 5; - final Rect vf = cf; - final Rect sf = vf; - // We use a decor content frame with insets to produce cropping. - Rect dcf = new Rect(cf); - - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); - w.computeFrameLw(); - assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom); - - windowFrames.mDecorFrame.setEmpty(); - // Likewise with no decor frame we would get no crop - w.computeFrameLw(); - assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight); - - // Now we set up a window which doesn't fill the entire decor frame. - // Normally it would be cropped to it's frame but in the case of docked resizing - // we need to account for the fact the windows surface will be made - // fullscreen and thus also make the crop fullscreen. - - windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - w.mAttrs.width = logicalWidth / 2; - w.mAttrs.height = logicalHeight / 2; - w.mRequestedWidth = logicalWidth / 2; - w.mRequestedHeight = logicalHeight / 2; - w.computeFrameLw(); - - // Normally the crop is shrunk from the decor frame - // to the computed window frame. - assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2); - - w.mDockedResizingForTest = true; - // But if we are docked resizing it won't be, however we will still be - // shrunk to the decor frame and the display. - assertPolicyCrop(w, 0, 0, - Math.min(pf.width(), displayInfo.logicalWidth), - Math.min(pf.height(), displayInfo.logicalHeight)); - } - - @Test - public void testLayoutLetterboxedWindow() { - // First verify task behavior in multi-window mode. - final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo(); - final int logicalWidth = displayInfo.logicalWidth; - final int logicalHeight = displayInfo.logicalHeight; - - final int taskLeft = logicalWidth / 5; - final int taskTop = logicalHeight / 5; - final int taskRight = logicalWidth / 4 * 3; - final int taskBottom = logicalHeight / 4 * 3; - final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); - final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds); - task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom); - task.mFullscreenForTest = false; - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); - w.computeFrameLw(); - // For non fullscreen tasks the containing frame is based off the - // task bounds not the parent frame. - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - assertContentFrame(w, taskBounds); - assertContentInset(w, 0, 0, 0, 0); - - // Now simulate switch to fullscreen for letterboxed app. - final int xInset = logicalWidth / 10; - final int yInset = logicalWidth / 10; - final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset); - Configuration config = new Configuration(w.mAppToken.getOverrideConfiguration()); - config.windowConfiguration.setBounds(cf); - w.mAppToken.onOverrideConfigurationChanged(config); - pf.set(0, 0, logicalWidth, logicalHeight); - task.mFullscreenForTest = true; - windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); - w.computeFrameLw(); - assertFrame(w, cf.left, cf.top, cf.right, cf.bottom); - assertContentFrame(w, cf); - assertContentInset(w, 0, 0, 0, 0); - } - - @Test - public void testDisplayCutout() { - // Regular fullscreen task and window - final Task task = new TaskWithBounds(mStubStack, mWm, null); - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final Rect pf = new Rect(0, 0, 1000, 2000); - // Create a display cutout of size 50x50, aligned top-center - final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP), - pf.width(), pf.height()); - - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); - windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(); - - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0); - } - - @Test - public void testDisplayCutout_tempInsetBounds() { - // Regular fullscreen task and window - final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, - new Rect(0, -500, 1000, 1500)); - task.mFullscreenForTest = false; - task.mInsetBounds.set(0, 0, 1000, 2000); - WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); - w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - - final Rect pf = new Rect(0, -500, 1000, 1500); - // Create a display cutout of size 50x50, aligned top-center - final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( - fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP), - pf.width(), pf.height()); - - final WindowFrames windowFrames = w.getWindowFrames(); - windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); - windowFrames.setDisplayCutout(cutout); - w.computeFrameLw(); - - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0); - assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0); - } - - private WindowStateWithTask createWindow(Task task, int width, int height) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); - attrs.width = width; - attrs.height = height; - - return new WindowStateWithTask(mWm, mIWindow, mWindowToken, attrs, task); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java deleted file mode 100644 index 9a13efb4c58b..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRule.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; -import static android.view.Display.DEFAULT_DISPLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; - -import android.app.ActivityManagerInternal; -import android.content.Context; -import android.hardware.display.DisplayManagerInternal; -import android.os.PowerManagerInternal; -import android.os.PowerSaveState; -import android.view.Display; -import android.view.InputChannel; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; - -import com.android.server.LocalServices; -import com.android.server.input.InputManagerService; -import com.android.server.policy.WindowManagerPolicy; - -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.mockito.invocation.InvocationOnMock; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - -/** - * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure - * to properly tear it down after. - * - *

- * Usage: - *

- * {@literal @}Rule
- *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * 
- */ -public class WindowManagerServiceRule implements TestRule { - - private WindowManagerService mService; - private TestWindowManagerPolicy mPolicy; - // Record all {@link SurfaceControl.Transaction} created while testing and releases native - // resources when test finishes. - private final List> mSurfaceTransactions = new ArrayList<>(); - // Record all {@link SurfaceControl} created while testing and releases native resources when - // test finishes. - private final List> mSurfaceControls = new ArrayList<>(); - - @Override - public Statement apply(Statement base, Description description) { - return new Statement() { - @Override - public void evaluate() throws Throwable { - runWithDexmakerShareClassLoader(this::setUp); - try { - base.evaluate(); - } finally { - tearDown(); - } - } - - private void setUp() { - final Context context = getInstrumentation().getTargetContext(); - - removeServices(); - - LocalServices.addService(DisplayManagerInternal.class, - mock(DisplayManagerInternal.class)); - - LocalServices.addService(PowerManagerInternal.class, - mock(PowerManagerInternal.class)); - final PowerManagerInternal pm = - LocalServices.getService(PowerManagerInternal.class); - PowerSaveState state = new PowerSaveState.Builder().build(); - doReturn(state).when(pm).getLowPowerState(anyInt()); - - LocalServices.addService(ActivityManagerInternal.class, - mock(ActivityManagerInternal.class)); - LocalServices.addService(ActivityTaskManagerInternal.class, - mock(ActivityTaskManagerInternal.class)); - final ActivityTaskManagerInternal atm = - LocalServices.getService(ActivityTaskManagerInternal.class); - doAnswer((InvocationOnMock invocationOnMock) -> { - final Runnable runnable = invocationOnMock.getArgument(0); - if (runnable != null) { - runnable.run(); - } - return null; - }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt()); - - InputManagerService ims = mock(InputManagerService.class); - // InputChannel is final and can't be mocked. - InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); - if (input != null && input.length > 1) { - doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); - } - - mService = WindowManagerService.main(context, ims, false, false, - mPolicy = new TestWindowManagerPolicy( - WindowManagerServiceRule.this::getWindowManagerService), - new WindowManagerGlobalLock()); - mService.mTransactionFactory = () -> { - final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - mSurfaceTransactions.add(new WeakReference<>(transaction)); - return transaction; - }; - mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) { - @Override - public SurfaceControl build() { - final SurfaceControl control = super.build(); - mSurfaceControls.add(new WeakReference<>(control)); - return control; - } - }; - - mService.onInitReady(); - - final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); - final DisplayWindowController dcw = new DisplayWindowController(display, mService); - // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor. - // We emulate those steps here. - mService.mRoot.createDisplayContent(display, dcw); - } - - private void removeServices() { - LocalServices.removeServiceForTest(DisplayManagerInternal.class); - LocalServices.removeServiceForTest(PowerManagerInternal.class); - LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerInternal.class); - LocalServices.removeServiceForTest(WindowManagerPolicy.class); - } - - private void tearDown() { - waitUntilWindowManagerHandlersIdle(); - destroyAllSurfaceTransactions(); - destroyAllSurfaceControls(); - removeServices(); - mService = null; - mPolicy = null; - } - }; - } - - public WindowManagerService getWindowManagerService() { - return mService; - } - - public TestWindowManagerPolicy getWindowManagerPolicy() { - return mPolicy; - } - - public void waitUntilWindowManagerHandlersIdle() { - final WindowManagerService wm = getWindowManagerService(); - if (wm != null) { - wm.mH.runWithScissors(() -> { }, 0); - wm.mAnimationHandler.runWithScissors(() -> { }, 0); - SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); - } - } - - private void destroyAllSurfaceTransactions() { - for (final WeakReference reference : mSurfaceTransactions) { - final Transaction transaction = reference.get(); - if (transaction != null) { - reference.clear(); - transaction.close(); - } - } - } - - private void destroyAllSurfaceControls() { - for (final WeakReference reference : mSurfaceControls) { - final SurfaceControl control = reference.get(); - if (control != null) { - reference.clear(); - control.destroy(); - } - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java deleted file mode 100644 index 343d35959df4..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowManagerServiceRuleTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 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 com.android.server.wm; - -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.junit.Assert.assertThat; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Rule; -import org.junit.Test; - -/** - * Build/InstallRun: - * atest FrameworksServicesTests:WindowManagerServiceRuleTest - */ -@Presubmit -@SmallTest -public class WindowManagerServiceRuleTest { - - @Rule - public final WindowManagerServiceRule mRule = new WindowManagerServiceRule(); - - @Test - public void testWindowManagerSetUp() { - assertThat(mRule.getWindowManagerService(), notNullValue()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java deleted file mode 100644 index 118ce8962259..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90; -import static android.view.Surface.ROTATION_0; -import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -import android.graphics.Insets; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.platform.test.annotations.Presubmit; -import android.util.Size; -import android.view.DisplayCutout; -import android.view.SurfaceControl; -import android.view.WindowManager; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.utils.WmDisplayCutout; - -import org.junit.Test; - -import java.util.LinkedList; - -/** - * Tests for the {@link WindowState} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowStateTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class WindowStateTests extends WindowTestsBase { - - @Test - public void testIsParentWindowHidden() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); - - // parentWindow is initially set to hidden. - assertTrue(parentWindow.mHidden); - assertFalse(parentWindow.isParentWindowHidden()); - assertTrue(child1.isParentWindowHidden()); - assertTrue(child2.isParentWindowHidden()); - - parentWindow.mHidden = false; - assertFalse(parentWindow.isParentWindowHidden()); - assertFalse(child1.isParentWindowHidden()); - assertFalse(child2.isParentWindowHidden()); - } - - @Test - public void testIsChildWindow() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); - - assertFalse(parentWindow.isChildWindow()); - assertTrue(child1.isChildWindow()); - assertTrue(child2.isChildWindow()); - assertFalse(randomWindow.isChildWindow()); - } - - @Test - public void testHasChild() { - final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1"); - final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11"); - final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12"); - final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2"); - final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21"); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); - - assertTrue(win1.hasChild(win11)); - assertTrue(win1.hasChild(win12)); - assertTrue(win2.hasChild(win21)); - - assertFalse(win1.hasChild(win21)); - assertFalse(win1.hasChild(randomWindow)); - - assertFalse(win2.hasChild(win11)); - assertFalse(win2.hasChild(win12)); - assertFalse(win2.hasChild(randomWindow)); - } - - @Test - public void testGetParentWindow() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); - - assertNull(parentWindow.getParentWindow()); - assertEquals(parentWindow, child1.getParentWindow()); - assertEquals(parentWindow, child2.getParentWindow()); - } - - @Test - public void testOverlayWindowHiddenWhenSuspended() { - final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY, - "overlayWindow")); - overlayWindow.setHiddenWhileSuspended(true); - verify(overlayWindow).hideLw(true, true); - overlayWindow.setHiddenWhileSuspended(false); - verify(overlayWindow).showLw(true, true); - } - - @Test - public void testGetTopParentWindow() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); - - assertEquals(root, root.getTopParentWindow()); - assertEquals(root, child1.getTopParentWindow()); - assertEquals(child1, child2.getParentWindow()); - assertEquals(root, child2.getTopParentWindow()); - - // Test case were child is detached from parent. - root.removeChild(child1); - assertEquals(child1, child1.getTopParentWindow()); - assertEquals(child1, child2.getParentWindow()); - } - - @Test - public void testIsOnScreen_hiddenByPolicy() { - final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); - window.setHasSurface(true); - assertTrue(window.isOnScreen()); - window.hideLw(false /* doAnimation */); - assertFalse(window.isOnScreen()); - } - - @Test - public void testCanBeImeTarget() { - final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); - final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow"); - - // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being - // an IME target. - appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; - imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; - - // Make windows visible - appWindow.setHasSurface(true); - imeWindow.setHasSurface(true); - - // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets - assertFalse(appWindow.canBeImeTarget()); - assertFalse(imeWindow.canBeImeTarget()); - - // Add IME target flags - appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); - imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); - - // Visible app window with flags can be IME target while an IME window can never be an IME - // target regardless of its visibility or flags. - assertTrue(appWindow.canBeImeTarget()); - assertFalse(imeWindow.canBeImeTarget()); - - // Make windows invisible - appWindow.hideLw(false /* doAnimation */); - imeWindow.hideLw(false /* doAnimation */); - - // Invisible window can't be IME targets even if they have the right flags. - assertFalse(appWindow.canBeImeTarget()); - assertFalse(imeWindow.canBeImeTarget()); - } - - @Test - public void testGetWindow() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild"); - final WindowState mediaOverlayChild = createWindow(root, - TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild"); - final WindowState attachedDialogChild = createWindow(root, - TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild"); - final WindowState subPanelChild = createWindow(root, - TYPE_APPLICATION_SUB_PANEL, "subPanelChild"); - final WindowState aboveSubPanelChild = createWindow(root, - TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild"); - - final LinkedList windows = new LinkedList<>(); - - root.getWindow(w -> { - windows.addLast(w); - return false; - }); - - // getWindow should have returned candidate windows in z-order. - assertEquals(aboveSubPanelChild, windows.pollFirst()); - assertEquals(subPanelChild, windows.pollFirst()); - assertEquals(attachedDialogChild, windows.pollFirst()); - assertEquals(root, windows.pollFirst()); - assertEquals(mediaOverlayChild, windows.pollFirst()); - assertEquals(mediaChild, windows.pollFirst()); - assertTrue(windows.isEmpty()); - } - - @Test - public void testPrepareWindowToDisplayDuringRelayout() { - testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/); - - // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON - // before calling prepareWindowToDisplayDuringRelayout for windows with flag in the same - // appWindowToken. - final AppWindowToken appWindowToken = createAppWindowToken(mDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final WindowState first = createWindow(null, TYPE_APPLICATION, appWindowToken, "first"); - final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second"); - second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - - reset(sPowerManagerWrapper); - first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); - assertTrue(appWindowToken.canTurnScreenOn()); - - reset(sPowerManagerWrapper); - second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); - - // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON - // from the same appWindowToken. Only one should trigger the wakeup. - appWindowToken.setCanTurnScreenOn(true); - first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - - reset(sPowerManagerWrapper); - first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); - - reset(sPowerManagerWrapper); - second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); - - // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an - // appWindowToken. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup - final WindowToken windowToken = WindowTestUtils.createTestWindowToken(FIRST_SUB_WINDOW, - mDisplayContent); - final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken, - "firstWindow"); - final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken, - "secondWindow"); - firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - - reset(sPowerManagerWrapper); - firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); - - reset(sPowerManagerWrapper); - secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); - } - - @Test - public void testCanAffectSystemUiFlags() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mToken.setHidden(false); - assertTrue(app.canAffectSystemUiFlags()); - app.mToken.setHidden(true); - assertFalse(app.canAffectSystemUiFlags()); - app.mToken.setHidden(false); - app.mAttrs.alpha = 0.0f; - assertFalse(app.canAffectSystemUiFlags()); - } - - @Test - public void testCanAffectSystemUiFlags_disallow() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mToken.setHidden(false); - assertTrue(app.canAffectSystemUiFlags()); - app.getTask().setCanAffectSystemUiFlags(false); - assertFalse(app.canAffectSystemUiFlags()); - } - - @Test - public void testIsSelfOrAncestorWindowAnimating() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); - assertFalse(child2.isSelfOrAncestorWindowAnimatingExit()); - child2.mAnimatingExit = true; - assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); - child2.mAnimatingExit = false; - root.mAnimatingExit = true; - assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); - } - - @Test - public void testLayoutSeqResetOnReparent() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - app.mLayoutSeq = 1; - mDisplayContent.mLayoutSeq = 1; - - app.onDisplayChanged(mDisplayContent); - - assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq))); - } - - @Test - public void testDisplayIdUpdatedOnReparent() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - // fake a different display - app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1; - app.onDisplayChanged(mDisplayContent); - - assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId())); - assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId())); - } - - @Test - public void testSeamlesslyRotateWindow() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); - - app.mHasSurface = true; - app.mSurfaceControl = mock(SurfaceControl.class); - try { - app.getFrameLw().set(10, 20, 60, 80); - app.updateSurfacePosition(t); - - app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true); - - assertTrue(app.mSeamlesslyRotated); - - // Verify we un-rotate the window state surface. - Matrix matrix = new Matrix(); - // Un-rotate 90 deg - matrix.setRotate(270); - // Translate it back to origin - matrix.postTranslate(0, mDisplayInfo.logicalWidth); - verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class)); - - // Verify we update the position as well. - float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y}; - matrix.mapPoints(currentSurfacePos); - verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]), - eq(currentSurfacePos[1])); - } finally { - app.mSurfaceControl = null; - app.mHasSurface = false; - } - } - - @Test - public void testDisplayCutoutIsCalculatedRelativeToFrame() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - WindowFrames wf = app.getWindowFrames(); - wf.mParentFrame.set(7, 10, 185, 380); - wf.mDisplayFrame.set(wf.mParentFrame); - final DisplayCutout cutout = new DisplayCutout( - Insets.of(0, 15, 0, 22) /* safeInset */, - null /* boundLeft */, - new Rect(95, 0, 105, 15), - null /* boundRight */, - new Rect(95, 378, 105, 400)); - wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400))); - - app.computeFrameLw(); - assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); - } - - private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { - reset(sPowerManagerWrapper); - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - - root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java deleted file mode 100644 index 2e47c353d8c2..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2017 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 com.android.server.wm; - -import static android.app.AppOpsManager.OP_NONE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; - -import static com.android.server.wm.WindowContainer.POSITION_TOP; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyFloat; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; - -import android.app.ActivityManager; -import android.content.ComponentName; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.Binder; -import android.os.IBinder; -import android.view.IApplicationToken; -import android.view.IWindow; -import android.view.SurfaceControl.Transaction; -import android.view.WindowManager; - -import org.mockito.invocation.InvocationOnMock; - -/** - * A collection of static functions that can be referenced by other test packages to provide access - * to WindowManager related test functionality. - */ -public class WindowTestUtils { - private static int sNextTaskId = 0; - - /** - * Creates a mock instance of {@link StackWindowController}. - */ - public static StackWindowController createMockStackWindowContainerController() { - StackWindowController controller = mock(StackWindowController.class); - controller.mContainer = mock(TestTaskStack.class); - - // many components rely on the {@link StackWindowController#adjustConfigurationForBounds} - // to properly set bounds values in the configuration. We must mimick those actions here. - doAnswer((InvocationOnMock invocationOnMock) -> { - final Configuration config = invocationOnMock.getArgument(7); - final Rect bounds = invocationOnMock.getArgument(0); - config.windowConfiguration.setBounds(bounds); - return null; - }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(), - anyBoolean(), anyBoolean(), anyFloat(), any(), any(), anyInt()); - - return controller; - } - - /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - public static Task createTaskInStack(WindowManagerService service, TaskStack stack, - int userId) { - synchronized (service.mGlobalLock) { - final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, - new ActivityManager.TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; - } - } - - /** - * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not - * normally be mocked out. - */ - public static class TestTaskStack extends TaskStack { - TestTaskStack(WindowManagerService service, int stackId) { - super(service, stackId, null); - } - - @Override - void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { - // Do nothing. - } - } - - static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { - synchronized (dc.mService.mGlobalLock) { - return new TestAppWindowToken(dc); - } - } - - /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - public static class TestAppWindowToken extends AppWindowToken { - boolean mOnTop = false; - private Transaction mPendingTransactionOverride; - - private TestAppWindowToken(DisplayContent dc) { - super(dc.mService, new IApplicationToken.Stub() { - public String getName() {return null;} - }, new ComponentName("", ""), false, dc, true /* fillsParent */); - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, - int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) { - super(service, token, activityComponent, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, rotationAnimationHint, configChanges, launchTaskBehind, - alwaysFocusable, activityRecord); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - - WindowState getFirstChild() { - return mChildren.peekFirst(); - } - - WindowState getLastChild() { - return mChildren.peekLast(); - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - void setIsOnTop(boolean onTop) { - mOnTop = onTop; - } - - @Override - boolean isOnTop() { - return mOnTop; - } - - void setPendingTransaction(Transaction transaction) { - mPendingTransactionOverride = transaction; - } - - @Override - public Transaction getPendingTransaction() { - return mPendingTransactionOverride == null - ? super.getPendingTransaction() - : mPendingTransactionOverride; - } - } - - static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { - return createTestWindowToken(type, dc, false /* persistOnEmpty */); - } - - static TestWindowToken createTestWindowToken(int type, DisplayContent dc, - boolean persistOnEmpty) { - synchronized (dc.mService.mGlobalLock) { - return new TestWindowToken(type, dc, persistOnEmpty); - } - } - - /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - public static class TestWindowToken extends WindowToken { - - private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { - super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc, - false /* ownerCanManageAppTokens */); - } - - int getWindowsCount() { - return mChildren.size(); - } - - boolean hasWindow(WindowState w) { - return mChildren.contains(w); - } - } - - /* Used so we can gain access to some protected members of the {@link Task} class */ - public static class TestTask extends Task { - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, - int resizeMode, boolean supportsPictureInPicture, - TaskWindowContainerController controller) { - super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, - new ActivityManager.TaskDescription(), controller); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isSelfAnimating() { - return mIsAnimating; - } - - void setLocalIsAnimating(boolean isAnimating) { - mIsAnimating = isAnimating; - } - } - - /** - * Used so we can gain access to some protected members of {@link TaskWindowContainerController} - * class. - */ - public static class TestTaskWindowContainerController extends TaskWindowContainerController { - - static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() { - @Override - public void registerConfigurationChangeListener( - ConfigurationContainerListener listener) { - } - - @Override - public void unregisterConfigurationChangeListener( - ConfigurationContainerListener listener) { - } - - @Override - public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { - } - - @Override - public void requestResize(Rect bounds, int resizeMode) { - } - }; - - TestTaskWindowContainerController(WindowTestsBase testsBase) { - this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent)); - } - - TestTaskWindowContainerController(StackWindowController stackController) { - super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */, - RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/, - true /* showForAllUsers */, new ActivityManager.TaskDescription(), - stackController.mService); - } - - @Override - TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode, - boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, resizeMode, - supportsPictureInPicture, this); - } - } - - public static class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - @Override - public String getName() { - return null; - } - } - - /** Used to track resize reports. */ - public static class TestWindowState extends WindowState { - boolean resizeReported; - - TestWindowState(WindowManagerService service, Session session, IWindow window, - WindowManager.LayoutParams attrs, WindowToken token) { - super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - void reportResized() { - super.reportResized(); - resizeReported = true; - } - - @Override - public boolean isGoneForLayoutLw() { - return false; - } - - @Override - void updateResizingWindowIfNeeded() { - // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive - // the system that it can actually update the window. - boolean hadSurface = mHasSurface; - mHasSurface = true; - - super.updateResizingWindowIfNeeded(); - - mHasSurface = hadSurface; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java deleted file mode 100644 index 1eb46fb05b5e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.AppOpsManager.OP_NONE; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; -import static android.view.View.VISIBLE; -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static org.mockito.Mockito.mock; - -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.hardware.display.DisplayManagerGlobal; -import android.testing.DexmakerShareClassLoaderRule; -import android.util.Log; -import android.view.Display; -import android.view.DisplayInfo; -import android.view.IWindow; -import android.view.WindowManager; - -import com.android.server.AttributeCache; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; - -import java.util.HashSet; -import java.util.LinkedList; - -/** - * Common base class for window manager unit test classes. - * - * Make sure any requests to WM hold the WM lock if needed b/73966377 - */ -class WindowTestsBase { - private static final String TAG = WindowTestsBase.class.getSimpleName(); - - WindowManagerService mWm; - private final IWindow mIWindow = new TestIWindow(); - private Session mMockSession; - // The default display is removed in {@link #setUp} and then we iterate over all displays to - // make sure we don't collide with any existing display. If we run into no other display, the - // added display should be treated as default. This cannot be the default display - private static int sNextDisplayId = DEFAULT_DISPLAY + 1; - static int sNextStackId = 1000; - - /** Non-default display. */ - DisplayContent mDisplayContent; - DisplayInfo mDisplayInfo = new DisplayInfo(); - WindowState mWallpaperWindow; - WindowState mImeWindow; - WindowState mImeDialogWindow; - WindowState mStatusBarWindow; - WindowState mDockedDividerWindow; - WindowState mNavBarWindow; - WindowState mAppWindow; - WindowState mChildAppWindowAbove; - WindowState mChildAppWindowBelow; - HashSet mCommonWindows; - - @Rule - public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = - new DexmakerShareClassLoaderRule(); - - @Rule - public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule(); - - static WindowState.PowerManagerWrapper sPowerManagerWrapper; // TODO(roosa): make non-static. - - @BeforeClass - public static void setUpOnceBase() { - AttributeCache.init(getInstrumentation().getTargetContext()); - sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); - } - - @Before - public void setUpBase() { - // If @Before throws an exception, the error isn't logged. This will make sure any failures - // in the set up are clear. This can be removed when b/37850063 is fixed. - try { - mMockSession = mock(Session.class); - - final Context context = getInstrumentation().getTargetContext(); - - mWm = mWmRule.getWindowManagerService(); - beforeCreateDisplay(); - - context.getDisplay().getDisplayInfo(mDisplayInfo); - mDisplayContent = createNewDisplay(); - mWm.mDisplayEnabled = true; - mWm.mDisplayReady = true; - - // Set-up some common windows. - mCommonWindows = new HashSet<>(); - synchronized (mWm.mGlobalLock) { - mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow"); - mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow"); - mDisplayContent.mInputMethodWindow = mImeWindow; - mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, - "mImeDialogWindow"); - mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow"); - mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow"); - mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, - "mDockedDividerWindow"); - mAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "mAppWindow"); - mChildAppWindowAbove = createCommonWindow(mAppWindow, - TYPE_APPLICATION_ATTACHED_DIALOG, - "mChildAppWindowAbove"); - mChildAppWindowBelow = createCommonWindow(mAppWindow, - TYPE_APPLICATION_MEDIA_OVERLAY, - "mChildAppWindowBelow"); - } - // Adding a display will cause freezing the display. Make sure to wait until it's - // unfrozen to not run into race conditions with the tests. - waitUntilHandlersIdle(); - } catch (Exception e) { - Log.e(TAG, "Failed to set up test", e); - throw e; - } - } - - void beforeCreateDisplay() { - // Called before display is created. - } - - @After - public void tearDownBase() { - // If @After throws an exception, the error isn't logged. This will make sure any failures - // in the tear down are clear. This can be removed when b/37850063 is fixed. - try { - // Test may schedule to perform surface placement or other messages. Wait until a - // stable state to clean up for consistency. - waitUntilHandlersIdle(); - - final LinkedList nonCommonWindows = new LinkedList<>(); - - synchronized (mWm.mGlobalLock) { - mWm.mRoot.forAllWindows(w -> { - if (!mCommonWindows.contains(w)) { - nonCommonWindows.addLast(w); - } - }, true /* traverseTopToBottom */); - - while (!nonCommonWindows.isEmpty()) { - nonCommonWindows.pollLast().removeImmediately(); - } - - for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) { - final DisplayContent displayContent = mWm.mRoot.mChildren.get(i); - if (!displayContent.isDefaultDisplay) { - displayContent.removeImmediately(); - } - } - // Remove app transition & window freeze timeout callbacks to prevent unnecessary - // actions after test. - mWm.getDefaultDisplayContentLocked().mAppTransition - .removeAppTransitionTimeoutCallbacks(); - mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT); - mDisplayContent.mInputMethodTarget = null; - } - - // Wait until everything is really cleaned up. - waitUntilHandlersIdle(); - } catch (Exception e) { - Log.e(TAG, "Failed to tear down test", e); - throw e; - } - } - - private WindowState createCommonWindow(WindowState parent, int type, String name) { - synchronized (mWm.mGlobalLock) { - final WindowState win = createWindow(parent, type, name); - mCommonWindows.add(win); - // Prevent common windows from been IMe targets - win.mAttrs.flags |= FLAG_NOT_FOCUSABLE; - return win; - } - } - - /** - * Waits until the main handler for WM has processed all messages. - */ - void waitUntilHandlersIdle() { - mWmRule.waitUntilWindowManagerHandlersIdle(); - } - - private WindowToken createWindowToken( - DisplayContent dc, int windowingMode, int activityType, int type) { - synchronized (mWm.mGlobalLock) { - if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { - return WindowTestUtils.createTestWindowToken(type, dc); - } - - return createAppWindowToken(dc, windowingMode, activityType); - } - } - - AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) { - return createTestAppWindowToken(dc, windowingMode, activityType); - } - - WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int - windowingMode, int activityType) { - final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType, - dc).mContainer; - final Task task = createTaskInStack(stack, 0 /* userId */); - final WindowTestUtils.TestAppWindowToken appWindowToken = - WindowTestUtils.createTestAppWindowToken(dc); - task.addChild(appWindowToken, 0); - return appWindowToken; - } - - WindowState createWindow(WindowState parent, int type, String name) { - synchronized (mWm.mGlobalLock) { - return (parent == null) - ? createWindow(parent, type, mDisplayContent, name) - : createWindow(parent, type, parent.mToken, name); - } - } - - WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType, - int type, DisplayContent dc, String name) { - synchronized (mWm.mGlobalLock) { - final WindowToken token = createWindowToken(dc, windowingMode, activityType, type); - return createWindow(parent, type, token, name); - } - } - - WindowState createAppWindow(Task task, int type, String name) { - synchronized (mWm.mGlobalLock) { - final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); - task.addChild(token, 0); - return createWindow(null, type, token, name); - } - } - - WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { - synchronized (mWm.mGlobalLock) { - final WindowToken token = createWindowToken( - dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); - return createWindow(parent, type, token, name); - } - } - - WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, - boolean ownerCanAddInternalSystemWindow) { - synchronized (mWm.mGlobalLock) { - final WindowToken token = createWindowToken( - dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); - return createWindow(parent, type, token, name, 0 /* ownerId */, - ownerCanAddInternalSystemWindow); - } - } - - WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { - synchronized (mWm.mGlobalLock) { - return createWindow(parent, type, token, name, 0 /* ownerId */, - false /* ownerCanAddInternalSystemWindow */); - } - } - - WindowState createWindow(WindowState parent, int type, WindowToken token, String name, - int ownerId, boolean ownerCanAddInternalSystemWindow) { - return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow, - mWm, mMockSession, mIWindow); - } - - static WindowState createWindow(WindowState parent, int type, WindowToken token, - String name, int ownerId, boolean ownerCanAddInternalSystemWindow, - WindowManagerService service, Session session, IWindow iWindow) { - synchronized (service.mGlobalLock) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); - attrs.setTitle(name); - - final WindowState w = new WindowState(service, session, iWindow, token, parent, - OP_NONE, - 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow, - sPowerManagerWrapper); - // TODO: Probably better to make this call in the WindowState ctor to avoid errors with - // adding it to the token... - token.addWindow(w); - return w; - } - } - - /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */ - TaskStack createTaskStackOnDisplay(DisplayContent dc) { - synchronized (mWm.mGlobalLock) { - return createStackControllerOnDisplay(dc).mContainer; - } - } - - StackWindowController createStackControllerOnDisplay(DisplayContent dc) { - synchronized (mWm.mGlobalLock) { - return createStackControllerOnStackOnDisplay( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); - } - } - - StackWindowController createStackControllerOnStackOnDisplay( - int windowingMode, int activityType, DisplayContent dc) { - synchronized (mWm.mGlobalLock) { - final Configuration overrideConfig = new Configuration(); - overrideConfig.windowConfiguration.setWindowingMode(windowingMode); - overrideConfig.windowConfiguration.setActivityType(activityType); - final int stackId = ++sNextStackId; - final StackWindowController controller = new StackWindowController(stackId, null, - dc.getDisplayId(), true /* onTop */, new Rect(), mWm); - controller.onOverrideConfigurationChanged(overrideConfig); - return controller; - } - } - - /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - Task createTaskInStack(TaskStack stack, int userId) { - return WindowTestUtils.createTaskInStack(mWm, stack, userId); - } - - /** Creates a {@link DisplayContent} and adds it to the system. */ - DisplayContent createNewDisplay() { - return createNewDisplay(mDisplayInfo); - } - - /** Creates a {@link DisplayContent} and adds it to the system. */ - DisplayContent createNewDisplay(DisplayInfo displayInfo) { - final int displayId = sNextDisplayId++; - final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, - displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); - synchronized (mWm.mGlobalLock) { - return new DisplayContent(display, mWm, mock(DisplayWindowController.class)); - } - } - - /** - * Creates a {@link DisplayContent} with given display state and adds it to the system. - * - * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to - * initialize {@link DisplayContent}, this method used real controller object when the test - * need to verify its related flows. - * - * @param displayState For initializing the state of the display. See - * {@link Display#getState()}. - */ - DisplayContent createNewDisplayWithController(int displayState) { - // Leverage main display info & initialize it with display state for given displayId. - DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.copyFrom(mDisplayInfo); - displayInfo.state = displayState; - final int displayId = sNextDisplayId++; - final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, - displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); - final DisplayWindowController dcw = new DisplayWindowController(display, mWm); - synchronized (mWm.mGlobalLock) { - // Display creation is driven by DisplayWindowController via ActivityStackSupervisor. - // We skip those steps here. - return mWm.mRoot.createDisplayContent(display, dcw); - } - } - - /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ - WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, - WindowToken token) { - synchronized (mWm.mGlobalLock) { - return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token); - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java deleted file mode 100644 index 3048f1a3487b..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTokenTests.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_TOAST; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -/** - * Tests for the {@link WindowToken} class. - * - * Build/Install/Run: - * atest FrameworksServicesTests:WindowTokenTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class WindowTokenTests extends WindowTestsBase { - - @Test - public void testAddWindow() { - final WindowTestUtils.TestWindowToken token = - WindowTestUtils.createTestWindowToken(0, mDisplayContent); - - assertEquals(0, token.getWindowsCount()); - - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); - final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3"); - - token.addWindow(window1); - // NOTE: Child windows will not be added to the token as window containers can only - // contain/reference their direct children. - token.addWindow(window11); - token.addWindow(window12); - token.addWindow(window2); - token.addWindow(window3); - - // Should not contain the child windows that were added above. - assertEquals(3, token.getWindowsCount()); - assertTrue(token.hasWindow(window1)); - assertFalse(token.hasWindow(window11)); - assertFalse(token.hasWindow(window12)); - assertTrue(token.hasWindow(window2)); - assertTrue(token.hasWindow(window3)); - } - - @Test - public void testChildRemoval() { - final DisplayContent dc = mDisplayContent; - final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(0, dc); - - assertEquals(token, dc.getWindowToken(token.token)); - - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); - - window2.removeImmediately(); - // The token should still be mapped in the display content since it still has a child. - assertEquals(token, dc.getWindowToken(token.token)); - - window1.removeImmediately(); - // The token should have been removed from the display content since it no longer has a - // child. - assertEquals(null, dc.getWindowToken(token.token)); - } - - /** - * Test that a window token isn't orphaned by the system when it is requested to be removed. - * Tokens should only be removed from the system when all their windows are gone. - */ - @Test - public void testTokenRemovalProcess() { - final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken( - TYPE_TOAST, mDisplayContent, true /* persistOnEmpty */); - - // Verify that the token is on the display - assertNotNull(mDisplayContent.getWindowToken(token.token)); - - final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1"); - final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2"); - - mDisplayContent.removeWindowToken(token.token); - // Verify that the token is no longer mapped on the display - assertNull(mDisplayContent.getWindowToken(token.token)); - // Verify that the token is still attached to its parent - assertNotNull(token.getParent()); - // Verify that the token windows are still around. - assertEquals(2, token.getWindowsCount()); - - window1.removeImmediately(); - // Verify that the token is still attached to its parent - assertNotNull(token.getParent()); - // Verify that the other token window is still around. - assertEquals(1, token.getWindowsCount()); - - window2.removeImmediately(); - // Verify that the token is no-longer attached to its parent - assertNull(token.getParent()); - // Verify that the token windows are no longer attached to it. - assertEquals(0, token.getWindowsCount()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java deleted file mode 100644 index 3dcea75b8ae5..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2016 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 com.android.server.wm; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; -import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; - -import static com.google.common.truth.Truth.assertThat; - -import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; -import android.view.SurfaceSession; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Test; - -import java.util.HashMap; -import java.util.LinkedList; - -/** - * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method. - * - * Build/Install/Run: - * atest FrameworksServicesTests:ZOrderingTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class ZOrderingTests extends WindowTestsBase { - - private static class LayerRecordingTransaction extends SurfaceControl.Transaction { - // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder - // such that we can keep track of the parents of Surfaces as they are constructed. - private final HashMap mParentFor = new HashMap<>(); - HashMap mLayersForControl = new HashMap<>(); - HashMap mRelativeLayersForControl = new HashMap<>(); - - @Override - public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) { - mRelativeLayersForControl.remove(sc); - mLayersForControl.put(sc, layer); - return super.setLayer(sc, layer); - } - - @Override - public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, - SurfaceControl relativeTo, - int layer) { - mRelativeLayersForControl.put(sc, relativeTo); - mLayersForControl.put(sc, layer); - return super.setRelativeLayer(sc, relativeTo, layer); - } - - private int getLayer(SurfaceControl sc) { - return mLayersForControl.getOrDefault(sc, 0); - } - - private SurfaceControl getRelativeLayer(SurfaceControl sc) { - return mRelativeLayersForControl.get(sc); - } - - void addParentFor(SurfaceControl child, SurfaceControl parent) { - mParentFor.put(child, parent); - } - - SurfaceControl getParentFor(SurfaceControl child) { - return mParentFor.get(child); - } - - @Override - public void close() { - - } - } - - private static class HierarchyRecorder extends SurfaceControl.Builder { - private LayerRecordingTransaction mTransaction; - private SurfaceControl mPendingParent; - - HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) { - super(s); - mTransaction = transaction; - } - - @Override - public SurfaceControl.Builder setParent(SurfaceControl sc) { - mPendingParent = sc; - return super.setParent(sc); - } - - @Override - public SurfaceControl build() { - final SurfaceControl sc = super.build(); - mTransaction.addParentFor(sc, mPendingParent); - mPendingParent = null; - return sc; - } - } - - private static class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory { - private LayerRecordingTransaction mTransaction; - - HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) { - mTransaction = transaction; - } - - @Override - public SurfaceControl.Builder make(SurfaceSession s) { - final LayerRecordingTransaction transaction = mTransaction; - return new HierarchyRecorder(s, transaction); - } - } - - private LayerRecordingTransaction mTransaction; - - @Override - void beforeCreateDisplay() { - // We can't use @Before here because it may happen after WindowTestsBase @Before - // which is after construction of the DisplayContent, meaning the HierarchyRecorder - // would miss construction of the top-level layers. - mTransaction = new LayerRecordingTransaction(); - mWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(mTransaction); - mWm.mTransactionFactory = () -> mTransaction; - } - - @After - public void tearDown() { - mTransaction.close(); - } - - private static LinkedList getAncestors(LayerRecordingTransaction t, - SurfaceControl sc) { - LinkedList p = new LinkedList<>(); - SurfaceControl current = sc; - do { - p.addLast(current); - - SurfaceControl rs = t.getRelativeLayer(current); - if (rs != null) { - current = rs; - } else { - current = t.getParentFor(current); - } - } while (current != null); - return p; - } - - - private static void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, - SurfaceControl right) { - final LinkedList leftParentChain = getAncestors(t, left); - final LinkedList rightParentChain = getAncestors(t, right); - - SurfaceControl leftTop = leftParentChain.peekLast(); - SurfaceControl rightTop = rightParentChain.peekLast(); - while (leftTop != null && rightTop != null && leftTop == rightTop) { - leftParentChain.removeLast(); - rightParentChain.removeLast(); - leftTop = leftParentChain.peekLast(); - rightTop = rightParentChain.peekLast(); - } - - if (rightTop == null) { // right is the parent of left. - assertThat(t.getLayer(leftTop)).isGreaterThan(0); - } else if (leftTop == null) { // left is the parent of right. - assertThat(t.getLayer(rightTop)).isLessThan(0); - } else { - assertThat(t.getLayer(leftTop)).isGreaterThan(t.getLayer(rightTop)); - } - } - - void assertWindowHigher(WindowState left, WindowState right) { - assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); - } - - WindowState createWindow(String name) { - return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); - } - - @Test - public void testAssignWindowLayers_ForImeWithNoTarget() { - mDisplayContent.mInputMethodTarget = null; - mDisplayContent.assignChildLayers(mTransaction); - - // The Ime has an higher base layer than app windows and lower base layer than system - // windows, so it should be above app windows and below system windows if there isn't an IME - // target. - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testAssignWindowLayers_ForImeWithAppTarget() { - final WindowState imeAppTarget = createWindow("imeAppTarget"); - mDisplayContent.mInputMethodTarget = imeAppTarget; - - mDisplayContent.assignChildLayers(mTransaction); - - // Ime should be above all app windows and below system windows if it is targeting an app - // window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { - final WindowState imeAppTarget = createWindow("imeAppTarget"); - final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, - TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, - "imeAppTargetChildAboveWindow"); - final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget, - TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken, - "imeAppTargetChildBelowWindow"); - - mDisplayContent.mInputMethodTarget = imeAppTarget; - mDisplayContent.assignChildLayers(mTransaction); - - // Ime should be above all app windows except for child windows that are z-ordered above it - // and below system windows if it is targeting an app window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { - final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); - final WindowState imeAppTarget = createWindow("imeAppTarget"); - final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); - - mDisplayContent.mInputMethodTarget = imeAppTarget; - mDisplayContent.assignChildLayers(mTransaction); - - // Ime should be above all app windows except for non-fullscreen app window above it and - // below system windows if it is targeting an app window. - assertWindowHigher(mImeWindow, imeAppTarget); - assertWindowHigher(mImeWindow, appBelowImeTarget); - assertWindowHigher(appAboveImeTarget, mImeWindow); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testAssignWindowLayers_ForImeNonAppImeTarget() { - final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, - mDisplayContent, "imeSystemOverlayTarget", - true /* ownerCanAddInternalSystemWindow */); - - mDisplayContent.mInputMethodTarget = imeSystemOverlayTarget; - mDisplayContent.assignChildLayers(mTransaction); - - // The IME target base layer is higher than all window except for the nav bar window, so the - // IME should be above all windows except for the nav bar. - assertWindowHigher(mImeWindow, imeSystemOverlayTarget); - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); - - // The IME has a higher base layer than the status bar so we may expect it to go - // above the status bar once they are both in the Non-App layer, as past versions of this - // test enforced. However this seems like the wrong behavior unless the status bar is the - // IME target. - assertWindowHigher(mNavBarWindow, mImeWindow); - assertWindowHigher(mStatusBarWindow, mImeWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testAssignWindowLayers_ForStatusBarImeTarget() { - mDisplayContent.mInputMethodTarget = mStatusBarWindow; - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(mImeWindow, mChildAppWindowAbove); - assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); - assertWindowHigher(mImeWindow, mStatusBarWindow); - - // And, IME dialogs should always have an higher layer than the IME. - assertWindowHigher(mImeDialogWindow, mImeWindow); - } - - @Test - public void testStackLayers() { - final WindowState anyWindow1 = createWindow("anyWindow"); - final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, - ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, - "pinnedStackWindow"); - final WindowState dockedStackWindow = createWindowOnStack(null, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "dockedStackWindow"); - final WindowState assistantStackWindow = createWindowOnStack(null, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, - mDisplayContent, "assistantStackWindow"); - final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, - mDisplayContent, "homeActivityWindow"); - final WindowState anyWindow2 = createWindow("anyWindow2"); - - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(dockedStackWindow, homeActivityWindow); - assertWindowHigher(assistantStackWindow, homeActivityWindow); - assertWindowHigher(pinnedStackWindow, homeActivityWindow); - assertWindowHigher(anyWindow1, homeActivityWindow); - assertWindowHigher(anyWindow2, homeActivityWindow); - assertWindowHigher(pinnedStackWindow, anyWindow1); - assertWindowHigher(pinnedStackWindow, anyWindow2); - assertWindowHigher(pinnedStackWindow, dockedStackWindow); - assertWindowHigher(pinnedStackWindow, assistantStackWindow); - } - - @Test - public void testAssignWindowLayers_ForSysUiPanels() { - final WindowState navBarPanel = - createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); - final WindowState statusBarPanel = - createWindow(null, TYPE_STATUS_BAR_PANEL, mDisplayContent, "StatusBarPanel"); - final WindowState statusBarSubPanel = - createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel"); - mDisplayContent.assignChildLayers(mTransaction); - - // Ime should be above all app windows and below system windows if it is targeting an app - // window. - assertWindowHigher(navBarPanel, mNavBarWindow); - assertWindowHigher(statusBarPanel, mStatusBarWindow); - assertWindowHigher(statusBarSubPanel, statusBarPanel); - } - - @Test - public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() { - // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA - // then we can drop all negative layering on the windowing side. - - final WindowState anyWindow = createWindow("anyWindow"); - final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, - "TypeApplicationMediaChild"); - final WindowState mediaOverlayChild = createWindow(anyWindow, - TYPE_APPLICATION_MEDIA_OVERLAY, - mDisplayContent, "TypeApplicationMediaOverlayChild"); - - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(anyWindow, mediaOverlayChild); - assertWindowHigher(mediaOverlayChild, child); - } - - @Test - public void testDockedDividerPosition() { - final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, - ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, - "pinnedStackWindow"); - final WindowState splitScreenWindow = createWindowOnStack(null, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "splitScreenWindow"); - final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, - TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); - final WindowState assistantStackWindow = createWindowOnStack(null, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, - mDisplayContent, "assistantStackWindow"); - - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(mDockedDividerWindow, splitScreenWindow); - assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); - assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); - } -} diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 67acec689244..3f3b99692e9c 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -34,6 +34,8 @@ + + diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java new file mode 100644 index 000000000000..5556a150cf3a --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for the {@link TaskStack} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AnimatingAppWindowTokenRegistryTest + */ +@SmallTest +@Presubmit +@FlakyTest(detail = "Promote once confirmed non-flaky") +public class AnimatingAppWindowTokenRegistryTest extends WindowTestsBase { + + @Mock + AnimationAdapter mAdapter; + + @Mock + Runnable mMockEndDeferFinishCallback1; + @Mock + Runnable mMockEndDeferFinishCallback2; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testDeferring() { + final AppWindowToken window1 = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD, + "window2").mAppToken; + final AnimatingAppWindowTokenRegistry registry = + window1.getStack().getAnimatingAppWindowTokenRegistry(); + + window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + assertTrue(window1.isSelfAnimating()); + assertTrue(window2.isSelfAnimating()); + + // Make sure that first animation finish is deferred, second one is not deferred, and first + // one gets cancelled. + assertTrue(registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1)); + assertFalse(registry.notifyAboutToFinish(window2, mMockEndDeferFinishCallback2)); + verify(mMockEndDeferFinishCallback1).run(); + verifyZeroInteractions(mMockEndDeferFinishCallback2); + } + + @Test + public void testContainerRemoved() { + final AppWindowToken window1 = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD, + "window2").mAppToken; + final AnimatingAppWindowTokenRegistry registry = + window1.getStack().getAnimatingAppWindowTokenRegistry(); + + window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */); + assertTrue(window1.isSelfAnimating()); + assertTrue(window2.isSelfAnimating()); + + // Make sure that first animation finish is deferred, and removing the second window stops + // finishes all pending deferred finishings. + registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1); + window2.setParent(null); + verify(mMockEndDeferFinishCallback1).run(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java new file mode 100644 index 000000000000..5e12a950c560 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_OPEN; + +import static junit.framework.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:AppTransitionControllerTest + */ +@SmallTest +@Presubmit +public class AppTransitionControllerTest extends WindowTestsBase { + + private AppTransitionController mAppTransitionController; + + @Before + public void setUp() throws Exception { + mAppTransitionController = new AppTransitionController(mWm, mDisplayContent); + } + + @Test + public void testTranslucentOpen() { + synchronized (mWm.mGlobalLock) { + final AppWindowToken behind = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + translucentOpening.setFillsParent(false); + translucentOpening.setHidden(true); + mDisplayContent.mOpeningApps.add(behind); + mDisplayContent.mOpeningApps.add(translucentOpening); + assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN, + mAppTransitionController.maybeUpdateTransitToTranslucentAnim( + TRANSIT_TASK_OPEN)); + } + } + + @Test + public void testTranslucentClose() { + synchronized (mWm.mGlobalLock) { + final AppWindowToken behind = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + translucentClosing.setFillsParent(false); + mDisplayContent.mClosingApps.add(translucentClosing); + assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE, + mAppTransitionController.maybeUpdateTransitToTranslucentAnim( + TRANSIT_TASK_CLOSE)); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java new file mode 100644 index 000000000000..577859cf2107 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyBoolean; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.Display; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link AppTransition}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AppTransitionTests + */ +@SmallTest +@Presubmit +public class AppTransitionTests extends WindowTestsBase { + + private DisplayContent mDc; + + @Before + public void setUp() throws Exception { + mDc = mWm.getDefaultDisplayContentLocked(); + // For unit test, we don't need to test performSurfacePlacement to prevent some + // abnormal interaction with surfaceflinger native side. + mWm.mRoot = spy(mWm.mRoot); + doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + } + + @Test + public void testKeyguardOverride() { + mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testKeyguardKeep() { + mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testForceOverride() { + mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); + mDc.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testCrashing() { + mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); + mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_CRASHING_ACTIVITY_CLOSE, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testKeepKeyguard_withCrashing() { + mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); + mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); + assertEquals(TRANSIT_KEYGUARD_GOING_AWAY, mDc.mAppTransition.getAppTransition()); + } + + @Test + public void testAppTransitionStateForMultiDisplay() { + // Create 2 displays & presume both display the state is ON for ready to display & animate. + final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); + final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); + + // Create 2 app window tokens to represent 2 activity window. + final WindowTestUtils.TestAppWindowToken token1 = createTestAppWindowToken(dc1, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + + token1.allDrawn = true; + token1.startingDisplayed = true; + token1.startingMoved = true; + + // Simulate activity resume / finish flows to prepare app transition & set visibility, + // make sure transition is set as expected for each display. + dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); + dc2.getController().prepareAppTransition(TRANSIT_ACTIVITY_CLOSE, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition()); + // One activity window is visible for resuming & the other activity window is invisible + // for finishing in different display. + token1.setVisibility(true, false); + token2.setVisibility(false, false); + + // Make sure each display is in animating stage. + assertTrue(dc1.mOpeningApps.size() > 0); + assertTrue(dc2.mClosingApps.size() > 0); + assertTrue(dc1.isAppAnimating()); + assertTrue(dc2.isAppAnimating()); + } + + @Test + public void testCleanAppTransitionWhenTaskStackReparent() { + // Create 2 displays & presume both display the state is ON for ready to display & animate. + final DisplayContent dc1 = createNewDisplayWithController(Display.STATE_ON); + final DisplayContent dc2 = createNewDisplayWithController(Display.STATE_ON); + + final TaskStack stack1 = createTaskStackOnDisplay(dc1); + final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token1 = + WindowTestUtils.createTestAppWindowToken(dc1); + task1.addChild(token1, 0); + + // Simulate same app is during opening / closing transition set stage. + dc1.mClosingApps.add(token1); + assertTrue(dc1.mClosingApps.size() > 0); + + dc1.getController().prepareAppTransition(TRANSIT_ACTIVITY_OPEN, + false /* alwaysKeepCurrent */, 0 /* flags */, false /* forceOverride */); + assertEquals(TRANSIT_ACTIVITY_OPEN, dc1.mAppTransition.getAppTransition()); + assertTrue(dc1.mAppTransition.isTransitionSet()); + + dc1.mOpeningApps.add(token1); + assertTrue(dc1.mOpeningApps.size() > 0); + + // Move stack to another display. + stack1.getController().reparent(dc2.getDisplayId(), new Rect(), true); + + // Verify if token are cleared from both pending transition list in former display. + assertFalse(dc1.mOpeningApps.contains(token1)); + assertFalse(dc1.mOpeningApps.contains(token1)); + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java new file mode 100644 index 000000000000..dcfb8797eaba --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.SurfaceControl.Transaction; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; + +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.WindowTestUtils.TestAppWindowToken; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Animation related tests for the {@link AppWindowToken} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AppWindowTokenAnimationTests + */ +@SmallTest +public class AppWindowTokenAnimationTests extends WindowTestsBase { + + private TestAppWindowToken mToken; + + @Mock + private Transaction mTransaction; + @Mock + private AnimationAdapter mSpec; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD); + mToken.setPendingTransaction(mTransaction); + } + + @Test + public void clipAfterAnim_boundsLayerIsCreated() { + mToken.mNeedsAnimationBoundsLayer = true; + + mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + verify(mTransaction).reparent(eq(mToken.getSurfaceControl()), + eq(mToken.mSurfaceAnimator.mLeash.getHandle())); + verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash), + eq(mToken.mAnimationBoundsLayer.getHandle())); + } + + @Test + public void clipAfterAnim_boundsLayerIsDestroyed() { + mToken.mNeedsAnimationBoundsLayer = true; + mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash; + final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer; + final ArgumentCaptor callbackCaptor = + ArgumentCaptor.forClass( + SurfaceAnimator.OnAnimationFinishedCallback.class); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + callbackCaptor.getValue().onAnimationFinished(mSpec); + verify(mTransaction).destroy(eq(leash)); + verify(mTransaction).destroy(eq(animationBoundsLayer)); + assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); + } + + @Test + public void clipAfterAnimCancelled_boundsLayerIsDestroyed() { + mToken.mNeedsAnimationBoundsLayer = true; + mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash; + final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer; + + mToken.mSurfaceAnimator.cancelAnimation(); + verify(mTransaction).destroy(eq(leash)); + verify(mTransaction).destroy(eq(animationBoundsLayer)); + assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse(); + } + + @Test + public void clipNoneAnim_boundsLayerIsNotCreated() { + mToken.mNeedsAnimationBoundsLayer = false; + + mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + verify(mTransaction).reparent(eq(mToken.getSurfaceControl()), + eq(mToken.mSurfaceAnimator.mLeash.getHandle())); + assertThat(mToken.mAnimationBoundsLayer).isNull(); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java new file mode 100644 index 000000000000..8653bf96d984 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_UNSET; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; + +import android.graphics.Point; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.Surface; +import android.view.WindowManager; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link AppWindowToken} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AppWindowTokenTests + */ +@FlakyTest(bugId = 68267650) +@SmallTest +@Presubmit +public class AppWindowTokenTests extends WindowTestsBase { + + TaskStack mStack; + Task mTask; + WindowTestUtils.TestAppWindowToken mToken; + + private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); + + @Before + public void setUp() throws Exception { + mStack = createTaskStackOnDisplay(mDisplayContent); + mTask = createTaskInStack(mStack, 0 /* userId */); + mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + + mTask.addChild(mToken, 0); + } + + @Test + @Presubmit + public void testAddWindow_Order() { + assertEquals(0, mToken.getWindowsCount()); + + final WindowState win1 = createWindow(null, TYPE_APPLICATION, mToken, "win1"); + final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, mToken, + "startingWin"); + final WindowState baseWin = createWindow(null, TYPE_BASE_APPLICATION, mToken, "baseWin"); + final WindowState win4 = createWindow(null, TYPE_APPLICATION, mToken, "win4"); + + // Should not contain the windows that were added above. + assertEquals(4, mToken.getWindowsCount()); + assertTrue(mToken.hasWindow(win1)); + assertTrue(mToken.hasWindow(startingWin)); + assertTrue(mToken.hasWindow(baseWin)); + assertTrue(mToken.hasWindow(win4)); + + // The starting window should be on-top of all other windows. + assertEquals(startingWin, mToken.getLastChild()); + + // The base application window should be below all other windows. + assertEquals(baseWin, mToken.getFirstChild()); + mToken.removeImmediately(); + } + + @Test + @Presubmit + public void testFindMainWindow() { + assertNull(mToken.findMainWindow()); + + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1"); + final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window11"); + final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, mToken, "window12"); + assertEquals(window1, mToken.findMainWindow()); + window1.mAnimatingExit = true; + assertEquals(window1, mToken.findMainWindow()); + final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, mToken, + "window2"); + assertEquals(window2, mToken.findMainWindow()); + mToken.removeImmediately(); + } + + @Test + @Presubmit + public void testGetTopFullscreenWindow() { + assertNull(mToken.getTopFullscreenWindow()); + + final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, mToken, "window1"); + final WindowState window11 = createWindow(null, TYPE_APPLICATION, mToken, "window11"); + final WindowState window12 = createWindow(null, TYPE_APPLICATION, mToken, "window12"); + assertEquals(window12, mToken.getTopFullscreenWindow()); + window12.mAttrs.width = 500; + assertEquals(window11, mToken.getTopFullscreenWindow()); + window11.mAttrs.width = 500; + assertEquals(window1, mToken.getTopFullscreenWindow()); + mToken.removeImmediately(); + } + + @Test + public void testLandscapeSeascapeRotationByApp() { + // Some plumbing to get the service ready for rotation updates. + mWm.mDisplayReady = true; + mWm.mDisplayEnabled = true; + + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.setTitle("AppWindow"); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); + mToken.addWindow(appWindow); + + // Set initial orientation and update. + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null, + mDisplayContent.getDisplayId()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getLastOrientation()); + appWindow.mResizeReported = false; + + // Update the orientation to perform 180 degree rotation and check that resize was reported. + mToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + mWm.updateOrientationFromAppTokens(mDisplayContent.getOverrideConfiguration(), null, + mDisplayContent.getDisplayId()); + mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation()); + assertTrue(appWindow.mResizeReported); + appWindow.removeImmediately(); + } + + @Test + public void testLandscapeSeascapeRotationByPolicy() { + // Some plumbing to get the service ready for rotation updates. + mWm.mDisplayReady = true; + mWm.mDisplayEnabled = true; + + final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation()); + mDisplayContent.setDisplayRotation(spiedRotation); + + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.setTitle("AppWindow"); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); + mToken.addWindow(appWindow); + + // Set initial orientation and update. + performRotation(spiedRotation, Surface.ROTATION_90); + appWindow.mResizeReported = false; + + // Update the rotation to perform 180 degree rotation and check that resize was reported. + performRotation(spiedRotation, Surface.ROTATION_270); + assertTrue(appWindow.mResizeReported); + + appWindow.removeImmediately(); + } + + private void performRotation(DisplayRotation spiedRotation, int rotationToReport) { + doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt()); + int oldRotation = mDisplayContent.getRotation(); + mWm.updateRotation(false, false); + // Must manually apply here since ATM doesn't know about the display during this test + // (meaning it can't perform the normal sendNewConfiguration flow). + mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation()); + // Prevent the next rotation from being deferred by animation. + mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null); + mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */); + } + + @Test + @Presubmit + public void testGetOrientation() { + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + mToken.setFillsParent(false); + // Can specify orientation if app doesn't fill parent. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); + + mToken.setFillsParent(true); + mToken.setHidden(true); + mToken.sendingToBottom = true; + // Can not specify orientation if app isn't visible even though it fills parent. + assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); + // Can specify orientation if the current orientation candidate is orientation behind. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, + mToken.getOrientation(SCREEN_ORIENTATION_BEHIND)); + } + + @Test + @Presubmit + public void testKeyguardFlagsDuringRelaunch() { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.flags |= FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD; + attrs.setTitle("AppWindow"); + final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken); + + // Add window with show when locked flag + mToken.addWindow(appWindow); + assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); + + // Start relaunching + mToken.startRelaunching(); + assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); + + // Remove window and make sure that we still report back flag + mToken.removeChild(appWindow); + assertTrue(mToken.containsShowWhenLockedWindow() && mToken.containsDismissKeyguardWindow()); + + // Finish relaunching and ensure flag is now not reported + mToken.finishRelaunching(); + assertFalse( + mToken.containsShowWhenLockedWindow() || mToken.containsDismissKeyguardWindow()); + } + + @Test + @FlakyTest(detail = "Promote once confirmed non-flaky") + public void testStuckExitingWindow() { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + closingWindow.mAnimatingExit = true; + closingWindow.mRemoveOnExit = true; + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + + // We pretended that we were running an exit animation, but that should have been cleared up + // by changing visibility of AppWindowToken + closingWindow.removeIfPossible(); + assertTrue(closingWindow.mRemoved); + } + + @Test + public void testSetOrientation() { + // Assert orientation is unspecified to start. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation()); + + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); + + mDisplayContent.removeAppToken(mToken.token); + // Assert orientation is unset to after container is removed. + assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); + + // Reset display frozen state + mWm.mDisplayFrozen = false; + } + + @Test + public void testCreateRemoveStartingWindow() { + mToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertHasStartingWindow(mToken); + mToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(mToken); + } + + @Test + public void testAddRemoveRace() { + // There was once a race condition between adding and removing starting windows + for (int i = 0; i < 1000; i++) { + final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken(); + + appToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + appToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(appToken); + + appToken.getParent().getParent().removeImmediately(); + } + } + + @Test + public void testTransferStartingWindow() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(), + true, true, false, true, false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + @Test + public void testTransferStartingWindowWhileCreating() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen( + () -> { + // Surprise, ...! Transfer window in the middle of the creation flow. + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, + token1.appToken.asBinder(), true, true, false, + true, false, false); + }); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() { + final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(taskStack, 0 /* userId */); + return createTestAppWindowTokenForGivenTask(task); + } + + private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) { + final WindowTestUtils.TestAppWindowToken appToken = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task.addChild(appToken, 0); + waitUntilHandlersIdle(); + return appToken; + } + + @Test + public void testTryTransferStartingWindowFromHiddenAboveToken() { + // Add two tasks on top of each other. + final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken tokenBottom = + createTestAppWindowTokenForGivenTask(tokenTop.getTask()); + + // Add a starting window. + tokenTop.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + + // Make the top one invisible, and try transferring the starting window from the top to the + // bottom one. + tokenTop.setVisibility(false, false); + tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded(); + + // Assert that the bottom window now has the starting window. + assertNoStartingWindow(tokenTop); + assertHasStartingWindow(tokenBottom); + } + + @Test + public void testTransitionAnimationPositionAndBounds() { + final Rect stackBounds = new Rect( + 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */); + final Rect taskBounds = new Rect( + 100/* left */, 200 /* top */, 600 /* right */, 600 /* bottom */); + mStack.setBounds(stackBounds); + mTask.setBounds(taskBounds); + + mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertTransitionAnimationPositionAndBounds(taskBounds.left, taskBounds.top, stackBounds); + + mTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + assertTransitionAnimationPositionAndBounds(stackBounds.left, stackBounds.top, stackBounds); + } + + private void assertTransitionAnimationPositionAndBounds(int expectedX, int expectedY, + Rect expectedBounds) { + final Point outPosition = new Point(); + final Rect outBounds = new Rect(); + mToken.getAnimationBounds(outPosition, outBounds); + assertEquals(expectedX, outPosition.x); + assertEquals(expectedY, outPosition.y); + assertEquals(expectedBounds, outBounds); + } + + private void assertHasStartingWindow(AppWindowToken atoken) { + assertNotNull(atoken.startingSurface); + assertNotNull(atoken.startingData); + assertNotNull(atoken.startingWindow); + } + + private void assertNoStartingWindow(AppWindowToken atoken) { + assertNull(atoken.startingSurface); + assertNull(atoken.startingWindow); + assertNull(atoken.startingData); + atoken.forAllWindows(windowState -> { + assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); + }, true); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java new file mode 100644 index 000000000000..1c5391ed3a6c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; +import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; +import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.platform.test.annotations.Presubmit; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.BoundsAnimationController.BoundsAnimator; +import com.android.server.wm.WindowManagerInternal.AppTransitionListener; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks + * depending on the various interactions. + * + * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] + * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, + * or if a new animation starts on the same target. The tests below verifies that the target is + * notified of all the cases where it is animating and cancelled so that it can respond + * appropriately. + * + * Build/Install/Run: + * atest FrameworksServicesTests:BoundsAnimationControllerTests + */ +@SmallTest +@Presubmit +public class BoundsAnimationControllerTests extends WindowTestsBase { + + /** + * Mock value animator to simulate updates with. + */ + private static class MockValueAnimator extends ValueAnimator { + + private float mFraction; + + MockValueAnimator getWithValue(float fraction) { + mFraction = fraction; + return this; + } + + @Override + public Object getAnimatedValue() { + return mFraction; + } + } + + /** + * Mock app transition to fire notifications to the bounds animator. + */ + private static class MockAppTransition extends AppTransition { + + private AppTransitionListener mListener; + + MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) { + super(context, wm, displayContent); + } + + @Override + void registerListenerLocked(AppTransitionListener listener) { + mListener = listener; + } + + public void notifyTransitionPending() { + mListener.onAppTransitionPendingLocked(); + } + + public void notifyTransitionCancelled(int transit) { + mListener.onAppTransitionCancelledLocked(transit); + } + + public void notifyTransitionStarting(int transit) { + mListener.onAppTransitionStartingLocked(transit, null, null, 0, 0, 0); + } + + public void notifyTransitionFinished() { + mListener.onAppTransitionFinishedLocked(null); + } + } + + /** + * A test animate bounds user to track callbacks from the bounds animation. + */ + private static class TestBoundsAnimationTarget implements BoundsAnimationTarget { + + boolean mAwaitingAnimationStart; + boolean mMovedToFullscreen; + boolean mAnimationStarted; + boolean mSchedulePipModeChangedOnStart; + boolean mForcePipModeChangedCallback; + boolean mAnimationEnded; + Rect mAnimationEndFinalStackBounds; + boolean mSchedulePipModeChangedOnEnd; + boolean mBoundsUpdated; + boolean mCancelRequested; + Rect mStackBounds; + Rect mTaskBounds; + + void initialize(Rect from) { + mAwaitingAnimationStart = true; + mMovedToFullscreen = false; + mAnimationStarted = false; + mAnimationEnded = false; + mAnimationEndFinalStackBounds = null; + mForcePipModeChangedCallback = false; + mSchedulePipModeChangedOnStart = false; + mSchedulePipModeChangedOnEnd = false; + mStackBounds = from; + mTaskBounds = null; + mBoundsUpdated = false; + } + + @Override + public boolean onAnimationStart(boolean schedulePipModeChangedCallback, + boolean forceUpdate) { + mAwaitingAnimationStart = false; + mAnimationStarted = true; + mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; + mForcePipModeChangedCallback = forceUpdate; + return true; + } + + @Override + public boolean shouldDeferStartOnMoveToFullscreen() { + return true; + } + + @Override + public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { + // TODO: Once we break the runs apart, we should fail() here if this is called outside + // of onAnimationStart() and onAnimationEnd() + if (mCancelRequested) { + mCancelRequested = false; + return false; + } else { + mBoundsUpdated = true; + mStackBounds = stackBounds; + mTaskBounds = taskBounds; + return true; + } + } + + @Override + public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, + boolean moveToFullscreen) { + mAnimationEnded = true; + mAnimationEndFinalStackBounds = finalStackBounds; + mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; + mMovedToFullscreen = moveToFullscreen; + mTaskBounds = null; + } + } + + /** + * Drives the animations, makes common assertions along the way. + */ + private static class BoundsAnimationDriver { + + private final BoundsAnimationController mController; + private final TestBoundsAnimationTarget mTarget; + private final MockValueAnimator mMockAnimator; + + private BoundsAnimator mAnimator; + private Rect mFrom; + private Rect mTo; + private Rect mLargerBounds; + private Rect mExpectedFinalBounds; + + BoundsAnimationDriver(BoundsAnimationController controller, + TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { + mController = controller; + mTarget = target; + mMockAnimator = mockValueAnimator; + } + + BoundsAnimationDriver start(Rect from, Rect to) { + if (mAnimator != null) { + throw new IllegalArgumentException("Call restart() to restart an animation"); + } + + boolean fromFullscreen = from.equals(BOUNDS_FULL); + boolean toFullscreen = to.equals(BOUNDS_FULL); + + mTarget.initialize(from); + + // Started, not running + assertTrue(mTarget.mAwaitingAnimationStart); + assertFalse(mTarget.mAnimationStarted); + + startImpl(from, to); + + // Ensure that the animator is paused for the all windows drawn signal when animating + // to/from fullscreen + if (fromFullscreen || toFullscreen) { + assertTrue(mAnimator.isPaused()); + mController.onAllWindowsDrawn(); + } else { + assertTrue(!mAnimator.isPaused()); + } + + // Started and running + assertFalse(mTarget.mAwaitingAnimationStart); + assertTrue(mTarget.mAnimationStarted); + + return this; + } + + BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) { + if (mAnimator == null) { + throw new IllegalArgumentException("Call start() to start a new animation"); + } + + BoundsAnimator oldAnimator = mAnimator; + boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); + + // Reset the animation start state + mTarget.mAnimationStarted = false; + + // Start animation + startImpl(mTarget.mStackBounds, to); + + if (toSameBounds) { + // Same animator if same final bounds + assertSame(oldAnimator, mAnimator); + } + + if (expectStartedAndPipModeChangedCallback) { + // Replacing animation with pending pip mode changed callback, ensure we update + assertTrue(mTarget.mAnimationStarted); + assertTrue(mTarget.mSchedulePipModeChangedOnStart); + assertTrue(mTarget.mForcePipModeChangedCallback); + } else { + // No animation start for replacing animation + assertFalse(mTarget.mAnimationStarted); + } + mTarget.mAnimationStarted = true; + return this; + } + + private BoundsAnimationDriver startImpl(Rect from, Rect to) { + boolean fromFullscreen = from.equals(BOUNDS_FULL); + boolean toFullscreen = to.equals(BOUNDS_FULL); + mFrom = new Rect(from); + mTo = new Rect(to); + mExpectedFinalBounds = new Rect(to); + mLargerBounds = getLargerBounds(mFrom, mTo); + + // Start animation + final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen + ? SCHEDULE_PIP_MODE_CHANGED_ON_START + : fromFullscreen + ? SCHEDULE_PIP_MODE_CHANGED_ON_END + : NO_PIP_MODE_CHANGED_CALLBACKS; + mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, + schedulePipModeChangedState, fromFullscreen, toFullscreen); + + // Original stack bounds, frozen task bounds + assertEquals(mFrom, mTarget.mStackBounds); + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + + // Animating to larger size + if (mFrom.equals(mLargerBounds)) { + assertFalse(mAnimator.animatingToLargerSize()); + } else if (mTo.equals(mLargerBounds)) { + assertTrue(mAnimator.animatingToLargerSize()); + } + + return this; + } + + BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { + // Callback made + assertTrue(mTarget.mAnimationStarted); + + assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); + return this; + } + + BoundsAnimationDriver update(float t) { + mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); + + // Temporary stack bounds, frozen task bounds + if (t == 0f) { + assertEquals(mFrom, mTarget.mStackBounds); + } else if (t == 1f) { + assertEquals(mTo, mTarget.mStackBounds); + } else { + assertNotEquals(mFrom, mTarget.mStackBounds); + assertNotEquals(mTo, mTarget.mStackBounds); + } + assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); + return this; + } + + BoundsAnimationDriver cancel() { + // Cancel + mTarget.mCancelRequested = true; + mTarget.mBoundsUpdated = false; + mExpectedFinalBounds = null; + + // Update + mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); + + // Not started, not running, cancel reset + assertFalse(mTarget.mCancelRequested); + + // Stack/task bounds not updated + assertFalse(mTarget.mBoundsUpdated); + + // Callback made + assertTrue(mTarget.mAnimationEnded); + assertNull(mTarget.mAnimationEndFinalStackBounds); + + return this; + } + + BoundsAnimationDriver end() { + mAnimator.end(); + + // Final stack bounds + assertEquals(mTo, mTarget.mStackBounds); + assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); + assertNull(mTarget.mTaskBounds); + + return this; + } + + BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, + boolean moveToFullscreen) { + // Callback made + assertTrue(mTarget.mAnimationEnded); + + assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); + assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); + return this; + } + + private static Rect getLargerBounds(Rect r1, Rect r2) { + int r1Area = r1.width() * r1.height(); + int r2Area = r2.width() * r2.height(); + return (r1Area <= r2Area) ? r2 : r1; + } + } + + // Constants + private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; + private static final boolean MOVE_TO_FULLSCREEN = true; + private static final int DURATION = 100; + + // Some dummy bounds to represent fullscreen and floating bounds + private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); + private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); + private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); + + // Common + private MockAppTransition mMockAppTransition; + private TestBoundsAnimationTarget mTarget; + private BoundsAnimationController mController; + private BoundsAnimationDriver mDriver; + + // Temp + private static final Rect sTmpRect = new Rect(); + + @Before + public void setUp() throws Exception { + final Context context = getInstrumentation().getTargetContext(); + final Handler handler = new Handler(Looper.getMainLooper()); + mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent); + mTarget = new TestBoundsAnimationTarget(); + mController = new BoundsAnimationController(context, mMockAppTransition, handler, null); + final MockValueAnimator mockValueAnimator = new MockValueAnimator(); + mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator); + } + + /** BASE TRANSITIONS **/ + + @UiThreadTest + @Test + public void testFullscreenToFloatingTransition() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToFullscreenTransition() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToSmallerFloatingTransition() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToLargerFloatingTransition() { + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0f) + .update(0.5f) + .update(1f) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + /** F->!F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFullscreenToFloatingCancelFromTarget() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_SMALLER_FLOATING, + false /* expectStartedAndPipModeChangedCallback */) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { + // When animating from fullscreen and the animation is interruped, we expect the animation + // start callback to be made, with a forced pip mode change callback + mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + /** !F->F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromTarget() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) + .end() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL) + .expectStarted(SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .restart(BOUNDS_SMALLER_FLOATING, + false /* expectStartedAndPipModeChangedCallback */) + .end() + .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); + } + + /** !F->!F w/ CANCEL **/ + + @UiThreadTest + @Test + public void testFloatingToSmallerFloatingCancelFromTarget() { + mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + @UiThreadTest + @Test + public void testFloatingToLargerFloatingCancelFromTarget() { + mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING) + .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) + .update(0.25f) + .cancel() + .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); + } + + /** MISC **/ + + @UiThreadTest + @Test + public void testBoundsAreCopied() { + Rect from = new Rect(0, 0, 100, 100); + Rect to = new Rect(25, 25, 75, 75); + mDriver.start(from, to) + .update(0.25f) + .end(); + assertEquals(new Rect(0, 0, 100, 100), from); + assertEquals(new Rect(25, 25, 75, 75), to); + } + + /** + * @return whether the task and stack bounds would be the same if they were at the same offset. + */ + private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { + sTmpRect.set(taskBounds); + sTmpRect.offsetTo(stackBounds.left, stackBounds.top); + return stackBounds.equals(sTmpRect); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java new file mode 100644 index 000000000000..ee1c8dfdd319 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import org.junit.Before; +import org.junit.Test; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:DimmerTests + */ +@Presubmit +public class DimmerTests extends WindowTestsBase { + + private static class TestWindowContainer extends WindowContainer { + final SurfaceControl mControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class); + + TestWindowContainer(WindowManagerService wm) { + super(wm); + } + + @Override + public SurfaceControl getSurfaceControl() { + return mControl; + } + + @Override + public SurfaceControl.Transaction getPendingTransaction() { + return mTransaction; + } + } + + private static class MockSurfaceBuildingContainer extends WindowContainer { + final SurfaceSession mSession = new SurfaceSession(); + final SurfaceControl mHostControl = mock(SurfaceControl.class); + final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class); + + MockSurfaceBuildingContainer(WindowManagerService wm) { + super(wm); + } + + class MockSurfaceBuilder extends SurfaceControl.Builder { + MockSurfaceBuilder(SurfaceSession ss) { + super(ss); + } + + @Override + public SurfaceControl build() { + return mock(SurfaceControl.class); + } + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceBuilder(mSession); + } + + @Override + public SurfaceControl getSurfaceControl() { + return mHostControl; + } + + @Override + public SurfaceControl.Transaction getPendingTransaction() { + return mHostTransaction; + } + } + + private MockSurfaceBuildingContainer mHost; + private Dimmer mDimmer; + private SurfaceControl.Transaction mTransaction; + private Dimmer.SurfaceAnimatorStarter mSurfaceAnimatorStarter; + + private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter { + @Override + public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t, + AnimationAdapter anim, boolean hidden) { + surfaceAnimator.mAnimationFinishedCallback.run(); + } + } + + @Before + public void setUp() throws Exception { + mHost = new MockSurfaceBuildingContainer(mWm); + mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl()); + mTransaction = mock(SurfaceControl.Transaction.class); + mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter); + } + + @Test + public void testDimAboveNoChildCreatesSurface() { + final float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, alpha); + + SurfaceControl dimLayer = getDimLayer(); + + assertNotNull("Dimmer should have created a surface", dimLayer); + + verify(mTransaction).setAlpha(dimLayer, alpha); + verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE); + } + + @Test + public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() { + float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, alpha); + final SurfaceControl firstSurface = getDimLayer(); + + alpha = 0.9f; + mDimmer.dimAbove(mTransaction, alpha); + + assertEquals(firstSurface, getDimLayer()); + verify(mTransaction).setAlpha(firstSurface, 0.9f); + } + + @Test + public void testUpdateDimsAppliesCrop() { + mDimmer.dimAbove(mTransaction, 0.8f); + + int width = 100; + int height = 300; + Rect bounds = new Rect(0, 0, width, height); + mDimmer.updateDims(mTransaction, bounds); + + verify(mTransaction).setWindowCrop(getDimLayer(), width, height); + verify(mTransaction).show(getDimLayer()); + } + + @Test + public void testDimAboveNoChildNotReset() { + mDimmer.dimAbove(mTransaction, 0.8f); + SurfaceControl dimLayer = getDimLayer(); + mDimmer.resetDimStates(); + + mDimmer.updateDims(mTransaction, new Rect()); + verify(mTransaction).show(getDimLayer()); + verify(dimLayer, never()).destroy(); + } + + @Test + public void testDimAboveWithChildCreatesSurfaceAboveChild() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(); + + assertNotNull("Dimmer should have created a surface", dimLayer); + + verify(mTransaction).setAlpha(dimLayer, alpha); + verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, 1); + } + + @Test + public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final float alpha = 0.8f; + mDimmer.dimBelow(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(); + + assertNotNull("Dimmer should have created a surface", dimLayer); + + verify(mTransaction).setAlpha(dimLayer, alpha); + verify(mTransaction).setRelativeLayer(dimLayer, child.mControl, -1); + } + + @Test + public void testDimBelowWithChildSurfaceDestroyedWhenReset() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(); + mDimmer.resetDimStates(); + + mDimmer.updateDims(mTransaction, new Rect()); + verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any( + SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); + verify(mHost.getPendingTransaction()).destroy(dimLayer); + } + + @Test + public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, child, alpha); + SurfaceControl dimLayer = getDimLayer(); + mDimmer.resetDimStates(); + mDimmer.dimAbove(mTransaction, child, alpha); + + mDimmer.updateDims(mTransaction, new Rect()); + verify(mTransaction).show(dimLayer); + verify(dimLayer, never()).destroy(); + } + + @Test + public void testDimUpdateWhileDimming() { + Rect bounds = new Rect(); + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + final float alpha = 0.8f; + mDimmer.dimAbove(mTransaction, child, alpha); + + SurfaceControl dimLayer = getDimLayer(); + bounds.set(0, 0, 10, 10); + mDimmer.updateDims(mTransaction, bounds); + verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); + verify(mTransaction, times(1)).show(dimLayer); + verify(mTransaction).setPosition(dimLayer, 0, 0); + + bounds.set(10, 10, 30, 30); + mDimmer.updateDims(mTransaction, bounds); + verify(mTransaction).setWindowCrop(dimLayer, bounds.width(), bounds.height()); + verify(mTransaction).setPosition(dimLayer, 10, 10); + } + + @Test + public void testRemoveDimImmediately() { + TestWindowContainer child = new TestWindowContainer(mWm); + mHost.addChild(child, 0); + + mDimmer.dimAbove(mTransaction, child, 1); + SurfaceControl dimLayer = getDimLayer(); + mDimmer.updateDims(mTransaction, new Rect()); + verify(mTransaction, times(1)).show(dimLayer); + + reset(mSurfaceAnimatorStarter); + mDimmer.dontAnimateExit(); + mDimmer.resetDimStates(); + mDimmer.updateDims(mTransaction, new Rect()); + verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any( + SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean()); + verify(mTransaction).destroy(dimLayer); + } + + private SurfaceControl getDimLayer() { + return mDimmer.mDimState.mDimLayer; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java new file mode 100644 index 000000000000..3b8d71dd8176 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; +import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; +import static android.view.DisplayCutout.fromBoundingRect; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import android.annotation.SuppressLint; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.util.DisplayMetrics; +import android.view.DisplayCutout; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.Surface; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * Tests for the {@link DisplayContent} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:DisplayContentTests + */ +@SmallTest +@Presubmit +public class DisplayContentTests extends WindowTestsBase { + + @Test + @FlakyTest(bugId = 77772044) + public void testForAllWindows() { + final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, + mDisplayContent, "exiting app"); + final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; + exitingAppToken.mIsExiting = true; + exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken); + + assertForAllWindowsOrder(Arrays.asList( + mWallpaperWindow, + exitingAppWindow, + mChildAppWindowBelow, + mAppWindow, + mChildAppWindowAbove, + mDockedDividerWindow, + mStatusBarWindow, + mNavBarWindow, + mImeWindow, + mImeDialogWindow)); + } + + @Test + public void testForAllWindows_WithAppImeTarget() { + final WindowState imeAppTarget = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); + + mDisplayContent.mInputMethodTarget = imeAppTarget; + + assertForAllWindowsOrder(Arrays.asList( + mWallpaperWindow, + mChildAppWindowBelow, + mAppWindow, + mChildAppWindowAbove, + imeAppTarget, + mImeWindow, + mImeDialogWindow, + mDockedDividerWindow, + mStatusBarWindow, + mNavBarWindow)); + } + + @Test + public void testForAllWindows_WithChildWindowImeTarget() throws Exception { + mDisplayContent.mInputMethodTarget = mChildAppWindowAbove; + + assertForAllWindowsOrder(Arrays.asList( + mWallpaperWindow, + mChildAppWindowBelow, + mAppWindow, + mChildAppWindowAbove, + mImeWindow, + mImeDialogWindow, + mDockedDividerWindow, + mStatusBarWindow, + mNavBarWindow)); + } + + @Test + public void testForAllWindows_WithStatusBarImeTarget() throws Exception { + mDisplayContent.mInputMethodTarget = mStatusBarWindow; + + assertForAllWindowsOrder(Arrays.asList( + mWallpaperWindow, + mChildAppWindowBelow, + mAppWindow, + mChildAppWindowAbove, + mDockedDividerWindow, + mStatusBarWindow, + mImeWindow, + mImeDialogWindow, + mNavBarWindow)); + } + + @Test + public void testForAllWindows_WithInBetweenWindowToken() { + // This window is set-up to be z-ordered between some windows that go in the same token like + // the nav bar and status bar. + final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION, + mDisplayContent, "voiceInteractionWindow"); + + assertForAllWindowsOrder(Arrays.asList( + mWallpaperWindow, + mChildAppWindowBelow, + mAppWindow, + mChildAppWindowAbove, + mDockedDividerWindow, + voiceInteractionWindow, + mStatusBarWindow, + mNavBarWindow, + mImeWindow, + mImeDialogWindow)); + } + + @Test + public void testComputeImeTarget() { + // Verify that an app window can be an ime target. + final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); + appWin.setHasSurface(true); + assertTrue(appWin.canBeImeTarget()); + WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); + assertEquals(appWin, imeTarget); + appWin.mHidden = false; + + // Verify that an child window can be an ime target. + final WindowState childWin = createWindow(appWin, + TYPE_APPLICATION_ATTACHED_DIALOG, "childWin"); + childWin.setHasSurface(true); + assertTrue(childWin.canBeImeTarget()); + imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); + assertEquals(childWin, imeTarget); + } + + /** + * This tests stack movement between displays and proper stack's, task's and app token's display + * container references updates. + */ + @Test + public void testMoveStackBetweenDisplays() { + // Create a second display. + final DisplayContent dc = createNewDisplay(); + + // Add stack with activity. + final TaskStack stack = createTaskStackOnDisplay(dc); + assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId()); + assertEquals(dc, stack.getParent().getParent()); + assertEquals(dc, stack.getDisplayContent()); + + final Task task = createTaskInStack(stack, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( + dc); + task.addChild(token, 0); + assertEquals(dc, task.getDisplayContent()); + assertEquals(dc, token.getDisplayContent()); + + // Move stack to first display. + mDisplayContent.moveStackToDisplay(stack, true /* onTop */); + assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId()); + assertEquals(mDisplayContent, stack.getParent().getParent()); + assertEquals(mDisplayContent, stack.getDisplayContent()); + assertEquals(mDisplayContent, task.getDisplayContent()); + assertEquals(mDisplayContent, token.getDisplayContent()); + } + + /** + * This tests override configuration updates for display content. + */ + @Test + public void testDisplayOverrideConfigUpdate() { + final Configuration currentOverrideConfig = mDisplayContent.getOverrideConfiguration(); + + // Create new, slightly changed override configuration and apply it to the display. + final Configuration newOverrideConfig = new Configuration(currentOverrideConfig); + newOverrideConfig.densityDpi += 120; + newOverrideConfig.fontScale += 0.3; + + mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent); + + // Check that override config is applied. + assertEquals(newOverrideConfig, mDisplayContent.getOverrideConfiguration()); + } + + /** + * This tests global configuration updates when default display config is updated. + */ + @Test + public void testDefaultDisplayOverrideConfigUpdate() { + DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); + final Configuration currentConfig = defaultDisplay.getConfiguration(); + + // Create new, slightly changed override configuration and apply it to the display. + final Configuration newOverrideConfig = new Configuration(currentConfig); + newOverrideConfig.densityDpi += 120; + newOverrideConfig.fontScale += 0.3; + + mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay); + + // Check that global configuration is updated, as we've updated default display's config. + Configuration globalConfig = mWm.mRoot.getConfiguration(); + assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi); + assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); + + // Return back to original values. + mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay); + globalConfig = mWm.mRoot.getConfiguration(); + assertEquals(currentConfig.densityDpi, globalConfig.densityDpi); + assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); + } + + /** + * Tests tapping on a stack in different display results in window gaining focus. + */ + @Test + public void testInputEventBringsCorrectDisplayInFocus() { + DisplayContent dc0 = mWm.getDefaultDisplayContentLocked(); + // Create a second display + final DisplayContent dc1 = createNewDisplay(); + + // Add stack with activity. + final TaskStack stack0 = createTaskStackOnDisplay(dc0); + final Task task0 = createTaskInStack(stack0, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token = + WindowTestUtils.createTestAppWindowToken(dc0); + task0.addChild(token, 0); + dc0.configureDisplayPolicy(); + assertNotNull(dc0.mTapDetector); + + final TaskStack stack1 = createTaskStackOnDisplay(dc1); + final Task task1 = createTaskInStack(stack1, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken token1 = + WindowTestUtils.createTestAppWindowToken(dc0); + task1.addChild(token1, 0); + dc1.configureDisplayPolicy(); + assertNotNull(dc1.mTapDetector); + + // tap on primary display. + tapOnDisplay(dc0); + // Check focus is on primary display. + assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, + dc0.findFocusedWindow()); + + // Tap on secondary display. + tapOnDisplay(dc1); + // Check focus is on secondary. + assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus, + dc1.findFocusedWindow()); + } + + @Test + public void testFocusedWindowMultipleDisplays() { + // Create a focusable window and check that focus is calculated correctly + final WindowState window1 = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1"); + updateFocusedWindow(); + assertTrue(window1.isFocused()); + assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); + + // Check that a new display doesn't affect focus + final DisplayContent dc = createNewDisplay(); + updateFocusedWindow(); + assertTrue(window1.isFocused()); + assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); + + // Add a window to the second display, and it should be focused + final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2"); + updateFocusedWindow(); + assertTrue(window1.isFocused()); + assertTrue(window2.isFocused()); + assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); + + // Move the first window to the to including parents, and make sure focus is updated + window1.getParent().positionChildAt(POSITION_TOP, window1, true); + updateFocusedWindow(); + assertTrue(window1.isFocused()); + assertTrue(window2.isFocused()); + assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); + } + + /** + * This tests setting the maximum ui width on a display. + */ + @Test + public void testMaxUiWidth() { + // Prevent base display metrics for test from being updated to the value of real display. + final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); + final int baseWidth = 1440; + final int baseHeight = 2560; + final int baseDensity = 300; + + displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); + + final int maxWidth = 300; + final int resultingHeight = (maxWidth * baseHeight) / baseWidth; + final int resultingDensity = (maxWidth * baseDensity) / baseWidth; + + displayContent.setMaxUiWidth(maxWidth); + verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); + + // Assert setting values again does not change; + displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity); + verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity); + + final int smallerWidth = 200; + final int smallerHeight = 400; + final int smallerDensity = 100; + + // Specify smaller dimension, verify that it is honored + displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity); + verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); + + // Verify that setting the max width to a greater value than the base width has no effect + displayContent.setMaxUiWidth(maxWidth); + verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity); + } + + @Test + public void testDisplayCutout_rot0() { + synchronized (mWm.getWindowManagerLock()) { + final DisplayContent dc = createNewDisplay(); + dc.mInitialDisplayWidth = 200; + dc.mInitialDisplayHeight = 400; + Rect r = new Rect(80, 0, 120, 10); + final DisplayCutout cutout = new WmDisplayCutout( + fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null) + .computeSafeInsets(200, 400).getDisplayCutout(); + + dc.mInitialDisplayCutout = cutout; + dc.setRotation(Surface.ROTATION_0); + dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. + + assertEquals(cutout, dc.getDisplayInfo().displayCutout); + } + } + + @Test + public void testDisplayCutout_rot90() { + synchronized (mWm.getWindowManagerLock()) { + // Prevent mInitialDisplayCutout from being updated from real display (e.g. null + // if the device has no cutout). + final DisplayContent dc = createDisplayNoUpdateDisplayInfo(); + // Rotation may use real display info to compute bound, so here also uses the + // same width and height. + final int displayWidth = dc.mInitialDisplayWidth; + final int displayHeight = dc.mInitialDisplayHeight; + final int cutoutWidth = 40; + final int cutoutHeight = 10; + final int left = (displayWidth - cutoutWidth) / 2; + final int top = 0; + final int right = (displayWidth + cutoutWidth) / 2; + final int bottom = cutoutHeight; + + final Rect r1 = new Rect(left, top, right, bottom); + final DisplayCutout cutout = new WmDisplayCutout( + fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), + null) + .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); + + dc.mInitialDisplayCutout = cutout; + dc.setRotation(Surface.ROTATION_90); + dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. + + // ----o---------- ------------- + // | | | | | + // | ------o | o--- + // | | | | + // | | -> | | + // | | ---o + // | | | + // | | ------------- + final Rect r = new Rect(top, left, bottom, right); + assertEquals(new WmDisplayCutout( + fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null) + .computeSafeInsets(displayHeight, displayWidth) + .getDisplayCutout(), dc.getDisplayInfo().displayCutout); + } + } + + @Test + public void testLayoutSeq_assignedDuringLayout() { + synchronized (mWm.getWindowManagerLock()) { + + final DisplayContent dc = createNewDisplay(); + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); + + dc.setLayoutNeeded(); + dc.performLayout(true /* initial */, false /* updateImeWindows */); + + assertThat(win.mLayoutSeq, is(dc.mLayoutSeq)); + } + } + + @Test + @SuppressLint("InlinedApi") + public void testOrientationDefinedByKeyguard() { + final DisplayContent dc = createNewDisplay(); + // Create a window that requests landscape orientation. It will define device orientation + // by default. + final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); + window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard"); + keyguard.mHasSurface = true; + keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; + + assertEquals("Screen orientation must be defined by the app window by default", + SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); + + keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; + assertEquals("Visible keyguard must influence device orientation", + SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); + + mWm.setKeyguardGoingAway(true); + assertEquals("Keyguard that is going away must not influence device orientation", + SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); + } + + @Test + public void testDisableDisplayInfoOverrideFromWindowManager() { + final DisplayContent dc = createNewDisplay(); + + assertTrue(dc.mShouldOverrideDisplayConfiguration); + mWm.dontOverrideDisplayInfo(dc.getDisplayId()); + + assertFalse(dc.mShouldOverrideDisplayConfiguration); + verify(mWm.mDisplayManagerInternal, times(1)) + .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null); + } + + @Test + public void testClearLastFocusWhenReparentingFocusedWindow() { + final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked(); + final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + defaultDisplay, "window"); + defaultDisplay.mLastFocus = window; + mDisplayContent.mCurrentFocus = window; + mDisplayContent.reParentWindowToken(window.mToken); + + assertNull(defaultDisplay.mLastFocus); + } + + @Test + public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() { + final DisplayContent portraitDisplay = createNewDisplay(); + portraitDisplay.mInitialDisplayHeight = 2000; + portraitDisplay.mInitialDisplayWidth = 1000; + + portraitDisplay.setRotation(Surface.ROTATION_0); + assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); + portraitDisplay.setRotation(Surface.ROTATION_90); + assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); + + final DisplayContent landscapeDisplay = createNewDisplay(); + landscapeDisplay.mInitialDisplayHeight = 1000; + landscapeDisplay.mInitialDisplayWidth = 2000; + + landscapeDisplay.setRotation(Surface.ROTATION_0); + assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); + landscapeDisplay.setRotation(Surface.ROTATION_90); + assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); + } + + @Test + public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() { + final DisplayContent newDisplay = createNewDisplay(); + + final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); + final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); + appWin.setHasSurface(true); + appWin1.setHasSurface(true); + + // Set current input method window on default display, make sure the input method target + // is appWin & null on the other display. + mDisplayContent.setInputMethodWindowLocked(mImeWindow); + newDisplay.setInputMethodWindowLocked(null); + assertTrue("appWin should be IME target window", + appWin.equals(mDisplayContent.mInputMethodTarget)); + assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget); + + // Switch input method window on new display & make sure the input method target also + // switched as expected. + newDisplay.setInputMethodWindowLocked(mImeWindow); + mDisplayContent.setInputMethodWindowLocked(null); + assertTrue("appWin1 should be IME target window", + appWin1.equals(newDisplay.mInputMethodTarget)); + assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget); + } + + private boolean isOptionsPanelAtRight(int displayId) { + return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; + } + + private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, + int expectedBaseHeight, int expectedBaseDensity) { + assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth); + assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight); + assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity); + } + + private void updateFocusedWindow() { + synchronized (mWm.mGlobalLock) { + mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false); + } + } + + /** + * Create DisplayContent that does not update display base/initial values from device to keep + * the values set by test. + */ + private DisplayContent createDisplayNoUpdateDisplayInfo() { + final DisplayContent displayContent = spy(createNewDisplay()); + doNothing().when(displayContent).updateDisplayInfo(); + return displayContent; + } + + private void assertForAllWindowsOrder(List expectedWindowsBottomToTop) { + final LinkedList actualWindows = new LinkedList<>(); + + // Test forward traversal. + mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */); + assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop)); + + actualWindows.clear(); + + // Test backward traversal. + mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */); + assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); + } + + private static List reverseList(List list) { + final ArrayList result = new ArrayList<>(list); + Collections.reverse(result); + return result; + } + + private void tapOnDisplay(final DisplayContent dc) { + final DisplayMetrics dm = dc.getDisplayMetrics(); + final float x = dm.widthPixels / 2; + final float y = dm.heightPixels / 2; + final long downTime = SystemClock.uptimeMillis(); + final long eventTime = SystemClock.uptimeMillis() + 100; + // sending ACTION_DOWN + final MotionEvent downEvent = MotionEvent.obtain( + downTime, + downTime, + MotionEvent.ACTION_DOWN, + x, + y, + 0 /*metaState*/); + downEvent.setDisplayId(dc.getDisplayId()); + dc.mTapDetector.onPointerEvent(downEvent); + + // sending ACTION_UP + final MotionEvent upEvent = MotionEvent.obtain( + downTime, + eventTime, + MotionEvent.ACTION_UP, + x, + y, + 0 /*metaState*/); + upEvent.setDisplayId(dc.getDisplayId()); + dc.mTapDetector.onPointerEvent(upEvent); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java new file mode 100644 index 000000000000..6767465f838c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static org.hamcrest.Matchers.equalTo; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; + +@SmallTest +@Presubmit +public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase { + + @Rule + public final ErrorCollector mErrorCollector = new ErrorCollector(); + + @Test + public void portrait() { + final DisplayInfo di = displayInfoForRotation(ROTATION_0, false /* withCutout */); + + verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); + verifyConsistency(di); + } + + @Test + public void portrait_withCutout() { + final DisplayInfo di = displayInfoForRotation(ROTATION_0, true /* withCutout */); + + verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + verifyNonDecorInsets(di, 0, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT); + verifyConsistency(di); + } + + @Test + public void landscape() { + final DisplayInfo di = displayInfoForRotation(ROTATION_90, false /* withCutout */); + + verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + verifyNonDecorInsets(di, 0, 0, NAV_BAR_HEIGHT, 0); + verifyConsistency(di); + } + + @Test + public void landscape_withCutout() { + final DisplayInfo di = displayInfoForRotation(ROTATION_90, true /* withCutout */); + + verifyStableInsets(di, DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + verifyNonDecorInsets(di, DISPLAY_CUTOUT_HEIGHT, 0, NAV_BAR_HEIGHT, 0); + verifyConsistency(di); + } + + @Test + public void seascape() { + final DisplayInfo di = displayInfoForRotation(ROTATION_270, false /* withCutout */); + + verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, 0, 0); + verifyConsistency(di); + } + + @Test + public void seascape_withCutout() { + final DisplayInfo di = displayInfoForRotation(ROTATION_270, true /* withCutout */); + + verifyStableInsets(di, NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); + verifyNonDecorInsets(di, NAV_BAR_HEIGHT, 0, DISPLAY_CUTOUT_HEIGHT, 0); + verifyConsistency(di); + } + + @Test + public void upsideDown() { + final DisplayInfo di = displayInfoForRotation(ROTATION_180, false /* withCutout */); + + verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT); + verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT); + verifyConsistency(di); + } + + @Test + public void upsideDown_withCutout() { + final DisplayInfo di = displayInfoForRotation(ROTATION_180, true /* withCutout */); + + verifyStableInsets(di, 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); + verifyNonDecorInsets(di, 0, 0, 0, NAV_BAR_HEIGHT + DISPLAY_CUTOUT_HEIGHT); + verifyConsistency(di); + } + + private void verifyStableInsets(DisplayInfo di, int left, int top, int right, int bottom) { + mErrorCollector.checkThat("stableInsets", getStableInsetsLw(di), equalTo(new Rect( + left, top, right, bottom))); + } + + private void verifyNonDecorInsets(DisplayInfo di, int left, int top, int right, int bottom) { + mErrorCollector.checkThat("nonDecorInsets", getNonDecorInsetsLw(di), equalTo(new Rect( + left, top, right, bottom))); + } + + private void verifyConsistency(DisplayInfo di) { + verifyConsistency("configDisplay", di, getStableInsetsLw(di), + getConfigDisplayWidth(di), getConfigDisplayHeight(di)); + verifyConsistency("nonDecorDisplay", di, getNonDecorInsetsLw(di), + getNonDecorDisplayWidth(di), getNonDecorDisplayHeight(di)); + } + + private void verifyConsistency(String what, DisplayInfo di, Rect insets, int width, + int height) { + mErrorCollector.checkThat(what + ":width", width, + equalTo(di.logicalWidth - insets.left - insets.right)); + mErrorCollector.checkThat(what + ":height", height, + equalTo(di.logicalHeight - insets.top - insets.bottom)); + } + + private Rect getStableInsetsLw(DisplayInfo di) { + Rect result = new Rect(); + mDisplayPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + di.displayCutout, result); + return result; + } + + private Rect getNonDecorInsetsLw(DisplayInfo di) { + Rect result = new Rect(); + mDisplayPolicy.getNonDecorInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + di.displayCutout, result); + return result; + } + + private int getNonDecorDisplayWidth(DisplayInfo di) { + return mDisplayPolicy.getNonDecorDisplayWidth(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); + } + + private int getNonDecorDisplayHeight(DisplayInfo di) { + return mDisplayPolicy.getNonDecorDisplayHeight(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); + } + + private int getConfigDisplayWidth(DisplayInfo di) { + return mDisplayPolicy.getConfigDisplayWidth(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); + } + + private int getConfigDisplayHeight(DisplayInfo di) { + return mDisplayPolicy.getConfigDisplayHeight(di.logicalWidth, di.logicalHeight, + di.rotation, 0 /* ui */, di.displayCutout); + } + + private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { + return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java new file mode 100644 index 000000000000..b94f472965ab --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; +import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; +import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; + +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.util.Pair; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Before; +import org.junit.Test; + +@SmallTest +@Presubmit +public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { + + private DisplayFrames mFrames; + private WindowState mWindow; + private int mRotation = ROTATION_0; + private boolean mHasDisplayCutout; + + @Before + public void setUp() throws Exception { + updateDisplayFrames(); + + mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); + // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from + // changing those frames. + doNothing().when(mWindow).computeFrameLw(); + + final WindowManager.LayoutParams attrs = mWindow.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + attrs.format = PixelFormat.TRANSLUCENT; + } + + public void setRotation(int rotation) { + mRotation = rotation; + updateDisplayFrames(); + } + + public void addDisplayCutout() { + mHasDisplayCutout = true; + updateDisplayFrames(); + } + + private void updateDisplayFrames() { + final Pair info = displayInfoAndCutoutForRotation(mRotation, + mHasDisplayCutout); + mFrames = new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second); + } + + @Test + public void layoutWindowLw_appDrawsBars() { + mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_appWontDrawBars() { + mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); + } + + @Test + public void layoutWindowLw_appWontDrawBars_forceStatus() throws Exception { + mWindow.mAttrs.flags &= ~FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT); + } + + @Test + public void addingWindow_doesNotTamperWithSysuiFlags() { + mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + addWindow(mWindow); + + assertEquals(0, mWindow.mAttrs.systemUiVisibility); + assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility); + } + + @Test + public void layoutWindowLw_withDisplayCutout() { + addDisplayCutout(); + + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_never() { + addDisplayCutout(); + + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_layoutFullscreen() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() { + addDisplayCutout(); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN; + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0); + } + + + @Test + public void layoutWindowLw_withDisplayCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_seascape() { + addDisplayCutout(); + setRotation(ROTATION_270); + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0); + assertInsetBy(mWindow.getContentFrameLw(), + NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + } + + @Test + public void layoutWindowLw_withDisplayCutout_floatingInScreen() { + addDisplayCutout(); + + mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN; + mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY; + mWindow.mAttrs.width = DISPLAY_WIDTH; + mWindow.mAttrs.height = DISPLAY_HEIGHT; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT); + assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT); + } + + @Test + public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() { + addDisplayCutout(); + setRotation(ROTATION_90); + + mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + addWindow(mWindow); + + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames); + + assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0); + assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getContentFrameLw(), + DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0); + assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0); + } + + @Test + public void layoutHint_appWindow() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, null, mFrames, + false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(mFrames.mUnrestricted)); + assertThat(outContentInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); + assertThat(outStableInsets, is(new Rect(0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT))); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + @Test + public void layoutHint_appWindowInTask() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + final Rect taskBounds = new Rect(100, 100, 200, 200); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, + false /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(taskBounds)); + assertThat(outContentInsets, is(new Rect())); + assertThat(outStableInsets, is(new Rect())); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + @Test + public void layoutHint_appWindowInTask_outsideContentFrame() { + // Initialize DisplayFrames + mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); + + // Task is in the nav bar area (usually does not happen, but this is similar enough to the + // possible overlap with the IME) + final Rect taskBounds = new Rect(100, mFrames.mContent.bottom + 1, + 200, mFrames.mContent.bottom + 10); + + final Rect outFrame = new Rect(); + final Rect outContentInsets = new Rect(); + final Rect outStableInsets = new Rect(); + final Rect outOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper outDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + + mDisplayPolicy.getLayoutHintLw(mWindow.mAttrs, taskBounds, mFrames, + true /* floatingStack */, outFrame, outContentInsets, outStableInsets, outOutsets, + outDisplayCutout); + + assertThat(outFrame, is(taskBounds)); + assertThat(outContentInsets, is(new Rect())); + assertThat(outStableInsets, is(new Rect())); + assertThat(outOutsets, is(new Rect())); + assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper())); + } + + /** + * Asserts that {@code actual} is inset by the given amounts from the full display rect. + * + * Convenience wrapper for when only the top and bottom inset are non-zero. + */ + private void assertInsetByTopBottom(Rect actual, int expectedInsetTop, + int expectedInsetBottom) { + assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); + } + + /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ + private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, + int expectedInsetRight, int expectedInsetBottom) { + assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, + mFrames.mDisplayWidth - expectedInsetRight, + mFrames.mDisplayHeight - expectedInsetBottom), actual); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java new file mode 100644 index 000000000000..8349ac7fc62c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; + +import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; +import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.graphics.PixelFormat; +import android.platform.test.annotations.Presubmit; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@Presubmit +public class DisplayPolicyTests extends WindowTestsBase { + + private WindowState createOpaqueFullscreen(boolean hasLightNavBar) { + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "opaqueFullscreen"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = + FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + attrs.format = PixelFormat.OPAQUE; + attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = + hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; + return win; + } + + private WindowState createDimmingDialogWindow(boolean canBeImTarget) { + final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog")); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = WRAP_CONTENT; + attrs.height = WRAP_CONTENT; + attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM); + attrs.format = PixelFormat.TRANSLUCENT; + when(win.isDimming()).thenReturn(true); + return win; + } + + private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar, + boolean hasLightNavBar) { + final WindowState win = createWindow(null, TYPE_INPUT_METHOD, "inputMethod"); + final WindowManager.LayoutParams attrs = win.mAttrs; + attrs.width = MATCH_PARENT; + attrs.height = MATCH_PARENT; + attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN + | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0); + attrs.format = PixelFormat.TRANSPARENT; + attrs.systemUiVisibility = attrs.subtreeSystemUiVisibility = win.mSystemUiVisibility = + hasLightNavBar ? SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR : 0; + win.mHasSurface = visible; + return win; + } + + @Test + public void testChooseNavigationColorWindowLw() { + final WindowState opaque = createOpaqueFullscreen(false); + + final WindowState dimmingImTarget = createDimmingDialogWindow(true); + final WindowState dimmingNonImTarget = createDimmingDialogWindow(false); + + final WindowState visibleIme = createInputMethodWindow(true, true, false); + final WindowState invisibleIme = createInputMethodWindow(false, true, false); + final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false); + + // If everything is null, return null + assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw( + null, null, null, NAV_BAR_BOTTOM)); + + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, null, NAV_BAR_BOTTOM)); + assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, null, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, null, NAV_BAR_BOTTOM)); + + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + null, null, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + null, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + null, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, visibleIme, NAV_BAR_BOTTOM)); + + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, invisibleIme, NAV_BAR_BOTTOM)); + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, visibleIme, NAV_BAR_RIGHT)); + + // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color + // window. + assertEquals(opaque, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, opaque, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw( + opaque, dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); + } + + @Test + public void testUpdateLightNavigationBarLw() { + final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false); + final WindowState opaqueLightNavBar = createOpaqueFullscreen(true); + + final WindowState dimming = createDimmingDialogWindow(false); + + final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false); + final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true); + + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, + null, null)); + + // Opaque top fullscreen window overrides SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR flag. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueDarkNavBar, opaqueDarkNavBar, null, opaqueDarkNavBar)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, opaqueDarkNavBar, null, + opaqueDarkNavBar)); + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(0, opaqueLightNavBar, + opaqueLightNavBar, null, opaqueLightNavBar)); + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + opaqueLightNavBar, opaqueLightNavBar, null, opaqueLightNavBar)); + + // Dimming window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueDarkNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + 0, opaqueLightNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueDarkNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, null, dimming)); + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, dimming, imeDrawLightNavBar, + dimming)); + + // IME window clears SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, null, null, imeDrawDarkNavBar, + imeDrawDarkNavBar)); + + // Even if the top fullscreen has SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, IME window wins. + assertEquals(0, DisplayPolicy.updateLightNavigationBarLw( + SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, opaqueLightNavBar, opaqueLightNavBar, + imeDrawDarkNavBar, imeDrawDarkNavBar)); + + // IME window should be able to use SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR. + assertEquals(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, + DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar, + opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java new file mode 100644 index 000000000000..1d63c57e6cfe --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.util.DisplayMetrics.DENSITY_DEFAULT; +import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; +import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; +import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; +import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_180; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.IBinder; +import android.testing.TestableResources; +import android.util.Pair; +import android.view.DisplayCutout; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManagerGlobal; + +import com.android.internal.R; +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Before; + +public class DisplayPolicyTestsBase extends WindowTestsBase { + + static final int DISPLAY_WIDTH = 500; + static final int DISPLAY_HEIGHT = 1000; + static final int DISPLAY_DENSITY = 320; + + static final int STATUS_BAR_HEIGHT = 10; + static final int NAV_BAR_HEIGHT = 15; + static final int DISPLAY_CUTOUT_HEIGHT = 8; + + DisplayPolicy mDisplayPolicy; + + @Before + public void setUpBase() { + super.setUpBase(); + mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy()); + + final TestContextWrapper context = + new TestContextWrapper(mDisplayPolicy.getSystemUiContext()); + final TestableResources resources = context.getResourceMocker(); + resources.addOverride(R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT); + resources.addOverride(R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); + resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); + when(mDisplayPolicy.getSystemUiContext()).thenReturn(context); + when(mDisplayPolicy.hasNavigationBar()).thenReturn(true); + + final int shortSizeDp = + Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; + final int longSizeDp = + Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; + mDisplayContent.getDisplayRotation().configure( + DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp, longSizeDp); + mDisplayPolicy.configure(DISPLAY_WIDTH, DISPLAY_HEIGHT, shortSizeDp); + mDisplayPolicy.onConfigurationChanged(); + + mStatusBarWindow.mAttrs.gravity = Gravity.TOP; + addWindow(mStatusBarWindow); + mDisplayPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT; + + mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; + addWindow(mNavBarWindow); + mDisplayPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; + } + + void addWindow(WindowState win) { + mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, true /* hasStatusBarPermission */); + assertEquals(WindowManagerGlobal.ADD_OKAY, + mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs)); + win.mHasSurface = true; + } + + static Pair displayInfoAndCutoutForRotation(int rotation, + boolean withDisplayCutout) { + final DisplayInfo info = new DisplayInfo(); + WmDisplayCutout cutout = null; + + final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; + info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + info.rotation = rotation; + if (withDisplayCutout) { + cutout = WmDisplayCutout.computeSafeInsets( + displayCutoutForRotation(rotation), info.logicalWidth, + info.logicalHeight); + info.displayCutout = cutout.getDisplayCutout(); + } else { + info.displayCutout = null; + } + return Pair.create(info, cutout); + } + + private static DisplayCutout displayCutoutForRotation(int rotation) { + final RectF rectF = + new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT); + + final Matrix m = new Matrix(); + transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); + m.mapRect(rectF); + + int pos = -1; + switch (rotation) { + case ROTATION_0: + pos = BOUNDS_POSITION_TOP; + break; + case ROTATION_90: + pos = BOUNDS_POSITION_LEFT; + break; + case ROTATION_180: + pos = BOUNDS_POSITION_BOTTOM; + break; + case ROTATION_270: + pos = BOUNDS_POSITION_RIGHT; + break; + } + + return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top, + (int) rectF.right, (int) rectF.bottom, pos); + } + + static class TestContextWrapper extends ContextWrapper { + private final TestableResources mResourceMocker; + + TestContextWrapper(Context targetContext) { + super(targetContext); + mResourceMocker = new TestableResources(targetContext.getResources()); + } + + @Override + public int checkPermission(String permission, int pid, int uid) { + return PackageManager.PERMISSION_GRANTED; + } + + @Override + public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { + return PackageManager.PERMISSION_GRANTED; + } + + @Override + public Resources getResources() { + return mResourceMocker.getResources(); + } + + TestableResources getResourceMocker() { + return mResourceMocker; + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java new file mode 100644 index 000000000000..8e881b54c422 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY; +import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +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.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Matchers.eq; + +import android.app.WindowConfiguration; +import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.Surface; + +import androidx.test.filters.SmallTest; + +import com.android.server.policy.WindowManagerPolicy; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +/** + * Tests for the {@link DisplayWindowSettings} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:DisplayWindowSettingsTests + */ +@SmallTest +@Presubmit +public class DisplayWindowSettingsTests extends WindowTestsBase { + + private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); + private DisplayWindowSettings mTarget; + + DisplayInfo mPrivateDisplayInfo; + + private DisplayContent mPrimaryDisplay; + private DisplayContent mSecondaryDisplay; + private DisplayContent mPrivateDisplay; + + @Before + public void setUp() throws Exception { + deleteRecursively(TEST_FOLDER); + + mWm.setSupportsFreeformWindowManagement(false); + mWm.setIsPc(false); + mWm.setForceDesktopModeOnExternalDisplays(false); + + mTarget = new DisplayWindowSettings(mWm, TEST_FOLDER); + + mPrimaryDisplay = mWm.getDefaultDisplayContentLocked(); + mSecondaryDisplay = mDisplayContent; + assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId()); + + mPrivateDisplayInfo = new DisplayInfo(mDisplayInfo); + mPrivateDisplayInfo.flags |= Display.FLAG_PRIVATE; + mPrivateDisplay = createNewDisplay(mPrivateDisplayInfo); + assertNotEquals(Display.DEFAULT_DISPLAY, mPrivateDisplay.getDisplayId()); + assertNotEquals(mSecondaryDisplay.getDisplayId(), mPrivateDisplay.getDisplayId()); + } + + @After + public void tearDown() { + deleteRecursively(TEST_FOLDER); + } + + @Test + public void testPrimaryDisplayDefaultToFullscreen_NoFreeformSupport() { + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() { + mWm.setSupportsFreeformWindowManagement(true); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() { + mWm.setSupportsFreeformWindowManagement(true); + mWm.setForceDesktopModeOnExternalDisplays(true); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() { + mWm.setSupportsFreeformWindowManagement(true); + mWm.setIsPc(true); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mPrimaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFullscreen_NoFreeformSupport() { + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() { + mWm.setSupportsFreeformWindowManagement(true); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() { + mWm.setSupportsFreeformWindowManagement(true); + mWm.setForceDesktopModeOnExternalDisplays(true); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() { + mWm.setSupportsFreeformWindowManagement(true); + mWm.setIsPc(true); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM, + mSecondaryDisplay.getWindowingMode()); + } + + @Test + public void testDefaultToOriginalMetrics() { + final int originalWidth = mSecondaryDisplay.mBaseDisplayWidth; + final int originalHeight = mSecondaryDisplay.mBaseDisplayHeight; + final int originalDensity = mSecondaryDisplay.mBaseDisplayDensity; + final boolean originalScalingDisabled = mSecondaryDisplay.mDisplayScalingDisabled; + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(originalWidth, mSecondaryDisplay.mBaseDisplayWidth); + assertEquals(originalHeight, mSecondaryDisplay.mBaseDisplayHeight); + assertEquals(originalDensity, mSecondaryDisplay.mBaseDisplayDensity); + assertEquals(originalScalingDisabled, mSecondaryDisplay.mDisplayScalingDisabled); + } + + @Test + public void testSetForcedSize() { + final DisplayInfo originalInfo = new DisplayInfo(mSecondaryDisplay.getDisplayInfo()); + // Provides the orginal display info to avoid changing initial display size. + doAnswer(invocation -> { + ((DisplayInfo) invocation.getArguments()[1]).copyFrom(originalInfo); + return null; + }).when(mWm.mDisplayManagerInternal).getNonOverrideDisplayInfo(anyInt(), any()); + + mTarget.setForcedSize(mSecondaryDisplay, 1000 /* width */, 2000 /* height */); + applySettingsToDisplayByNewInstance(mSecondaryDisplay); + + assertEquals(1000 /* width */, mSecondaryDisplay.mBaseDisplayWidth); + assertEquals(2000 /* height */, mSecondaryDisplay.mBaseDisplayHeight); + + mWm.clearForcedDisplaySize(mSecondaryDisplay.getDisplayId()); + assertEquals(mSecondaryDisplay.mInitialDisplayWidth, mSecondaryDisplay.mBaseDisplayWidth); + assertEquals(mSecondaryDisplay.mInitialDisplayHeight, mSecondaryDisplay.mBaseDisplayHeight); + } + + @Test + public void testSetForcedDensity() { + mTarget.setForcedDensity(mSecondaryDisplay, 600 /* density */, 0 /* userId */); + applySettingsToDisplayByNewInstance(mSecondaryDisplay); + + assertEquals(600 /* density */, mSecondaryDisplay.mBaseDisplayDensity); + + mWm.clearForcedDisplayDensityForUser(mSecondaryDisplay.getDisplayId(), 0 /* userId */); + assertEquals(mSecondaryDisplay.mInitialDisplayDensity, + mSecondaryDisplay.mBaseDisplayDensity); + } + + @Test + public void testSetForcedScalingMode() { + mTarget.setForcedScalingMode(mSecondaryDisplay, DisplayContent.FORCE_SCALING_MODE_DISABLED); + applySettingsToDisplayByNewInstance(mSecondaryDisplay); + + assertTrue(mSecondaryDisplay.mDisplayScalingDisabled); + + mWm.setForcedDisplayScalingMode(mSecondaryDisplay.getDisplayId(), + DisplayContent.FORCE_SCALING_MODE_AUTO); + assertFalse(mSecondaryDisplay.mDisplayScalingDisabled); + } + + @Test + public void testDefaultToZeroOverscan() { + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */); + } + + @Test + public void testPersistOverscanInSameInstance() { + final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); + mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + } + + @Test + public void testPersistOverscanAcrossInstances() { + final DisplayInfo info = mPrimaryDisplay.getDisplayInfo(); + mTarget.setOverscanLocked(info, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + + applySettingsToDisplayByNewInstance(mPrimaryDisplay); + + assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */); + } + + @Test + public void testDefaultToFreeUserRotation() { + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); + assertEquals(WindowManagerPolicy.USER_ROTATION_FREE, rotation.getUserRotationMode()); + assertFalse(rotation.isRotationFrozen()); + } + + @Test + public void testDefaultTo0DegRotation() { + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(Surface.ROTATION_0, mSecondaryDisplay.getDisplayRotation().getUserRotation()); + } + + @Test + public void testPrivateDisplayDefaultToDestroyContent() { + assertEquals(REMOVE_CONTENT_MODE_DESTROY, + mTarget.getRemoveContentModeLocked(mPrivateDisplay)); + } + + @Test + public void testPublicDisplayDefaultToMoveToPrimary() { + assertEquals(REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY, + mTarget.getRemoveContentModeLocked(mSecondaryDisplay)); + } + + @Test + public void testDefaultToNotShowWithInsecureKeyguard() { + assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mPrivateDisplay)); + assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mSecondaryDisplay)); + } + + @Test + public void testPublicDisplayNotAllowSetShouldShowWithInsecureKeyguard() { + mTarget.setShouldShowWithInsecureKeyguardLocked(mSecondaryDisplay, true); + + assertFalse(mTarget.shouldShowWithInsecureKeyguardLocked(mSecondaryDisplay)); + } + + @Test + public void testPrivateDisplayAllowSetShouldShowWithInsecureKeyguard() { + mTarget.setShouldShowWithInsecureKeyguardLocked(mPrivateDisplay, true); + + assertTrue(mTarget.shouldShowWithInsecureKeyguardLocked(mPrivateDisplay)); + } + + @Test + public void testPrimaryDisplayShouldShowSystemDecors() { + assertTrue(mTarget.shouldShowSystemDecorsLocked(mPrimaryDisplay)); + + mTarget.setShouldShowSystemDecorsLocked(mPrimaryDisplay, false); + + // Default display should show system decors + assertTrue(mTarget.shouldShowSystemDecorsLocked(mPrimaryDisplay)); + } + + @Test + public void testSecondaryDisplayDefaultToNotShowSystemDecors() { + assertFalse(mTarget.shouldShowSystemDecorsLocked(mSecondaryDisplay)); + } + + @Test + public void testPrimaryDisplayShouldShowIme() { + assertTrue(mTarget.shouldShowImeLocked(mPrimaryDisplay)); + + mTarget.setShouldShowImeLocked(mPrimaryDisplay, false); + + assertTrue(mTarget.shouldShowImeLocked(mPrimaryDisplay)); + } + + @Test + public void testSecondaryDisplayDefaultToNotShowIme() { + assertFalse(mTarget.shouldShowImeLocked(mSecondaryDisplay)); + } + + @Test + public void testPersistUserRotationModeInSameInstance() { + mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_90); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode()); + assertTrue(rotation.isRotationFrozen()); + } + + @Test + public void testPersistUserRotationInSameInstance() { + mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_90); + + mTarget.applySettingsToDisplayLocked(mSecondaryDisplay); + + assertEquals(Surface.ROTATION_90, mSecondaryDisplay.getDisplayRotation().getUserRotation()); + } + + @Test + public void testPersistUserRotationModeAcrossInstances() { + mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_270); + + applySettingsToDisplayByNewInstance(mSecondaryDisplay); + + final DisplayRotation rotation = mSecondaryDisplay.getDisplayRotation(); + assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, rotation.getUserRotationMode()); + assertTrue(rotation.isRotationFrozen()); + } + + @Test + public void testPersistUserRotationAcrossInstances() { + mTarget.setUserRotation(mSecondaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_270); + + applySettingsToDisplayByNewInstance(mSecondaryDisplay); + + assertEquals(Surface.ROTATION_270, + mSecondaryDisplay.getDisplayRotation().getUserRotation()); + } + + @Test + public void testNotFixedToUserRotationByDefault() { + mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, + Surface.ROTATION_0); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false)); + } + + @Test + public void testSetFixedToUserRotation() { + mTarget.setFixedToUserRotation(mPrimaryDisplay, true); + + final DisplayRotation displayRotation = mock(DisplayRotation.class); + mPrimaryDisplay = spy(mPrimaryDisplay); + when(mPrimaryDisplay.getDisplayRotation()).thenReturn(displayRotation); + + applySettingsToDisplayByNewInstance(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true)); + } + + private static void assertOverscan(DisplayContent display, int left, int top, int right, + int bottom) { + final DisplayInfo info = display.getDisplayInfo(); + + assertEquals(left, info.overscanLeft); + assertEquals(top, info.overscanTop); + assertEquals(right, info.overscanRight); + assertEquals(bottom, info.overscanBottom); + } + + /** + * This method helps to ensure read and write persistent settings successfully because the + * constructor of {@link DisplayWindowSettings} should read the persistent file from the given + * path that also means the previous state must be written correctly. + */ + private void applySettingsToDisplayByNewInstance(DisplayContent display) { + new DisplayWindowSettings(mWm, TEST_FOLDER).applySettingsToDisplayLocked(display); + } + + private static boolean deleteRecursively(File file) { + boolean fullyDeleted = true; + if (file.isFile()) { + return file.delete(); + } else if (file.isDirectory()) { + final File[] files = file.listFiles(); + for (File child : files) { + fullyDeleted &= deleteRecursively(child); + } + fullyDeleted &= file.delete(); + } + return fullyDeleted; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java new file mode 100644 index 000000000000..32062080a22c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DockedStackDividerControllerTests.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.WindowManager.DOCKED_BOTTOM; +import static android.view.WindowManager.DOCKED_LEFT; +import static android.view.WindowManager.DOCKED_RIGHT; +import static android.view.WindowManager.DOCKED_TOP; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; +import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@Presubmit +public class DockedStackDividerControllerTests { + + @Test + public void testIsDockSideAllowedDockTop() { + // Docked top is always allowed + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, + NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_TOP, DOCKED_LEFT, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + } + + @Test + public void testIsDockSideAllowedDockBottom() { + // Cannot dock bottom + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_BOTTOM, DOCKED_LEFT, + NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); + } + + @Test + public void testIsDockSideAllowedNavigationBarMovable() { + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, + NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, + NAV_BAR_LEFT, true /* navigationBarCanMove */)); + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, + NAV_BAR_RIGHT, true /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, + NAV_BAR_BOTTOM, true /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, + NAV_BAR_RIGHT, true /* navigationBarCanMove */)); + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, + NAV_BAR_LEFT, true /* navigationBarCanMove */)); + } + + @Test + public void testIsDockSideAllowedNavigationBarNotMovable() { + // Navigation bar is not movable such as tablets + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_LEFT, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_TOP, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_LEFT, DOCKED_RIGHT, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_LEFT, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + assertFalse(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_TOP, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + assertTrue(DockedStackDividerController.isDockSideAllowed(DOCKED_RIGHT, DOCKED_RIGHT, + NAV_BAR_BOTTOM, false /* navigationBarCanMove */)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java new file mode 100644 index 000000000000..f1c6eab2143d --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.content.ClipData; +import android.graphics.PixelFormat; +import android.os.IBinder; +import android.os.Looper; +import android.os.UserHandle; +import android.os.UserManagerInternal; +import android.platform.test.annotations.Presubmit; +import android.view.InputChannel; +import android.view.SurfaceControl; +import android.view.SurfaceSession; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.server.LocalServices; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for the {@link DragDropController} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:DragDropControllerTests + */ +@SmallTest +@Presubmit +public class DragDropControllerTests extends WindowTestsBase { + private static final int TIMEOUT_MS = 3000; + private TestDragDropController mTarget; + private WindowState mWindow; + private IBinder mToken; + + static class TestDragDropController extends DragDropController { + private Runnable mCloseCallback; + + TestDragDropController(WindowManagerService service, Looper looper) { + super(service, looper); + } + + void setOnClosedCallbackLocked(Runnable runnable) { + assertTrue(dragDropActiveLocked()); + mCloseCallback = runnable; + } + + @Override + void onDragStateClosedLocked(DragState dragState) { + super.onDragStateClosedLocked(dragState); + if (mCloseCallback != null) { + mCloseCallback.run(); + mCloseCallback = null; + } + } + } + + /** + * Creates a window state which can be used as a drop target. + */ + private WindowState createDropTargetWindow(String name, int ownerId) { + final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken( + mDisplayContent); + final TaskStack stack = createStackControllerOnStackOnDisplay( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; + final Task task = createTaskInStack(stack, ownerId); + task.addChild(token, 0); + + final WindowState window = createWindow( + null, TYPE_BASE_APPLICATION, token, name, ownerId, false); + window.mInputChannel = new InputChannel(); + window.mHasSurface = true; + return window; + } + + @BeforeClass + public static void setUpOnce() { + final UserManagerInternal userManager = mock(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, userManager); + } + + @AfterClass + public static void tearDownOnce() { + LocalServices.removeServiceForTest(UserManagerInternal.class); + } + + @Before + public void setUp() throws Exception { + mTarget = new TestDragDropController(mWm, mWm.mH.getLooper()); + mDisplayContent = spy(mDisplayContent); + mWindow = createDropTargetWindow("Drag test window", 0); + doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0); + when(mWm.mInputManager.transferTouchFocus(any(), any())).thenReturn(true); + + synchronized (mWm.mGlobalLock) { + mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); + } + } + + @After + public void tearDown() throws Exception { + final CountDownLatch latch; + synchronized (mWm.mGlobalLock) { + if (!mTarget.dragDropActiveLocked()) { + return; + } + if (mToken != null) { + mTarget.cancelDragAndDrop(mToken); + } + latch = new CountDownLatch(1); + mTarget.setOnClosedCallbackLocked(latch::countDown); + } + assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + + @Test + public void testDragFlow() { + dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0); + } + + @Test + public void testPerformDrag_NullDataWithGrantUri() { + dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0); + } + + @Test + public void testPerformDrag_NullDataToOtherUser() { + final WindowState otherUsersWindow = + createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE); + doReturn(otherUsersWindow).when(mDisplayContent).getTouchableWinAtPointLocked(10, 10); + + dragFlow(0, null, 10, 10); + } + + private void dragFlow(int flag, ClipData data, float dropX, float dropY) { + final SurfaceSession appSession = new SurfaceSession(); + try { + final SurfaceControl surface = new SurfaceControl.Builder(appSession) + .setName("drag surface") + .setBufferSize(100, 100) + .setFormat(PixelFormat.TRANSLUCENT) + .build(); + + assertTrue(mWm.mInputManager.transferTouchFocus(null, null)); + mToken = mTarget.performDrag( + new SurfaceSession(), 0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, + data); + assertNotNull(mToken); + + mTarget.handleMotionEvent(false, dropX, dropY); + mToken = mWindow.mClient.asBinder(); + } finally { + appSession.kill(); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java new file mode 100644 index 000000000000..c11e606386e6 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import static org.junit.Assert.assertEquals; + +import android.graphics.Insets; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.InsetsSource; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsSourceProviderTest extends WindowTestsBase { + + private InsetsSourceProvider mProvider = new InsetsSourceProvider( + new InsetsSource(TYPE_TOP_BAR)); + + @Test + public void testPostLayout() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + topBar.mHasSurface = true; + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame()); + assertEquals(Insets.of(0, 100, 0, 0), + mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_invisible() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, null); + mProvider.onPostLayout(); + assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500), + false /* ignoreVisibility */)); + } + + @Test + public void testPostLayout_frameProvider() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + topBar.getFrameLw().set(0, 0, 500, 100); + mProvider.setWindow(topBar, + (displayFrames, windowState, rect) -> { + rect.set(10, 10, 20, 20); + }); + mProvider.onPostLayout(); + assertEquals(new Rect(10, 10, 20, 20), mProvider.getSource().getFrame()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java new file mode 100644 index 000000000000..331622ce22a5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.TYPE_NAVIGATION_BAR; +import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.platform.test.annotations.Presubmit; +import android.view.InsetsState; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +@SmallTest +@FlakyTest(detail = "Promote once confirmed non-flaky") +@Presubmit +public class InsetsStateControllerTest extends WindowTestsBase { + + @Test + public void testStripForDispatch_notOwn() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertNotNull(mDisplayContent.getInsetsStateController().getInsetsForDispatch(app) + .getSource(TYPE_TOP_BAR)); + } + + @Test + public void testStripForDispatch_own() { + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + topBar.setInsetProvider( + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(topBar)); + } + + @Test + public void testStripForDispatch_navBar() { + final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow"); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR) + .setWindow(topBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_NAVIGATION_BAR) + .setWindow(navBar, null); + mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_IME) + .setWindow(ime, null); + assertEquals(new InsetsState(), + mDisplayContent.getInsetsStateController().getInsetsForDispatch(navBar)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java new file mode 100644 index 000000000000..63d9fb9c17e9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; + +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.view.IPinnedStackListener; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:PinnedStackControllerTest + */ +@SmallTest +@Presubmit +public class PinnedStackControllerTest extends WindowTestsBase { + + private static final int SHELF_HEIGHT = 300; + + @Mock private IPinnedStackListener mIPinnedStackListener; + @Mock private IPinnedStackListener.Stub mIPinnedStackListenerStub; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mIPinnedStackListener.asBinder()).thenReturn(mIPinnedStackListenerStub); + } + + @Test + public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException { + mWm.mSupportsPictureInPicture = true; + mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener); + + verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0); + verify(mIPinnedStackListener).onShelfVisibilityChanged(false, 0); + verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), + eq(false), anyInt()); + verify(mIPinnedStackListener).onActionsChanged(any()); + verify(mIPinnedStackListener).onMinimizedStateChanged(anyBoolean()); + + reset(mIPinnedStackListener); + + mWm.setShelfHeight(true, SHELF_HEIGHT); + verify(mIPinnedStackListener).onShelfVisibilityChanged(true, SHELF_HEIGHT); + verify(mIPinnedStackListener).onMovementBoundsChanged(any(), any(), any(), eq(false), + eq(true), anyInt()); + verify(mIPinnedStackListener, never()).onImeVisibilityChanged(anyBoolean(), anyInt()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java new file mode 100644 index 000000000000..cc6a58a81635 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.Display.DEFAULT_DISPLAY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; + +import android.os.Binder; +import android.os.IInterface; +import android.platform.test.annotations.Presubmit; +import android.util.SparseBooleanArray; +import android.view.IRecentsAnimationRunner; +import android.view.SurfaceControl; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:RecentsAnimationControllerTest + */ +@SmallTest +@Presubmit +public class RecentsAnimationControllerTest extends WindowTestsBase { + + @Mock SurfaceControl mMockLeash; + @Mock SurfaceControl.Transaction mMockTransaction; + @Mock OnAnimationFinishedCallback mFinishedCallback; + @Mock IRecentsAnimationRunner mMockRunner; + @Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks; + private RecentsAnimationController mController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mMockRunner.asBinder()).thenReturn(new Binder()); + mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, + DEFAULT_DISPLAY); + } + + @Test + public void testRemovedBeforeStarted_expectCanceled() throws Exception { + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(), + false /* isRecentTaskInvisible */); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + + // Remove the app window so that the animation target can not be created + appWindow.removeImmediately(); + mController.startAnimation(); + + // Verify that the finish callback to reparent the leash is called + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + // Verify the animation canceled callback to the app was made + verify(mMockRunner).onAnimationCanceled(); + verifyNoMoreInteractionsExceptAsBinder(mMockRunner); + } + + @Test + public void testCancelAfterRemove_expectIgnored() { + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(), + false /* isRecentTaskInvisible */); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + + // Remove the app window so that the animation target can not be created + appWindow.removeImmediately(); + mController.startAnimation(); + mController.cleanupAnimation(REORDER_KEEP_IN_PLACE); + try { + mController.cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "test"); + } catch (Exception e) { + fail("Unexpected failure when canceling animation after finishing it"); + } + } + + @FlakyTest(bugId = 117117823) + @Test + public void testIncludedApps_expectTargetAndVisible() { + mWm.setRecentsAnimationController(mController); + final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + hiddenAppWindow.setHidden(true); + mDisplayContent.getConfiguration().windowConfiguration.setRotation( + mDisplayContent.getRotation()); + mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + + // Ensure that we are animating the target activity as well + assertTrue(mController.isAnimatingTask(homeAppWindow.getTask())); + assertTrue(mController.isAnimatingTask(appWindow.getTask())); + assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask())); + } + + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { + verify(binder, atLeast(0)).asBinder(); + verifyNoMoreInteractions(binder); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java new file mode 100644 index 000000000000..ad2a708b88d9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; + +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Binder; +import android.os.IInterface; +import android.platform.test.annotations.Presubmit; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +import androidx.test.filters.SmallTest; + +import com.android.server.testutils.OffsettableClock; +import com.android.server.testutils.TestHandler; +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Build/Install/Run: + * atest FrameworksServicesTests:RemoteAnimationControllerTest + */ +@SmallTest +@Presubmit +public class RemoteAnimationControllerTest extends WindowTestsBase { + + @Mock SurfaceControl mMockLeash; + @Mock Transaction mMockTransaction; + @Mock OnAnimationFinishedCallback mFinishedCallback; + @Mock IRemoteAnimationRunner mMockRunner; + private RemoteAnimationAdapter mAdapter; + private RemoteAnimationController mController; + private final OffsettableClock mClock = new OffsettableClock.Stopped(); + private TestHandler mHandler; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mMockRunner.asBinder()).thenReturn(new Binder()); + mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50); + mAdapter.setCallingPid(123); + mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0); + mController = new RemoteAnimationController(mWm, mAdapter, mHandler); + } + + @Test + public void testRun() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mDisplayContent.mOpeningApps.add(win.mAppToken); + try { + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture()); + assertEquals(1, appsCaptor.getValue().length); + final RemoteAnimationTarget app = appsCaptor.getValue()[0]; + assertEquals(new Point(50, 100), app.position); + assertEquals(new Rect(50, 100, 150, 150), app.sourceContainerBounds); + assertEquals(win.mAppToken.getPrefixOrderIndex(), app.prefixOrderIndex); + assertEquals(win.mAppToken.getTask().mTaskId, app.taskId); + assertEquals(mMockLeash, app.leash); + assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect); + assertEquals(false, app.isTranslucent); + verify(mMockTransaction).setLayer(mMockLeash, app.prefixOrderIndex); + verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y); + verify(mMockTransaction).setWindowCrop(mMockLeash, new Rect(0, 0, 100, 50)); + + finishedCaptor.getValue().onAnimationFinished(); + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + } finally { + mDisplayContent.mOpeningApps.clear(); + } + } + + @Test + public void testCancel() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + + adapter.onAnimationCancelled(mMockLeash); + verify(mMockRunner).onAnimationCancelled(); + } + + @Test + public void testTimeout() throws Exception { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + + mClock.fastForward(2500); + mHandler.timeAdvance(); + + verify(mMockRunner).onAnimationCancelled(); + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + } + + @Test + public void testTimeout_scaled() throws Exception { + mWm.setAnimationScale(2, 5.0f); + try { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, + "testWin"); + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + + mClock.fastForward(2500); + mHandler.timeAdvance(); + + verify(mMockRunner, never()).onAnimationCancelled(); + + mClock.fastForward(10000); + mHandler.timeAdvance(); + + verify(mMockRunner).onAnimationCancelled(); + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + } finally { + mWm.setAnimationScale(2, 1.0f); + } + } + + @Test + public void testZeroAnimations() { + mController.goodToGo(); + verifyNoMoreInteractionsExceptAsBinder(mMockRunner); + } + + @Test + public void testNotReallyStarted() { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + mController.goodToGo(); + verifyNoMoreInteractionsExceptAsBinder(mMockRunner); + } + + @Test + public void testOneNotStarted() throws Exception { + final WindowState win1 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin1"); + final WindowState win2 = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin2"); + mController.createAnimationAdapter(win1.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + final AnimationAdapter adapter = mController.createAnimationAdapter(win2.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + mController.goodToGo(); + mWm.mAnimator.executeAfterPrepareSurfacesRunnables(); + final ArgumentCaptor appsCaptor = + ArgumentCaptor.forClass(RemoteAnimationTarget[].class); + final ArgumentCaptor finishedCaptor = + ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class); + verify(mMockRunner).onAnimationStart(appsCaptor.capture(), finishedCaptor.capture()); + assertEquals(1, appsCaptor.getValue().length); + assertEquals(mMockLeash, appsCaptor.getValue()[0].leash); + } + + @Test + public void testRemovedBeforeStarted() { + final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); + final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken, + new Point(50, 100), new Rect(50, 100, 150, 150)); + adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback); + win.mAppToken.removeImmediately(); + mController.goodToGo(); + verifyNoMoreInteractionsExceptAsBinder(mMockRunner); + verify(mFinishedCallback).onAnimationFinished(eq(adapter)); + } + + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { + verify(binder, atLeast(0)).asBinder(); + verifyNoMoreInteractions(binder); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java new file mode 100644 index 000000000000..ce5b13cab1a7 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/StackWindowControllerTests.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Test class for {@link StackWindowController}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:StackWindowControllerTests + */ +@SmallTest +@Presubmit +public class StackWindowControllerTests extends WindowTestsBase { + @Test + public void testRemoveContainer() { + final StackWindowController stackController = + createStackControllerOnDisplay(mDisplayContent); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + + final TaskStack stack = stackController.mContainer; + final Task task = taskController.mContainer; + assertNotNull(stack); + assertNotNull(task); + stackController.removeContainer(); + // Assert that the container was removed. + assertNull(stackController.mContainer); + assertNull(taskController.mContainer); + assertNull(stack.getDisplayContent()); + assertNull(task.getDisplayContent()); + assertNull(task.mStack); + } + + @Test + public void testRemoveContainer_deferRemoval() { + final StackWindowController stackController = + createStackControllerOnDisplay(mDisplayContent); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController); + + final TaskStack stack = stackController.mContainer; + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; + // Stack removal is deferred if one of its child is animating. + task.setLocalIsAnimating(true); + + stackController.removeContainer(); + // For the case of deferred removal the stack controller will no longer be connected to the + // container, but the task controller will still be connected to the its container until + // the stack window container is removed. + assertNull(stackController.mContainer); + assertNull(stack.getController()); + assertNotNull(taskController.mContainer); + assertNotNull(task.getController()); + + stack.removeImmediately(); + assertNull(taskController.mContainer); + assertNull(task.getController()); + } + + @Test + public void testReparent() { + // Create first stack on primary display. + final StackWindowController stack1Controller = + createStackControllerOnDisplay(mDisplayContent); + final TaskStack stack1 = stack1Controller.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; + task1.mOnDisplayChangedCalled = false; + + // Create second display and put second stack on it. + final DisplayContent dc = createNewDisplay(); + final StackWindowController stack2Controller = + createStackControllerOnDisplay(dc); + final TaskStack stack2 = stack2Controller.mContainer; + + // Reparent + stack1Controller.reparent(dc.getDisplayId(), new Rect(), true /* onTop */); + assertEquals(dc, stack1.getDisplayContent()); + final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); + final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); + assertEquals(stack1PositionInParent, stack2PositionInParent + 1); + assertTrue(task1.mOnDisplayChangedCalled); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java new file mode 100644 index 000000000000..83e7ee711831 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import android.animation.AnimationHandler.AnimationFrameCallbackProvider; +import android.animation.ValueAnimator; +import android.graphics.Matrix; +import android.graphics.Point; +import android.os.PowerManagerInternal; +import android.platform.test.annotations.Presubmit; +import android.view.Choreographer; +import android.view.Choreographer.FrameCallback; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.animation.Animation; +import android.view.animation.TranslateAnimation; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.CountDownLatch; + +/** + * Test class for {@link SurfaceAnimationRunner}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:SurfaceAnimationRunnerTest + */ +@SmallTest +@Presubmit +public class SurfaceAnimationRunnerTest extends WindowTestsBase { + + @Mock SurfaceControl mMockSurface; + @Mock Transaction mMockTransaction; + @Mock AnimationSpec mMockAnimationSpec; + @Mock PowerManagerInternal mMockPowerManager; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + private SurfaceAnimationRunner mSurfaceAnimationRunner; + private CountDownLatch mFinishCallbackLatch; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mFinishCallbackLatch = new CountDownLatch(1); + mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null, + mMockTransaction, mMockPowerManager); + } + + private void finishedCallback() { + mFinishCallbackLatch.countDown(); + } + + @Test + public void testAnimation() throws Exception { + mSurfaceAnimationRunner + .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, + this::finishedCallback); + + // Ensure that the initial transformation has been applied. + final Matrix m = new Matrix(); + m.setTranslate(-10, 0); + verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); + verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f)); + + mFinishCallbackLatch.await(1, SECONDS); + assertFinishCallbackCalled(); + + m.setTranslate(10, 0); + verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any()); + + // At least 3 times: After initialization, first frame, last frame. + verify(mMockTransaction, atLeast(3)).setAlpha(eq(mMockSurface), eq(1.0f)); + } + + @Test + public void testCancel_notStarted() { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, + mMockTransaction, mMockPowerManager); + mSurfaceAnimationRunner + .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction, + this::finishedCallback); + mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); + waitUntilHandlersIdle(); + assertTrue(mSurfaceAnimationRunner.mPendingAnimations.isEmpty()); + assertFinishCallbackNotCalled(); + } + + @Test + public void testCancel_running() throws Exception { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, + mMockTransaction, mMockPowerManager); + mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, + mMockTransaction, this::finishedCallback); + waitUntilNextFrame(); + assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); + assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + waitUntilHandlersIdle(); + assertFinishCallbackNotCalled(); + } + + @FlakyTest(bugId = 71719744) + @Test + public void testCancel_sneakyCancelBeforeUpdate() throws Exception { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(null, () -> new ValueAnimator() { + { + setFloatValues(0f, 1f); + } + + @Override + public void addUpdateListener(AnimatorUpdateListener listener) { + super.addUpdateListener(animation -> { + // Sneaky test cancels animation just before applying frame to simulate + // interleaving of multiple threads. Muahahaha + if (animation.getCurrentPlayTime() > 0) { + mSurfaceAnimationRunner.onAnimationCancelled(mMockSurface); + } + listener.onAnimationUpdate(animation); + }); + } + }, mMockTransaction, mMockPowerManager); + when(mMockAnimationSpec.getDuration()).thenReturn(200L); + mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction, + this::finishedCallback); + + // We need to wait for two frames: The first frame starts the animation, the second frame + // actually cancels the animation. + waitUntilNextFrame(); + waitUntilNextFrame(); + assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + verify(mMockAnimationSpec, atLeastOnce()).apply(any(), any(), eq(0L)); + } + + @FlakyTest(bugId = 74780584) + @Test + public void testDeferStartingAnimations() throws Exception { + mSurfaceAnimationRunner.deferStartingAnimations(); + mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, + mMockTransaction, this::finishedCallback); + waitUntilNextFrame(); + assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mSurfaceAnimationRunner.continueStartingAnimations(); + waitUntilNextFrame(); + assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty()); + mFinishCallbackLatch.await(1, SECONDS); + assertFinishCallbackCalled(); + } + + @Test + public void testPowerHint() throws Exception { + mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null, + mMockTransaction, mMockPowerManager); + mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface, + mMockTransaction, this::finishedCallback); + waitUntilNextFrame(); + + // TODO: For some reason we don't have access to PowerHint definition from the tests. For + // now let's just verify that we got some kind of hint. + verify(mMockPowerManager).powerHint(anyInt(), anyInt()); + } + + private void waitUntilNextFrame() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, + latch::countDown, null /* token */); + latch.await(); + } + + private void assertFinishCallbackCalled() { + assertEquals(0, mFinishCallbackLatch.getCount()); + } + + private void assertFinishCallbackNotCalled() { + assertEquals(1, mFinishCallbackLatch.getCount()); + } + + private AnimationSpec createTranslateAnimation() { + final Animation a = new TranslateAnimation(-10, 10, 0, 0); + a.initialize(0, 0, 0, 0); + a.setDuration(50); + return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */); + } + + /** + * Callback provider that doesn't animate at all. + */ + private static final class NoOpFrameCallbackProvider implements AnimationFrameCallbackProvider { + + @Override + public void postFrameCallback(FrameCallback callback) { + } + + @Override + public void postCommitCallback(Runnable runnable) { + } + + @Override + public long getFrameTime() { + return 0; + } + + @Override + public long getFrameDelay() { + return 0; + } + + @Override + public void setFrameDelay(long delay) { + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java new file mode 100644 index 000000000000..d14f30db8e9f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; + +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Builder; +import android.view.SurfaceControl.Transaction; +import android.view.SurfaceSession; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.SurfaceAnimator.Animatable; +import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for {@link SurfaceAnimatorTest}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:SurfaceAnimatorTest + */ +@SmallTest +@Presubmit +public class SurfaceAnimatorTest extends WindowTestsBase { + + @Mock AnimationAdapter mSpec; + @Mock AnimationAdapter mSpec2; + @Mock Transaction mTransaction; + + private SurfaceSession mSession = new SurfaceSession(); + private MyAnimatable mAnimatable; + private MyAnimatable mAnimatable2; + private DeferFinishAnimatable mDeferFinishAnimatable; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mAnimatable = new MyAnimatable(mWm, mSession, mTransaction); + mAnimatable2 = new MyAnimatable(mWm, mSession, mTransaction); + mDeferFinishAnimatable = new DeferFinishAnimatable(mWm, mSession, mTransaction); + } + + @After + public void tearDown() { + mAnimatable = null; + mAnimatable2 = null; + mDeferFinishAnimatable = null; + mSession.kill(); + mSession = null; + } + + @Test + public void testRunAnimation() { + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + assertAnimating(mAnimatable); + verify(mTransaction).reparent(eq(mAnimatable.mSurface), eq(mAnimatable.mLeash.getHandle())); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertNotAnimating(mAnimatable); + assertTrue(mAnimatable.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(mAnimatable.mLeash)); + // TODO: Verify reparenting once we use mPendingTransaction to reparent it back + } + + @Test + public void testOverrideAnimation() { + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + final SurfaceControl firstLeash = mAnimatable.mLeash; + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */); + + verify(mTransaction).destroy(eq(firstLeash)); + assertFalse(mAnimatable.mFinishedCallbackCalled); + + final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + assertAnimating(mAnimatable); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + // First animation was finished, but this shouldn't cancel the second animation + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertTrue(mAnimatable.mSurfaceAnimator.isAnimating()); + + // Second animation was finished + verify(mSpec2).startAnimation(any(), any(), callbackCaptor.capture()); + callbackCaptor.getValue().onAnimationFinished(mSpec2); + assertNotAnimating(mAnimatable); + assertTrue(mAnimatable.mFinishedCallbackCalled); + } + + @Test + public void testCancelAnimation() { + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + assertAnimating(mAnimatable); + mAnimatable.mSurfaceAnimator.cancelAnimation(); + assertNotAnimating(mAnimatable); + verify(mSpec).onAnimationCancelled(any()); + assertTrue(mAnimatable.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(mAnimatable.mLeash)); + } + + @Test + public void testDelayingAnimationStart() { + mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + verifyZeroInteractions(mSpec); + assertAnimating(mAnimatable); + assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed()); + mAnimatable.mSurfaceAnimator.endDelayingAnimationStart(); + verify(mSpec).startAnimation(any(), any(), any()); + } + + @Test + public void testDelayingAnimationStartAndCancelled() { + mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + mAnimatable.mSurfaceAnimator.cancelAnimation(); + verifyZeroInteractions(mSpec); + assertNotAnimating(mAnimatable); + assertTrue(mAnimatable.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(mAnimatable.mLeash)); + } + + @Test + public void testTransferAnimation() { + mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */); + + final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + final SurfaceControl leash = mAnimatable.mLeash; + + mAnimatable2.mSurfaceAnimator.transferAnimation(mAnimatable.mSurfaceAnimator); + assertNotAnimating(mAnimatable); + assertAnimating(mAnimatable2); + assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash); + verify(mTransaction, never()).destroy(eq(leash)); + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertNotAnimating(mAnimatable2); + assertTrue(mAnimatable2.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(leash)); + } + + @Test + @FlakyTest(detail = "Promote once confirmed non-flaky") + public void testDeferFinish() { + + // Start animation + mDeferFinishAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, + true /* hidden */); + final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass( + OnAnimationFinishedCallback.class); + assertAnimating(mDeferFinishAnimatable); + verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture()); + + // Finish the animation but then make sure we are deferring. + callbackCaptor.getValue().onAnimationFinished(mSpec); + assertAnimating(mDeferFinishAnimatable); + + // Now end defer finishing. + mDeferFinishAnimatable.mEndDeferFinishCallback.run(); + assertNotAnimating(mAnimatable2); + assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled); + verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash)); + } + + private void assertAnimating(MyAnimatable animatable) { + assertTrue(animatable.mSurfaceAnimator.isAnimating()); + assertNotNull(animatable.mSurfaceAnimator.getAnimation()); + } + + private void assertNotAnimating(MyAnimatable animatable) { + assertFalse(animatable.mSurfaceAnimator.isAnimating()); + assertNull(animatable.mSurfaceAnimator.getAnimation()); + } + + private static class MyAnimatable implements Animatable { + + private final SurfaceSession mSession; + private final Transaction mTransaction; + final SurfaceControl mParent; + final SurfaceControl mSurface; + final SurfaceAnimator mSurfaceAnimator; + SurfaceControl mLeash; + boolean mFinishedCallbackCalled; + + MyAnimatable(WindowManagerService wm, SurfaceSession session, Transaction transaction) { + mSession = session; + mTransaction = transaction; + mParent = wm.makeSurfaceBuilder(mSession) + .setName("test surface parent") + .build(); + mSurface = wm.makeSurfaceBuilder(mSession) + .setName("test surface") + .build(); + mFinishedCallbackCalled = false; + mLeash = null; + mSurfaceAnimator = new SurfaceAnimator(this, mFinishedCallback, wm); + } + + @Override + public Transaction getPendingTransaction() { + return mTransaction; + } + + @Override + public void commitPendingTransaction() { + } + + @Override + public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { + } + + @Override + public void onAnimationLeashDestroyed(Transaction t) { + } + + @Override + public Builder makeAnimationLeash() { + return new SurfaceControl.Builder(mSession) { + + @Override + public SurfaceControl build() { + mLeash = super.build(); + return mLeash; + } + }.setParent(mParent); + } + + @Override + public SurfaceControl getAnimationLeashParent() { + return mParent; + } + + @Override + public SurfaceControl getSurfaceControl() { + return mSurface; + } + + @Override + public SurfaceControl getParentSurfaceControl() { + return mParent; + } + + @Override + public int getSurfaceWidth() { + return 1; + } + + @Override + public int getSurfaceHeight() { + return 1; + } + + private final Runnable mFinishedCallback = () -> mFinishedCallbackCalled = true; + } + + private static class DeferFinishAnimatable extends MyAnimatable { + + Runnable mEndDeferFinishCallback; + + DeferFinishAnimatable(WindowManagerService wm, SurfaceSession session, + Transaction transaction) { + super(wm, session, transaction); + } + + @Override + public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { + mEndDeferFinishCallback = endDeferFinishCallback; + return true; + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java new file mode 100644 index 000000000000..b996bfbf2101 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.server.wm.TaskPositioner.MIN_ASPECT; +import static com.android.server.wm.WindowManagerService.dipToPixel; +import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; +import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.app.IActivityTaskManager; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Display; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +/** + * Tests for the {@link TaskPositioner} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskPositionerTests + */ +@SmallTest +public class TaskPositionerTests extends WindowTestsBase { + + private static final boolean DEBUGGING = false; + private static final String TAG = "TaskPositionerTest"; + + private static final int MOUSE_DELTA_X = 5; + private static final int MOUSE_DELTA_Y = 5; + + private int mMinVisibleWidth; + private int mMinVisibleHeight; + private TaskPositioner mPositioner; + private WindowState mWindow; + private Rect mDimBounds = new Rect(); + + @Before + public void setUp() throws Exception { + TaskPositioner.setFactory(null); + + final Display display = mDisplayContent.getDisplay(); + final DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + // This should be the same calculation as the TaskPositioner uses. + mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, dm); + mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, dm); + + mPositioner = new TaskPositioner(mWm, Mockito.mock(IActivityTaskManager.class)); + mPositioner.register(mDisplayContent); + + mWindow = Mockito.spy(createWindow(null, TYPE_BASE_APPLICATION, "window")); + final Task task = Mockito.spy(mWindow.getTask()); + Mockito.when(mWindow.getTask()).thenReturn(task); + + Mockito.doAnswer(invocation -> { + final Rect rect = (Rect) invocation.getArguments()[0]; + rect.set(mDimBounds); + return (Void) null; + }).when(task).getDimBounds(Mockito.any(Rect.class)); + + mWindow.getStack().setWindowingMode(WINDOWING_MODE_FREEFORM); + } + + @Test + public void testOverrideFactory() { + final boolean[] created = new boolean[1]; + created[0] = false; + TaskPositioner.setFactory(new TaskPositioner.Factory() { + @Override + public TaskPositioner create(WindowManagerService service) { + created[0] = true; + return null; + } + }); + + assertNull(TaskPositioner.create(mWm)); + assertTrue(created[0]); + } + + /** + * This tests that free resizing will allow to change the orientation as well + * as does some basic tests (e.g. dragging in Y only will keep X stable). + */ + @Test + public void testBasicFreeWindowResizing() { + final Rect r = new Rect(100, 220, 700, 520); + final int midY = (r.top + r.bottom) / 2; + mDimBounds.set(r); + + // Start a drag resize starting upper left. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); + assertBoundsEquals(r, mPositioner.getWindowDragBounds()); + + // Drag to a good landscape size. + mPositioner.resizeDrag(0.0f, 0.0f); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a good portrait size. + mPositioner.resizeDrag(400.0f, 0.0f); + assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a too small size for the width. + mPositioner.resizeDrag(2000.0f, r.top); + assertBoundsEquals( + new Rect(r.right - mMinVisibleWidth, r.top + MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a too small size for the height. + mPositioner.resizeDrag(r.left, 2000.0f); + assertBoundsEquals( + new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Start a drag resize left and see that only the left coord changes.. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); + + // Drag to the left. + mPositioner.resizeDrag(0.0f, midY); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the right. + mPositioner.resizeDrag(200.0f, midY); + assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the top + mPositioner.resizeDrag(r.left, 0.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the bottom + mPositioner.resizeDrag(r.left, 1000.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that by dragging any edge, the fixed / opposite edge(s) remains anchored. + */ + @Test + public void testFreeWindowResizingTestAllEdges() { + final Rect r = new Rect(100, 220, 700, 520); + final int midX = (r.left + r.right) / 2; + final int midY = (r.top + r.bottom) / 2; + mDimBounds.set(r); + + // Drag upper left. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); + mPositioner.resizeDrag(0.0f, 0.0f); + assertTrue(r.left != mPositioner.getWindowDragBounds().left); + assertEquals(r.right, mPositioner.getWindowDragBounds().right); + assertTrue(r.top != mPositioner.getWindowDragBounds().top); + assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); + + // Drag upper. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); + mPositioner.resizeDrag(0.0f, 0.0f); + assertEquals(r.left, mPositioner.getWindowDragBounds().left); + assertEquals(r.right, mPositioner.getWindowDragBounds().right); + assertTrue(r.top != mPositioner.getWindowDragBounds().top); + assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); + + // Drag upper right. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); + mPositioner.resizeDrag(r.right + 100, 0.0f); + assertEquals(r.left, mPositioner.getWindowDragBounds().left); + assertTrue(r.right != mPositioner.getWindowDragBounds().right); + assertTrue(r.top != mPositioner.getWindowDragBounds().top); + assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); + + // Drag right. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.right + MOUSE_DELTA_X, midY); + mPositioner.resizeDrag(r.right + 100, 0.0f); + assertEquals(r.left, mPositioner.getWindowDragBounds().left); + assertTrue(r.right != mPositioner.getWindowDragBounds().right); + assertEquals(r.top, mPositioner.getWindowDragBounds().top); + assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); + + // Drag bottom right. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, + r.right + MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y); + mPositioner.resizeDrag(r.right + 100, r.bottom + 100); + assertEquals(r.left, mPositioner.getWindowDragBounds().left); + assertTrue(r.right != mPositioner.getWindowDragBounds().right); + assertEquals(r.top, mPositioner.getWindowDragBounds().top); + assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); + + // Drag bottom. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, midX, r.bottom + MOUSE_DELTA_Y); + mPositioner.resizeDrag(r.right + 100, r.bottom + 100); + assertEquals(r.left, mPositioner.getWindowDragBounds().left); + assertEquals(r.right, mPositioner.getWindowDragBounds().right); + assertEquals(r.top, mPositioner.getWindowDragBounds().top); + assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); + + // Drag bottom left. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.bottom + MOUSE_DELTA_Y); + mPositioner.resizeDrag(0.0f, r.bottom + 100); + assertTrue(r.left != mPositioner.getWindowDragBounds().left); + assertEquals(r.right, mPositioner.getWindowDragBounds().right); + assertEquals(r.top, mPositioner.getWindowDragBounds().top); + assertTrue(r.bottom != mPositioner.getWindowDragBounds().bottom); + + // Drag left. + mPositioner.startDrag(mWindow, true /*resizing*/, + false /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midX); + mPositioner.resizeDrag(0.0f, r.bottom + 100); + assertTrue(r.left != mPositioner.getWindowDragBounds().left); + assertEquals(r.right, mPositioner.getWindowDragBounds().right); + assertEquals(r.top, mPositioner.getWindowDragBounds().top); + assertEquals(r.bottom, mPositioner.getWindowDragBounds().bottom); + } + + /** + * This tests that a constrained landscape window will keep the aspect and do the + * right things upon resizing when dragged from the top left corner. + */ + @Test + public void testLandscapePreservedWindowResizingDragTopLeft() { + final Rect r = new Rect(100, 220, 700, 520); + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); + assertBoundsEquals(r, mPositioner.getWindowDragBounds()); + + // Drag to a good landscape size. + mPositioner.resizeDrag(0.0f, 0.0f); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a good portrait size. + mPositioner.resizeDrag(400.0f, 0.0f); + int width = Math.round((float) (r.bottom - MOUSE_DELTA_Y) * MIN_ASPECT); + assertBoundsEquals(new Rect(r.right - width, MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a too small size for the width. + mPositioner.resizeDrag(2000.0f, r.top); + final int w = mMinVisibleWidth; + final int h = Math.round(w / MIN_ASPECT); + assertBoundsEquals(new Rect(r.right - w, r.bottom - h, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a too small size for the height. + mPositioner.resizeDrag(r.left, 2000.0f); + assertBoundsEquals( + new Rect(r.left + MOUSE_DELTA_X, r.bottom - mMinVisibleHeight, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that a constrained landscape window will keep the aspect and do the + * right things upon resizing when dragged from the left corner. + */ + @Test + public void testLandscapePreservedWindowResizingDragLeft() { + final Rect r = new Rect(100, 220, 700, 520); + final int midY = (r.top + r.bottom) / 2; + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); + + // Drag to the left. + mPositioner.resizeDrag(0.0f, midY); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the right. + mPositioner.resizeDrag(200.0f, midY); + assertBoundsEquals(new Rect(200 + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag all the way to the right and see the height also shrinking. + mPositioner.resizeDrag(2000.0f, midY); + final int w = mMinVisibleWidth; + final int h = Math.round((float) w / MIN_ASPECT); + assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), + mPositioner.getWindowDragBounds()); + + // Drag to the top. + mPositioner.resizeDrag(r.left, 0.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the bottom. + mPositioner.resizeDrag(r.left, 1000.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that a constrained landscape window will keep the aspect and do the + * right things upon resizing when dragged from the top corner. + */ + @Test + public void testLandscapePreservedWindowResizingDragTop() { + final Rect r = new Rect(100, 220, 700, 520); + final int midX = (r.left + r.right) / 2; + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); + + // Drag to the left (no change). + mPositioner.resizeDrag(0.0f, r.top); + assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the right (no change). + mPositioner.resizeDrag(2000.0f, r.top); + assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the top. + mPositioner.resizeDrag(300.0f, 0.0f); + int h = r.bottom - MOUSE_DELTA_Y; + int w = Math.max(r.right - r.left, Math.round(h * MIN_ASPECT)); + assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the bottom. + mPositioner.resizeDrag(r.left, 1000.0f); + h = mMinVisibleHeight; + assertBoundsEquals(new Rect(r.left, r.bottom - h, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that a constrained portrait window will keep the aspect and do the + * right things upon resizing when dragged from the top left corner. + */ + @Test + public void testPortraitPreservedWindowResizingDragTopLeft() { + final Rect r = new Rect(330, 100, 630, 600); + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, r.top - MOUSE_DELTA_Y); + assertBoundsEquals(r, mPositioner.getWindowDragBounds()); + + // Drag to a good landscape size. + mPositioner.resizeDrag(0.0f, 0.0f); + int height = Math.round((float) (r.right - MOUSE_DELTA_X) * MIN_ASPECT); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.bottom - height, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a good portrait size. + mPositioner.resizeDrag(400.0f, 0.0f); + assertBoundsEquals(new Rect(400 + MOUSE_DELTA_X, MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to a too small size for the height and the the width shrinking. + mPositioner.resizeDrag(r.left + MOUSE_DELTA_X, 2000.0f); + final int w = Math.max(mMinVisibleWidth, Math.round(mMinVisibleHeight / MIN_ASPECT)); + final int h = Math.max(mMinVisibleHeight, Math.round(w * MIN_ASPECT)); + assertBoundsEquals( + new Rect(r.right - w, r.bottom - h, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that a constrained portrait window will keep the aspect and do the + * right things upon resizing when dragged from the left corner. + */ + @Test + public void testPortraitPreservedWindowResizingDragLeft() { + final Rect r = new Rect(330, 100, 630, 600); + final int midY = (r.top + r.bottom) / 2; + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, r.left - MOUSE_DELTA_X, midY); + + // Drag to the left. + mPositioner.resizeDrag(0.0f, midY); + int w = r.right - MOUSE_DELTA_X; + int h = Math.round(w * MIN_ASPECT); + assertBoundsEquals(new Rect(MOUSE_DELTA_X, r.top, r.right, r.top + h), + mPositioner.getWindowDragBounds()); + + // Drag to the right. + mPositioner.resizeDrag(450.0f, midY); + assertBoundsEquals(new Rect(450 + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag all the way to the right. + mPositioner.resizeDrag(2000.0f, midY); + w = mMinVisibleWidth; + h = Math.max(Math.round((float) w * MIN_ASPECT), r.height()); + assertBoundsEquals(new Rect(r.right - w, r.top, r.right, r.top + h), + mPositioner.getWindowDragBounds()); + + // Drag to the top. + mPositioner.resizeDrag(r.left, 0.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the bottom. + mPositioner.resizeDrag(r.left, 1000.0f); + assertBoundsEquals(new Rect(r.left + MOUSE_DELTA_X, r.top, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + } + + /** + * This tests that a constrained portrait window will keep the aspect and do the + * right things upon resizing when dragged from the top corner. + */ + @Test + public void testPortraitPreservedWindowResizingDragTop() { + final Rect r = new Rect(330, 100, 630, 600); + final int midX = (r.left + r.right) / 2; + mDimBounds.set(r); + + mPositioner.startDrag(mWindow, true /*resizing*/, + true /*preserveOrientation*/, midX, r.top - MOUSE_DELTA_Y); + + // Drag to the left (no change). + mPositioner.resizeDrag(0.0f, r.top); + assertBoundsEquals(new Rect(r.left, r.top + MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the right (no change). + mPositioner.resizeDrag(2000.0f, r.top); + assertBoundsEquals(new Rect(r.left , r.top + MOUSE_DELTA_Y, r.right, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the top. + mPositioner.resizeDrag(300.0f, 0.0f); + int h = r.bottom - MOUSE_DELTA_Y; + int w = Math.min(r.width(), Math.round(h / MIN_ASPECT)); + assertBoundsEquals(new Rect(r.left, MOUSE_DELTA_Y, r.left + w, r.bottom), + mPositioner.getWindowDragBounds()); + + // Drag to the bottom. + mPositioner.resizeDrag(r.left, 1000.0f); + h = Math.max(mMinVisibleHeight, Math.round(mMinVisibleWidth * MIN_ASPECT)); + w = Math.round(h / MIN_ASPECT); + assertBoundsEquals(new Rect(r.left, r.bottom - h, r.left + w, r.bottom), + mPositioner.getWindowDragBounds()); + } + + private static void assertBoundsEquals(Rect expected, Rect actual) { + if (DEBUGGING) { + if (!expected.equals(actual)) { + Log.e(TAG, "rect(" + actual.toString() + ") != isRect(" + actual.toString() + + ") " + Log.getStackTraceString(new Throwable())); + } + } + assertEquals("left", expected.left, actual.left); + assertEquals("right", expected.right, actual.right); + assertEquals("top", expected.top, actual.top); + assertEquals("bottom", expected.bottom, actual.bottom); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java new file mode 100644 index 000000000000..3991e06d6f96 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyInt; + +import android.platform.test.annotations.Presubmit; +import android.view.InputChannel; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link TaskPositioningController} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskPositioningControllerTests + */ +@FlakyTest(bugId = 117924387) +@SmallTest +@Presubmit +public class TaskPositioningControllerTests extends WindowTestsBase { + private static final int TIMEOUT_MS = 1000; + + private TaskPositioningController mTarget; + private WindowState mWindow; + + @Before + public void setUp() throws Exception { + assertNotNull(mWm.mTaskPositioningController); + mTarget = mWm.mTaskPositioningController; + + when(mWm.mInputManager.transferTouchFocus( + any(InputChannel.class), + any(InputChannel.class))).thenReturn(true); + + mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window"); + mWindow.mInputChannel = new InputChannel(); + synchronized (mWm.mGlobalLock) { + mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow); + } + } + + @Test + public void testStartAndFinishPositioning() { + synchronized (mWm.mGlobalLock) { + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + assertTrue(mTarget.startMovingTask(mWindow.mClient, 0, 0)); + + synchronized (mWm.mGlobalLock) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + mTarget.finishTaskPositioning(); + // Wait until the looper processes finishTaskPositioning. + assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + @Test + public void testHandleTapOutsideTask() { + synchronized (mWm.mGlobalLock) { + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } + + final DisplayContent content = mock(DisplayContent.class); + when(content.findTaskForResizePoint(anyInt(), anyInt())).thenReturn(mWindow.getTask()); + assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow()); + + mTarget.handleTapOutsideTask(content, 0, 0); + // Wait until the looper processes finishTaskPositioning. + assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); + + synchronized (mWm.mGlobalLock) { + assertTrue(mTarget.isPositioningLocked()); + assertNotNull(mTarget.getDragWindowHandleLocked()); + } + + mTarget.finishTaskPositioning(); + // Wait until the looper processes finishTaskPositioning. + assertTrue(mWm.mH.runWithScissors(() -> { }, TIMEOUT_MS)); + + assertFalse(mTarget.isPositioningLocked()); + assertNull(mTarget.getDragWindowHandleLocked()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java new file mode 100644 index 000000000000..1c6afd545b1f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link TaskSnapshotCache}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskSnapshotCacheTest + */ +@SmallTest +@Presubmit +public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { + + private TaskSnapshotCache mCache; + + @Override + @Before + public void setUp() { + super.setUp(); + + mCache = new TaskSnapshotCache(mWm, mLoader); + } + + @Test + public void testAppRemoved() { + final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + mCache.putSnapshot(window.getTask(), createSnapshot()); + assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + mCache.onAppRemoved(window.mAppToken); + assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + } + + @Test + public void testAppDied() { + final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + mCache.putSnapshot(window.getTask(), createSnapshot()); + assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + mCache.onAppDied(window.mAppToken); + assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + } + + @Test + public void testTaskRemoved() { + final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + mCache.putSnapshot(window.getTask(), createSnapshot()); + assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + mCache.onTaskRemoved(window.getTask().mTaskId); + assertNull(mCache.getSnapshot(window.getTask().mTaskId, 0 /* userId */, + false /* restoreFromDisk */, false /* reducedResolution */)); + } + + @Test + public void testReduced_notCached() { + final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); + mPersister.waitForQueueEmpty(); + assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, + false /* restoreFromDisk */, false /* reducedResolution */)); + + // Load it from disk + assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, + true /* restoreFromDisk */, true /* reducedResolution */)); + + // Make sure it's not in the cache now. + assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, + false /* restoreFromDisk */, false /* reducedResolution */)); + } + + @Test + public void testRestoreFromDisk() { + final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); + mPersister.waitForQueueEmpty(); + assertNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, + false /* restoreFromDisk */, false /* reducedResolution */)); + + // Load it from disk + assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, + true /* restoreFromDisk */, false /* reducedResolution */)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java new file mode 100644 index 000000000000..792e8a6f7582 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.TRANSIT_UNSET; + +import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME; +import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL; + +import static junit.framework.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; + +import androidx.test.filters.SmallTest; + +import com.google.android.collect.Sets; + +import org.junit.Test; + +/** + * Test class for {@link TaskSnapshotController}. + * + * Build/Install/Run: + * * atest FrameworksServicesTests:TaskSnapshotControllerTest + */ +@SmallTest +@Presubmit +public class TaskSnapshotControllerTest extends WindowTestsBase { + + @Test + public void testGetClosingApps_closing() { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet closingTasks = new ArraySet<>(); + mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(1, closingTasks.size()); + assertEquals(closingWindow.mAppToken.getTask(), closingTasks.valueAt(0)); + } + + @Test + public void testGetClosingApps_notClosing() { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + final WindowState openingWindow = createAppWindow(closingWindow.getTask(), + FIRST_APPLICATION_WINDOW, "openingWindow"); + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet closingTasks = new ArraySet<>(); + mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(0, closingTasks.size()); + } + + @Test + public void testGetClosingApps_skipClosingAppsSnapshotTasks() { + final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, + "closingWindow"); + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + final ArraySet closingApps = new ArraySet<>(); + closingApps.add(closingWindow.mAppToken); + final ArraySet closingTasks = new ArraySet<>(); + mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks( + Sets.newArraySet(closingWindow.mAppToken.getTask())); + mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); + assertEquals(0, closingTasks.size()); + } + + @Test + public void testGetSnapshotMode() { + final WindowState disabledWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); + disabledWindow.mAppToken.setDisablePreviewScreenshots(true); + assertEquals(SNAPSHOT_MODE_APP_THEME, + mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask())); + + final WindowState normalWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); + assertEquals(SNAPSHOT_MODE_REAL, + mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask())); + + final WindowState secureWindow = createWindow(null, + FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow"); + secureWindow.mAttrs.flags |= FLAG_SECURE; + assertEquals(SNAPSHOT_MODE_APP_THEME, + mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask())); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java new file mode 100644 index 000000000000..b0eafeeae043 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.app.ActivityManager.TaskSnapshot; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.util.ArraySet; +import android.view.View; + +import androidx.test.filters.MediumTest; + +import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; + +import org.junit.Test; + +import java.io.File; +import java.util.function.Predicate; + +/** + * Test class for {@link TaskSnapshotPersister} and {@link TaskSnapshotLoader} + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskSnapshotPersisterLoaderTest + */ +@MediumTest +@Presubmit +public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { + + private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); + + @Test + public void testPersistAndLoadSnapshot() { + mPersister.persistSnapshot(1 , mTestUserId, createSnapshot()); + mPersister.waitForQueueEmpty(); + final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + assertTrueForFiles(files, File::exists, " must exist"); + final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* reduced */); + assertNotNull(snapshot); + assertEquals(TEST_INSETS, snapshot.getContentInsets()); + assertNotNull(snapshot.getSnapshot()); + assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); + } + + private static void assertTrueForFiles(File[] files, Predicate predicate, + String message) { + for (File file : files) { + assertTrue(file.getName() + message, predicate.test(file)); + } + } + + @Test + public void testTaskRemovedFromRecents() { + mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); + mPersister.onTaskRemovedFromRecents(1, mTestUserId); + mPersister.waitForQueueEmpty(); + assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists()); + assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists()); + assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists()); + } + + /** + * Tests that persisting a couple of snapshots is being throttled. + */ + @Test + public void testThrottling() { + long ms = SystemClock.elapsedRealtime(); + mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); + mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); + mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); + mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); + mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); + mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); + mPersister.waitForQueueEmpty(); + assertTrue(SystemClock.elapsedRealtime() - ms > 500); + } + + /** + * Tests that too many store write queue items are being purged. + */ + @Test + public void testPurging() { + mPersister.persistSnapshot(100, mTestUserId, createSnapshot()); + mPersister.waitForQueueEmpty(); + mPersister.setPaused(true); + mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); + mPersister.removeObsoleteFiles(new ArraySet<>(), new int[] { mTestUserId }); + mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); + mPersister.persistSnapshot(3, mTestUserId, createSnapshot()); + mPersister.persistSnapshot(4, mTestUserId, createSnapshot()); + mPersister.setPaused(false); + mPersister.waitForQueueEmpty(); + + // Make sure 1,2 were purged but removeObsoleteFiles wasn't. + final File[] existsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/3.proto"), + new File(FILES_DIR.getPath() + "/snapshots/4.proto")}; + final File[] nonExistsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/100.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1.proto")}; + assertTrueForFiles(existsFiles, File::exists, " must exist"); + assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); + } + + @Test + public void testGetTaskId() { + RemoveObsoleteFilesQueueItem removeObsoleteFilesQueueItem = + mPersister.new RemoveObsoleteFilesQueueItem(new ArraySet<>(), new int[] {}); + assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("blablablulp")); + assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("nothing.err")); + assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("/invalid/")); + assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.jpg")); + assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.proto")); + assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1.jpg")); + assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1_reduced.jpg")); + } + + @Test + public void testLowResolutionPersistAndLoadSnapshot() { + TaskSnapshot a = createSnapshot(0.5f /* reducedResolution */); + assertTrue(a.isReducedResolution()); + mPersister.persistSnapshot(1 , mTestUserId, a); + mPersister.waitForQueueEmpty(); + final File[] files = new File[] { new File(FILES_DIR.getPath() + "/snapshots/1.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + final File[] nonExistsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + }; + assertTrueForFiles(files, File::exists, " must exist"); + assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); + final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* reduced */); + assertNotNull(snapshot); + assertEquals(TEST_INSETS, snapshot.getContentInsets()); + assertNotNull(snapshot.getSnapshot()); + assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); + + final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId, false /* reduced */); + assertNull(snapshotNotExist); + } + + @Test + public void testIsRealSnapshotPersistAndLoadSnapshot() { + TaskSnapshot a = new TaskSnapshotBuilder() + .setIsRealSnapshot(true) + .build(); + TaskSnapshot b = new TaskSnapshotBuilder() + .setIsRealSnapshot(false) + .build(); + assertTrue(a.isRealSnapshot()); + assertFalse(b.isRealSnapshot()); + mPersister.persistSnapshot(1, mTestUserId, a); + mPersister.persistSnapshot(2, mTestUserId, b); + mPersister.waitForQueueEmpty(); + final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); + final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); + assertNotNull(snapshotA); + assertNotNull(snapshotB); + assertTrue(snapshotA.isRealSnapshot()); + assertFalse(snapshotB.isRealSnapshot()); + } + + @Test + public void testWindowingModePersistAndLoadSnapshot() { + TaskSnapshot a = new TaskSnapshotBuilder() + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .build(); + TaskSnapshot b = new TaskSnapshotBuilder() + .setWindowingMode(WINDOWING_MODE_PINNED) + .build(); + assertEquals(WINDOWING_MODE_FULLSCREEN, a.getWindowingMode()); + assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode()); + mPersister.persistSnapshot(1, mTestUserId, a); + mPersister.persistSnapshot(2, mTestUserId, b); + mPersister.waitForQueueEmpty(); + final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); + final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); + assertNotNull(snapshotA); + assertNotNull(snapshotB); + assertEquals(WINDOWING_MODE_FULLSCREEN, snapshotA.getWindowingMode()); + assertEquals(WINDOWING_MODE_PINNED, snapshotB.getWindowingMode()); + } + + @Test + public void testIsTranslucentPersistAndLoadSnapshot() { + TaskSnapshot a = new TaskSnapshotBuilder() + .setIsTranslucent(true) + .build(); + TaskSnapshot b = new TaskSnapshotBuilder() + .setIsTranslucent(false) + .build(); + assertTrue(a.isTranslucent()); + assertFalse(b.isTranslucent()); + mPersister.persistSnapshot(1, mTestUserId, a); + mPersister.persistSnapshot(2, mTestUserId, b); + mPersister.waitForQueueEmpty(); + final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); + final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); + assertNotNull(snapshotA); + assertNotNull(snapshotB); + assertTrue(snapshotA.isTranslucent()); + assertFalse(snapshotB.isTranslucent()); + } + + @Test + public void testSystemUiVisibilityPersistAndLoadSnapshot() { + final int lightBarFlags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + TaskSnapshot a = new TaskSnapshotBuilder() + .setSystemUiVisibility(0) + .build(); + TaskSnapshot b = new TaskSnapshotBuilder() + .setSystemUiVisibility(lightBarFlags) + .build(); + assertEquals(0, a.getSystemUiVisibility()); + assertEquals(lightBarFlags, b.getSystemUiVisibility()); + mPersister.persistSnapshot(1, mTestUserId, a); + mPersister.persistSnapshot(2, mTestUserId, b); + mPersister.waitForQueueEmpty(); + final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, false /* reduced */); + final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, false /* reduced */); + assertNotNull(snapshotA); + assertNotNull(snapshotB); + assertEquals(0, snapshotA.getSystemUiVisibility()); + assertEquals(lightBarFlags, snapshotB.getSystemUiVisibility()); + } + + @Test + public void testRemoveObsoleteFiles() { + mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); + mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); + final ArraySet taskIds = new ArraySet<>(); + taskIds.add(1); + mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId }); + mPersister.waitForQueueEmpty(); + final File[] existsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/1.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg") }; + final File[] nonExistsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/2.proto"), + new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + assertTrueForFiles(existsFiles, File::exists, " must exist"); + assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); + } + + @Test + public void testRemoveObsoleteFiles_addedOneInTheMeantime() { + mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); + final ArraySet taskIds = new ArraySet<>(); + taskIds.add(1); + mPersister.removeObsoleteFiles(taskIds, new int[] { mTestUserId }); + mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); + mPersister.waitForQueueEmpty(); + final File[] existsFiles = new File[] { + new File(FILES_DIR.getPath() + "/snapshots/1.proto"), + new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/2.proto"), + new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; + assertTrueForFiles(existsFiles, File::exists, " must exist"); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java new file mode 100644 index 000000000000..946ffb60c759 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; +import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import android.app.ActivityManager.TaskSnapshot; +import android.content.ComponentName; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.UserManager; + +import org.junit.After; +import org.junit.Before; + +import java.io.File; + +/** + * Base class for tests that use a {@link TaskSnapshotPersister}. + */ +class TaskSnapshotPersisterTestBase extends WindowTestsBase { + + private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); + static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir(); + + TaskSnapshotPersister mPersister; + TaskSnapshotLoader mLoader; + int mTestUserId; + + @Before + public void setUp() { + final UserManager um = UserManager.get(getInstrumentation().getTargetContext()); + mTestUserId = um.getUserHandle(); + mPersister = new TaskSnapshotPersister(userId -> FILES_DIR); + mLoader = new TaskSnapshotLoader(mPersister); + mPersister.start(); + } + + @After + public void tearDown() { + cleanDirectory(); + } + + private void cleanDirectory() { + final File[] files = new File(FILES_DIR, "snapshots").listFiles(); + if (files == null) { + return; + } + for (File file : files) { + if (!file.isDirectory()) { + file.delete(); + } + } + } + + TaskSnapshot createSnapshot() { + return createSnapshot(1f /* scale */); + } + + TaskSnapshot createSnapshot(float scale) { + return new TaskSnapshotBuilder() + .setScale(scale) + .build(); + } + + /** + * Builds a TaskSnapshot. + */ + static class TaskSnapshotBuilder { + + private float mScale = 1f; + private boolean mIsRealSnapshot = true; + private boolean mIsTranslucent = false; + private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; + private int mSystemUiVisibility = 0; + + TaskSnapshotBuilder setScale(float scale) { + mScale = scale; + return this; + } + + TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) { + mIsRealSnapshot = isRealSnapshot; + return this; + } + + TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) { + mIsTranslucent = isTranslucent; + return this; + } + + TaskSnapshotBuilder setWindowingMode(int windowingMode) { + mWindowingMode = windowingMode; + return this; + } + + TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) { + mSystemUiVisibility = systemUiVisibility; + return this; + } + + TaskSnapshot build() { + final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888, + USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); + Canvas c = buffer.lockCanvas(); + c.drawColor(Color.RED); + buffer.unlockCanvasAndPost(c); + return new TaskSnapshot(new ComponentName("", ""), buffer, ORIENTATION_PORTRAIT, + TEST_INSETS, mScale < 1f /* reducedResolution */, mScale, mIsRealSnapshot, + mWindowingMode, mSystemUiVisibility, mIsTranslucent); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java new file mode 100644 index 000000000000..624ef9ba1653 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.eq; + +import android.app.ActivityManager.TaskSnapshot; +import android.content.ComponentName; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.GraphicBuffer; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.Surface; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.TaskSnapshotSurface.Window; + +import org.junit.Test; + +/** + * Test class for {@link TaskSnapshotSurface}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskSnapshotSurfaceTest + */ +@SmallTest +@Presubmit +public class TaskSnapshotSurfaceTest extends WindowTestsBase { + + private TaskSnapshotSurface mSurface; + + private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis, + int windowFlags, Rect taskBounds) { + final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); + final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer, + ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */, + WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); + mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test", + Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds, + ORIENTATION_PORTRAIT); + } + + private void setupSurface(int width, int height) { + setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, width, height)); + } + + @Test + public void fillEmptyBackground_fillHorizontally() { + setupSurface(200, 100); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(200); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); + verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); + } + + @Test + public void fillEmptyBackground_fillVertically() { + setupSurface(100, 200); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(200); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); + verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); + } + + @Test + public void fillEmptyBackground_fillBoth() { + setupSurface(200, 200); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(200); + when(mockCanvas.getHeight()).thenReturn(200); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); + verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); + verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); + } + + @Test + public void fillEmptyBackground_dontFill_sameSize() { + setupSurface(100, 100); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void fillEmptyBackground_dontFill_bitmapLarger() { + setupSurface(100, 100); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void testCalculateSnapshotCrop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_taskNotOnTop() { + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarLeft() { + setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(10, 0, 100, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotCrop_navBarRight() { + setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 90, 100), mSurface.calculateSnapshotCrop()); + } + + @Test + public void testCalculateSnapshotFrame() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 0, 10); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 100, 70), + mSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); + } + + @Test + public void testCalculateSnapshotFrame_navBarLeft() { + setupSurface(100, 100); + final Rect insets = new Rect(10, 10, 0, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + assertEquals(new Rect(0, -10, 90, 80), + mSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); + } + + @Test + public void testDrawStatusBarBackground() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, new Rect(0, 0, 50, 100), 10); + verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); + } + + @Test + public void testDrawStatusBarBackground_nullFrame() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, null, 10); + verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); + } + + @Test + public void testDrawStatusBarBackground_nope() { + setupSurface(100, 100); + final Rect insets = new Rect(0, 10, 10, 0); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawStatusBarBackground( + mockCanvas, new Rect(0, 0, 100, 100), 10); + verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); + } + + @Test + public void testDrawNavigationBarBackground() { + final Rect insets = new Rect(0, 10, 0, 10); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_left() { + final Rect insets = new Rect(10, 10, 0, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); + } + + @Test + public void testDrawNavigationBarBackground_right() { + final Rect insets = new Rect(0, 10, 10, 0); + setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + new Rect(0, 0, 100, 100)); + mSurface.setFrames(new Rect(0, 0, 100, 100), insets, insets); + final Canvas mockCanvas = mock(Canvas.class); + when(mockCanvas.getWidth()).thenReturn(100); + when(mockCanvas.getHeight()).thenReturn(100); + mSurface.mSystemBarBackgroundPainter.drawNavigationBarBackground(mockCanvas); + verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java new file mode 100644 index 000000000000..f01e9f0662c9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskStackContainersTests + */ +@SmallTest +@Presubmit +public class TaskStackContainersTests extends WindowTestsBase { + + private TaskStack mPinnedStack; + + @Before + public void setUp() throws Exception { + mPinnedStack = createStackControllerOnStackOnDisplay( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer; + // Stack should contain visible app window to be considered visible. + final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */); + assertFalse(mPinnedStack.isVisible()); + final WindowTestUtils.TestAppWindowToken pinnedApp = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + pinnedTask.addChild(pinnedApp, 0 /* addPos */); + assertTrue(mPinnedStack.isVisible()); + } + + @After + public void tearDown() throws Exception { + mPinnedStack.removeImmediately(); + } + + @Test + public void testStackPositionChildAt() { + // Test that always-on-top stack can't be moved to position other than top. + final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); + final TaskStack stack2 = createTaskStackOnDisplay(mDisplayContent); + + final WindowContainer taskStackContainer = stack1.getParent(); + + final int stack1Pos = taskStackContainer.mChildren.indexOf(stack1); + final int stack2Pos = taskStackContainer.mChildren.indexOf(stack2); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + assertThat(pinnedStackPos).isGreaterThan(stack2Pos); + assertThat(stack2Pos).isGreaterThan(stack1Pos); + + taskStackContainer.positionChildAt(WindowContainer.POSITION_BOTTOM, mPinnedStack, false); + assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); + assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + + taskStackContainer.positionChildAt(1, mPinnedStack, false); + assertEquals(taskStackContainer.mChildren.get(stack1Pos), stack1); + assertEquals(taskStackContainer.mChildren.get(stack2Pos), stack2); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + } + + @Test + public void testStackPositionBelowPinnedStack() { + // Test that no stack can be above pinned stack. + final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); + + final WindowContainer taskStackContainer = stack1.getParent(); + + final int stackPos = taskStackContainer.mChildren.indexOf(stack1); + final int pinnedStackPos = taskStackContainer.mChildren.indexOf(mPinnedStack); + assertThat(pinnedStackPos).isGreaterThan(stackPos); + + taskStackContainer.positionChildAt(WindowContainer.POSITION_TOP, stack1, false); + assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + + taskStackContainer.positionChildAt(taskStackContainer.mChildren.size() - 1, stack1, false); + assertEquals(taskStackContainer.mChildren.get(stackPos), stack1); + assertEquals(taskStackContainer.mChildren.get(pinnedStackPos), mPinnedStack); + } + + @Test + public void testDisplayPositionWithPinnedStack() { + // The display contains pinned stack that was added in {@link #setUp}. + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + + // Add another display at top. + mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(), + false /* includingParents */); + + // Move the task of {@code mDisplayContent} to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */); + final int indexOfDisplayWithPinnedStack = mWm.mRoot.mChildren.indexOf(mDisplayContent); + + assertEquals("The testing DisplayContent should be moved to top with task", + mWm.mRoot.getChildCount() - 1, indexOfDisplayWithPinnedStack); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java new file mode 100644 index 000000000000..7ac331829fb1 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Tests for the {@link TaskStack} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskStackTests + */ +@SmallTest +@Presubmit +public class TaskStackTests extends WindowTestsBase { + + @Test + public void testStackPositionChildAt() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task1 = createTaskInStack(stack, 0 /* userId */); + final Task task2 = createTaskInStack(stack, 1 /* userId */); + + // Current user task should be moved to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task1, false /* includingParents */); + assertEquals(stack.mChildren.get(0), task2); + assertEquals(stack.mChildren.get(1), task1); + + // Non-current user won't be moved to top. + stack.positionChildAt(WindowContainer.POSITION_TOP, task2, false /* includingParents */); + assertEquals(stack.mChildren.get(0), task2); + assertEquals(stack.mChildren.get(1), task1); + } + + @Test + public void testClosingAppDifferentStackOrientation() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task1 = createTaskInStack(stack, 0 /* userId */); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task1.addChild(appWindowToken1, 0); + appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + final Task task2 = createTaskInStack(stack, 1 /* userId */); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task2.addChild(appWindowToken2, 0); + appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation()); + mDisplayContent.mClosingApps.add(appWindowToken2); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation()); + } + + @Test + public void testMoveTaskToBackDifferentStackOrientation() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task1 = createTaskInStack(stack, 0 /* userId */); + WindowTestUtils.TestAppWindowToken appWindowToken1 = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task1.addChild(appWindowToken1, 0); + appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + final Task task2 = createTaskInStack(stack, 1 /* userId */); + WindowTestUtils.TestAppWindowToken appWindowToken2 = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task2.addChild(appWindowToken2, 0); + appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, stack.getOrientation()); + task2.setSendingToBottom(true); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, stack.getOrientation()); + } + + @Test + public void testStackRemoveImmediately() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + assertEquals(stack, task.mStack); + + // Remove stack and check if its child is also removed. + stack.removeImmediately(); + assertNull(stack.getDisplayContent()); + assertNull(task.mStack); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java new file mode 100644 index 000000000000..bbf508dd1630 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Test class for {@link TaskWindowContainerController}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:TaskWindowContainerControllerTests + */ +@SmallTest +@Presubmit +public class TaskWindowContainerControllerTests extends WindowTestsBase { + + /* Comment out due to removal of AppWindowContainerController + @Test + public void testRemoveContainer() { + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(this); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); + + taskController.removeContainer(); + // Assert that the container was removed. + assertNull(taskController.mContainer); + assertNull(appController.mContainer); + } + */ + + /* Comment out due to removal of AppWindowContainerController + @Test + public void testRemoveContainer_deferRemoval() { + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(this); + final WindowTestUtils.TestAppWindowContainerController appController = + new WindowTestUtils.TestAppWindowContainerController(taskController); + + final WindowTestUtils.TestTask task = (WindowTestUtils.TestTask) taskController.mContainer; + final AppWindowToken app = appController.mContainer; + task.mShouldDeferRemoval = true; + + taskController.removeContainer(); + // For the case of deferred removal the task controller will no longer be connected to the + // container, but the app controller will still be connected to the its container until + // the task window container is removed. + assertNull(taskController.mContainer); + assertNull(task.getController()); + assertNotNull(appController.mContainer); + assertNotNull(app.getController()); + + task.removeImmediately(); + assertNull(appController.mContainer); + assertNull(app.getController()); + } + */ + + @Test + public void testReparent() { + final StackWindowController stackController1 = + createStackControllerOnDisplay(mDisplayContent); + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stackController1); + final StackWindowController stackController2 = + createStackControllerOnDisplay(mDisplayContent); + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stackController2); + + boolean gotException = false; + try { + taskController.reparent(stackController1, 0, false/* moveParents */); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue("Should not be able to reparent to the same parent", gotException); + + final StackWindowController stackController3 = + createStackControllerOnDisplay(mDisplayContent); + stackController3.setContainer(null); + gotException = false; + try { + taskController.reparent(stackController3, 0, false/* moveParents */); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue("Should not be able to reparent to a stack that doesn't have a container", + gotException); + + taskController.reparent(stackController2, 0, false/* moveParents */); + assertEquals(stackController2.mContainer, taskController.mContainer.getParent()); + assertEquals(0, ((WindowTestUtils.TestTask) taskController.mContainer).positionInParent()); + assertEquals(1, ((WindowTestUtils.TestTask) taskController2.mContainer).positionInParent()); + } + + @Test + public void testReparent_BetweenDisplays() { + // Create first stack on primary display. + final StackWindowController stack1Controller = + createStackControllerOnDisplay(mDisplayContent); + final TaskStack stack1 = stack1Controller.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController = + new WindowTestUtils.TestTaskWindowContainerController(stack1Controller); + final WindowTestUtils.TestTask task1 = (WindowTestUtils.TestTask) taskController.mContainer; + task1.mOnDisplayChangedCalled = false; + assertEquals(mDisplayContent, stack1.getDisplayContent()); + + // Create second display and put second stack on it. + final DisplayContent dc = createNewDisplay(); + final StackWindowController stack2Controller = createStackControllerOnDisplay(dc); + final TaskStack stack2 = stack2Controller.mContainer; + final WindowTestUtils.TestTaskWindowContainerController taskController2 = + new WindowTestUtils.TestTaskWindowContainerController(stack2Controller); + final WindowTestUtils.TestTask task2 = + (WindowTestUtils.TestTask) taskController2.mContainer; + + // Reparent and check state + taskController.reparent(stack2Controller, 0, false /* moveParents */); + assertEquals(stack2, task1.getParent()); + assertEquals(0, task1.positionInParent()); + assertEquals(1, task2.positionInParent()); + assertTrue(task1.mOnDisplayChangedCalled); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java new file mode 100644 index 000000000000..432af0d7a469 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import android.graphics.Rect; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.MergedConfiguration; +import android.view.DisplayCutout; +import android.view.DragEvent; +import android.view.IWindow; +import android.view.InsetsState; + +import com.android.internal.os.IResultReceiver; + +public class TestIWindow extends IWindow.Stub { + @Override + public void executeCommand(String command, String parameters, + ParcelFileDescriptor descriptor) throws RemoteException { + } + + @Override + public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, + Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig, + Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId, + DisplayCutout.ParcelableWrapper displayCutout) throws RemoteException { + } + @Override + public void insetsChanged(InsetsState insetsState) throws RemoteException { + } + + @Override + public void moved(int newX, int newY) throws RemoteException { + } + + @Override + public void dispatchAppVisibility(boolean visible) throws RemoteException { + } + + @Override + public void dispatchGetNewSurface() throws RemoteException { + } + + @Override + public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) + throws RemoteException { + } + + @Override + public void closeSystemDialogs(String reason) throws RemoteException { + } + + @Override + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, boolean sync) + throws RemoteException { + } + + @Override + public void dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, + boolean sync) throws RemoteException { + } + + @Override + public void dispatchDragEvent(DragEvent event) throws RemoteException { + } + + @Override + public void updatePointerIcon(float x, float y) throws RemoteException { + } + + @Override + public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue, + int localChanges) throws RemoteException { + } + + @Override + public void dispatchWindowShown() throws RemoteException { + } + + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) + throws RemoteException { + } + + @Override + public void dispatchPointerCaptureChanged(boolean hasCapture) { + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java new file mode 100644 index 000000000000..ba81bd1c3b12 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.proto.ProtoOutputStream; +import android.view.IWindow; +import android.view.IWindowManager; +import android.view.KeyEvent; +import android.view.WindowManager; +import android.view.animation.Animation; + +import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.internal.policy.IShortcutService; +import com.android.server.policy.WindowManagerPolicy; + +import java.io.PrintWriter; +import java.util.function.Supplier; + +class TestWindowManagerPolicy implements WindowManagerPolicy { + private final Supplier mWmSupplier; + + int mRotationToReport = 0; + boolean mKeyguardShowingAndNotOccluded = false; + + private Runnable mRunnableWhenAddingSplashScreen; + + TestWindowManagerPolicy(Supplier wmSupplier) { + mWmSupplier = wmSupplier; + } + + @Override + public void registerShortcutKey(long shortcutCode, IShortcutService shortcutKeyReceiver) + throws RemoteException { + } + + @Override + public void init(Context context, IWindowManager windowManager, + WindowManagerFuncs windowManagerFuncs) { + } + + public void setDefaultDisplay(DisplayContentInfo displayContentInfo) { + } + + @Override + public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) { + return 0; + } + + @Override + public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) { + return false; + } + + @Override + public void adjustConfigurationLw(Configuration config, int keyboardPresence, + int navigationPresence) { + } + + @Override + public int getMaxWallpaperLayer() { + return 0; + } + + @Override + public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) { + return attrs.type == TYPE_STATUS_BAR; + } + + @Override + public boolean canBeHiddenByKeyguardLw(WindowState win) { + return false; + } + + /** + * Sets a runnable to run when adding a splash screen which gets executed after the window has + * been added but before returning the surface. + */ + void setRunnableWhenAddingSplashScreen(Runnable r) { + mRunnableWhenAddingSplashScreen = r; + } + + @Override + public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme, + CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, + int logo, int windowFlags, Configuration overrideConfig, int displayId) { + final com.android.server.wm.WindowState window; + final AppWindowToken atoken; + final WindowManagerService wm = mWmSupplier.get(); + synchronized (wm.mGlobalLock) { + atoken = wm.mRoot.getAppWindowToken(appToken); + IWindow iWindow = mock(IWindow.class); + doReturn(mock(IBinder.class)).when(iWindow).asBinder(); + window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken, + "Starting window", 0 /* ownerId */, false /* internalWindows */, wm, + mock(Session.class), iWindow); + atoken.startingWindow = window; + } + if (mRunnableWhenAddingSplashScreen != null) { + mRunnableWhenAddingSplashScreen.run(); + mRunnableWhenAddingSplashScreen = null; + } + return () -> { + synchronized (wm.mGlobalLock) { + atoken.removeChild(window); + atoken.startingWindow = null; + } + }; + } + + @Override + public void setKeyguardCandidateLw(WindowState win) { + } + + @Override + public Animation createHiddenByKeyguardExit(boolean onWallpaper, + boolean goingToNotificationShade) { + return null; + } + + @Override + public Animation createKeyguardWallpaperExit(boolean goingToNotificationShade) { + return null; + } + + @Override + public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { + return 0; + } + + @Override + public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags) { + return 0; + } + + @Override + public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) { + return 0; + } + + @Override + public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) { + return null; + } + + @Override + public void applyKeyguardPolicyLw(WindowState win, WindowState imeTarget) { + } + + @Override + public void setAllowLockscreenWhenOn(int displayId, boolean allow) { + } + + @Override + public void startedWakingUp() { + } + + @Override + public void finishedWakingUp() { + } + + @Override + public void startedGoingToSleep(int why) { + } + + @Override + public void finishedGoingToSleep(int why) { + } + + @Override + public void screenTurningOn(ScreenOnListener screenOnListener) { + } + + @Override + public void screenTurnedOn() { + } + + @Override + public void screenTurningOff(ScreenOffListener screenOffListener) { + } + + @Override + public void screenTurnedOff() { + } + + @Override + public boolean isScreenOn() { + return true; + } + + @Override + public boolean okToAnimate() { + return true; + } + + @Override + public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen) { + } + + @Override + public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered) { + } + + @Override + public void enableKeyguard(boolean enabled) { + } + + @Override + public void exitKeyguardSecurely(OnKeyguardExitResult callback) { + } + + @Override + public boolean isKeyguardLocked() { + return mKeyguardShowingAndNotOccluded; + } + + @Override + public boolean isKeyguardSecure(int userId) { + return false; + } + + @Override + public boolean isKeyguardOccluded() { + return false; + } + + @Override + public boolean isKeyguardTrustedLw() { + return false; + } + + @Override + public boolean isKeyguardShowingAndNotOccluded() { + return mKeyguardShowingAndNotOccluded; + } + + @Override + public boolean inKeyguardRestrictedKeyInputMode() { + return false; + } + + @Override + public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback, + CharSequence message) { + } + + @Override + public boolean isKeyguardDrawnLw() { + return false; + } + + @Override + public void onKeyguardOccludedChangedLw(boolean occluded) { + } + + public void setSafeMode(boolean safeMode) { + } + + @Override + public void systemReady() { + } + + @Override + public void systemBooted() { + } + + @Override + public void showBootMessage(CharSequence msg, boolean always) { + } + + @Override + public void hideBootMessages() { + } + + @Override + public void userActivity() { + } + + @Override + public void enableScreenAfterBoot() { + } + + @Override + public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always, + String reason) { + return false; + } + + @Override + public void keepScreenOnStartedLw() { + } + + @Override + public void keepScreenOnStoppedLw() { + } + + @Override + public boolean hasNavigationBar() { + return false; + } + + @Override + public void lockNow(Bundle options) { + } + + @Override + public void showRecentApps() { + } + + @Override + public void showGlobalActions() { + } + + @Override + public boolean isUserSetupComplete() { + return false; + } + + @Override + public int getUiMode() { + return 0; + } + + @Override + public void setCurrentUserLw(int newUserId) { + } + + @Override + public void setSwitchingUser(boolean switching) { + } + + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { + } + + @Override + public void dump(String prefix, PrintWriter writer, String[] args) { + } + + @Override + public boolean isTopLevelWindow(int windowType) { + return false; + } + + @Override + public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) { + } + + @Override + public void setPipVisibilityLw(boolean visible) { + } + + @Override + public void setRecentsVisibilityLw(boolean visible) { + } + + @Override + public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) { + } + + @Override + public void onSystemUiStarted() { + } + + @Override + public boolean canDismissBootAnimation() { + return true; + } + + @Override + public void requestUserActivityNotification() { + } + + @Override + public boolean setAodShowing(boolean aodShowing) { + return false; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java new file mode 100644 index 000000000000..612f9ad923d6 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static junit.framework.Assert.assertTrue; + +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link AppTransition}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:UnknownAppVisibilityControllerTest + */ +@SmallTest +@Presubmit +public class UnknownAppVisibilityControllerTest extends WindowTestsBase { + + @Before + public void setUp() throws Exception { + mDisplayContent.mUnknownAppVisibilityController.clear(); + } + + @Test + public void testFlow() { + final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token); + + // Make sure our handler processed the message. + SystemClock.sleep(100); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test + public void testMultiple() { + final AppWindowToken token1 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + final AppWindowToken token2 = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token2); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token1); + mDisplayContent.mUnknownAppVisibilityController.notifyAppResumedFinished(token2); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(token2); + + // Make sure our handler processed the message. + SystemClock.sleep(100); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test + public void testClear() { + final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.clear(); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test + public void testAppRemoved() { + final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(token); + mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(token); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java new file mode 100644 index 000000000000..d07230ef2ca3 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static junit.framework.TestCase.assertNotNull; + +import static org.junit.Assert.assertNull; + +import android.graphics.Bitmap; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Tests for the {@link WallpaperController} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WallpaperControllerTests + */ +@SmallTest +@Presubmit +public class WallpaperControllerTests extends WindowTestsBase { + @Test + public void testWallpaperScreenshot() { + WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class); + + synchronized (mWm.mGlobalLock) { + // No wallpaper + final DisplayContent dc = createNewDisplay(); + Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); + assertNull(wallpaperBitmap); + + // No wallpaper WSA Surface + WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), + true, dc, true /* ownerCanManageAppTokens */); + WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, + wallpaperWindowToken, "wallpaperWindow"); + wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); + assertNull(wallpaperBitmap); + + // Wallpaper with not visible WSA surface. + wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController; + wallpaperWindow.mWinAnimator.mLastAlpha = 1; + wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); + assertNull(wallpaperBitmap); + + when(windowSurfaceController.getShown()).thenReturn(true); + + // Wallpaper with WSA alpha set to 0. + wallpaperWindow.mWinAnimator.mLastAlpha = 0; + wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); + assertNull(wallpaperBitmap); + + // Wallpaper window with WSA Surface + wallpaperWindow.mWinAnimator.mLastAlpha = 1; + wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); + assertNotNull(wallpaperBitmap); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java new file mode 100644 index 000000000000..3643457f061f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2017 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOW_CONFIG_ALWAYS_ON_TOP; +import static android.app.WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; +import static android.app.WindowConfiguration.WINDOW_CONFIG_ROTATION; +import static android.app.WindowConfiguration.WINDOW_CONFIG_WINDOWING_MODE; +import static android.content.pm.ActivityInfo.CONFIG_WINDOW_CONFIGURATION; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import android.app.WindowConfiguration; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; +import android.view.Surface; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class to for {@link android.app.WindowConfiguration}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowConfigurationTests + */ +@FlakyTest(bugId = 74078662) +@SmallTest +@Presubmit +public class WindowConfigurationTests extends WindowTestsBase { + private Rect mParentBounds; + + @Before + public void setUp() throws Exception { + mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); + } + + /** Tests {@link android.app.WindowConfiguration#diff(WindowConfiguration, boolean)}. */ + @Test + public void testDiff() { + final Configuration config1 = new Configuration(); + final WindowConfiguration winConfig1 = config1.windowConfiguration; + final Configuration config2 = new Configuration(); + final WindowConfiguration winConfig2 = config2.windowConfiguration; + final Configuration config3 = new Configuration(); + final WindowConfiguration winConfig3 = config3.windowConfiguration; + final Configuration config4 = new Configuration(); + final WindowConfiguration winConfig4 = config4.windowConfiguration; + + winConfig1.setAppBounds(0, 1, 1, 0); + winConfig2.setAppBounds(1, 2, 2, 1); + winConfig3.setAppBounds(winConfig1.getAppBounds()); + winConfig4.setRotation(Surface.ROTATION_90); + + assertEquals(CONFIG_WINDOW_CONFIGURATION, config1.diff(config2)); + assertEquals(0, config1.diffPublicOnly(config2)); + assertEquals(WINDOW_CONFIG_APP_BOUNDS, + winConfig1.diff(winConfig2, false /* compareUndefined */)); + + winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE, + winConfig1.diff(winConfig2, false /* compareUndefined */)); + + winConfig2.setAlwaysOnTop(true); + assertEquals(WINDOW_CONFIG_APP_BOUNDS | WINDOW_CONFIG_WINDOWING_MODE + | WINDOW_CONFIG_ALWAYS_ON_TOP, + winConfig1.diff(winConfig2, false /* compareUndefined */)); + + assertEquals(WINDOW_CONFIG_ROTATION, + winConfig1.diff(winConfig4, false /* compareUndefined */)); + + assertEquals(0, config1.diff(config3)); + assertEquals(0, config1.diffPublicOnly(config3)); + assertEquals(0, winConfig1.diff(winConfig3, false /* compareUndefined */)); + } + + /** Tests {@link android.app.WindowConfiguration#compareTo(WindowConfiguration)}. */ + @Test + public void testConfigurationCompareTo() { + final Configuration blankConfig = new Configuration(); + final WindowConfiguration blankWinConfig = new WindowConfiguration(); + + final Configuration config1 = new Configuration(); + final WindowConfiguration winConfig1 = config1.windowConfiguration; + winConfig1.setAppBounds(1, 2, 3, 4); + + final Configuration config2 = new Configuration(config1); + final WindowConfiguration winConfig2 = config2.windowConfiguration; + + assertEquals(config1.compareTo(config2), 0); + assertEquals(winConfig1.compareTo(winConfig2), 0); + + // Different windowing mode + winConfig2.setWindowingMode(WINDOWING_MODE_FREEFORM); + assertNotEquals(config1.compareTo(config2), 0); + assertNotEquals(winConfig1.compareTo(winConfig2), 0); + winConfig2.setWindowingMode(winConfig1.getWindowingMode()); + + // Different always on top state + winConfig2.setAlwaysOnTop(true); + assertNotEquals(config1.compareTo(config2), 0); + assertNotEquals(winConfig1.compareTo(winConfig2), 0); + winConfig2.setAlwaysOnTop(winConfig1.isAlwaysOnTop()); + + // Different bounds + winConfig2.setAppBounds(0, 2, 3, 4); + assertNotEquals(config1.compareTo(config2), 0); + assertNotEquals(winConfig1.compareTo(winConfig2), 0); + winConfig2.setAppBounds(winConfig1.getAppBounds()); + + // No bounds + assertEquals(config1.compareTo(blankConfig), -1); + assertEquals(winConfig1.compareTo(blankWinConfig), -1); + + // Different rotation + winConfig2.setRotation(Surface.ROTATION_180); + assertNotEquals(config1.compareTo(config2), 0); + assertNotEquals(winConfig1.compareTo(winConfig2), 0); + winConfig2.setRotation(winConfig1.getRotation()); + + assertEquals(blankConfig.compareTo(config1), 1); + assertEquals(blankWinConfig.compareTo(winConfig1), 1); + } + + @Test + public void testSetActivityType() { + final WindowConfiguration config = new WindowConfiguration(); + config.setActivityType(ACTIVITY_TYPE_HOME); + assertEquals(ACTIVITY_TYPE_HOME, config.getActivityType()); + + // Allowed to change from app process. + config.setActivityType(ACTIVITY_TYPE_STANDARD); + assertEquals(ACTIVITY_TYPE_STANDARD, config.getActivityType()); + } + + /** Ensures the configuration app bounds at the root level match the app dimensions. */ + @Test + public void testAppBounds_RootConfigurationBounds() { + final DisplayInfo info = mDisplayContent.getDisplayInfo(); + info.appWidth = 1024; + info.appHeight = 768; + + final Rect appBounds = mWm.computeNewConfiguration( + mDisplayContent.getDisplayId()).windowConfiguration.getAppBounds(); + // The bounds should always be positioned in the top left. + assertEquals(appBounds.left, 0); + assertEquals(appBounds.top, 0); + + // The bounds should equal the defined app width and height + assertEquals(appBounds.width(), info.appWidth); + assertEquals(appBounds.height(), info.appHeight); + } + + /** Ensures that bounds are clipped to their parent. */ + @Test + public void testAppBounds_BoundsClipping() { + final Rect shiftedBounds = new Rect(mParentBounds); + shiftedBounds.offset(10, 10); + final Rect expectedBounds = new Rect(mParentBounds); + expectedBounds.intersect(shiftedBounds); + testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds, + expectedBounds); + } + + /** Ensures that empty bounds are not propagated to the configuration. */ + @Test + public void testAppBounds_EmptyBounds() { + final Rect emptyBounds = new Rect(); + testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds, + null /*ExpectedBounds*/); + } + + /** Ensures that bounds on freeform stacks are not clipped. */ + @Test + public void testAppBounds_FreeFormBounds() { + final Rect freeFormBounds = new Rect(mParentBounds); + freeFormBounds.offset(10, 10); + testStackBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds, + freeFormBounds); + } + + /** Ensures that fully contained bounds are not clipped. */ + @Test + public void testAppBounds_ContainedBounds() { + final Rect insetBounds = new Rect(mParentBounds); + insetBounds.inset(5, 5, 5, 5); + testStackBoundsConfiguration( + WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds); + } + + /** Ensures that full screen free form bounds are clipped */ + @Test + public void testAppBounds_FullScreenFreeFormBounds() { + final Rect fullScreenBounds = new Rect(0, 0, mDisplayInfo.logicalWidth, + mDisplayInfo.logicalHeight); + testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds, + mParentBounds); + } + + private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, + Rect expectedConfigBounds) { + final StackWindowController stackController = createStackControllerOnStackOnDisplay( + windowingMode, ACTIVITY_TYPE_STANDARD, mDisplayContent); + + final Configuration parentConfig = mDisplayContent.getConfiguration(); + parentConfig.windowConfiguration.setAppBounds(parentBounds); + + final Configuration config = new Configuration(); + final WindowConfiguration winConfig = config.windowConfiguration; + stackController.adjustConfigurationForBounds(bounds, null /*insetBounds*/, + new Rect() /*nonDecorBounds*/, new Rect() /*stableBounds*/, false /*overrideWidth*/, + false /*overrideHeight*/, mDisplayInfo.logicalDensityDpi, config, parentConfig, + windowingMode); + // Assert that both expected and actual are null or are equal to each other + + assertEquals(expectedConfigBounds, winConfig.getAppBounds()); + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java new file mode 100644 index 000000000000..7592f1c1fca0 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerControllerTests.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.content.res.Configuration.EMPTY; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.content.res.Configuration; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Test class for {@link WindowContainerController}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowContainerControllerTests + */ +@FlakyTest(bugId = 74078662) +@SmallTest +@Presubmit +public class WindowContainerControllerTests extends WindowTestsBase { + + @Test + public void testCreation() { + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + final WindowContainer container = new WindowContainer(mWm); + + container.setController(controller); + assertEquals(controller, container.getController()); + assertEquals(controller.mContainer, container); + } + + @Test + public void testSetContainer() { + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + final WindowContainer container = new WindowContainer(mWm); + + controller.setContainer(container); + assertEquals(controller.mContainer, container); + + // Assert we can't change the container to another one once set + boolean gotException = false; + try { + controller.setContainer(new WindowContainer(mWm)); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + + // Assert that we can set the container to null. + controller.setContainer(null); + assertNull(controller.mContainer); + } + + @Test + public void testRemoveContainer() { + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + final WindowContainer container = new WindowContainer(mWm); + + controller.setContainer(container); + assertEquals(controller.mContainer, container); + + controller.removeContainer(); + assertNull(controller.mContainer); + } + + @Test + public void testOnOverrideConfigurationChanged() { + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + final WindowContainer container = new WindowContainer(mWm); + + controller.setContainer(container); + assertEquals(controller.mContainer, container); + assertEquals(EMPTY, container.getOverrideConfiguration()); + + final Configuration config = new Configuration(); + config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + config.windowConfiguration.setAppBounds(10, 10, 10, 10); + + // Assert that the config change through the controller is propagated to the container. + controller.onOverrideConfigurationChanged(config); + assertEquals(config, container.getOverrideConfiguration()); + + // Assert the container configuration isn't changed after removal from the controller. + controller.removeContainer(); + controller.onOverrideConfigurationChanged(EMPTY); + assertEquals(config, container.getOverrideConfiguration()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java new file mode 100644 index 000000000000..a2e0ed933bb9 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.util.Comparator; + +/** + * Test class for {@link WindowContainer}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowContainerTests + */ +@SmallTest +@Presubmit +@FlakyTest(bugId = 74078662) +public class WindowContainerTests extends WindowTestsBase { + + @Test + public void testCreation() { + final TestWindowContainer w = new TestWindowContainerBuilder(mWm).setLayer(0).build(); + assertNull("window must have no parent", w.getParentWindow()); + assertEquals("window must have no children", 0, w.getChildrenCount()); + } + + @Test + public void testAdd() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer layer1 = root.addChildWindow(builder.setLayer(1)); + final TestWindowContainer secondLayer1 = root.addChildWindow(builder.setLayer(1)); + final TestWindowContainer layer2 = root.addChildWindow(builder.setLayer(2)); + final TestWindowContainer layerNeg1 = root.addChildWindow(builder.setLayer(-1)); + final TestWindowContainer layerNeg2 = root.addChildWindow(builder.setLayer(-2)); + final TestWindowContainer secondLayerNeg1 = root.addChildWindow(builder.setLayer(-1)); + final TestWindowContainer layer0 = root.addChildWindow(builder.setLayer(0)); + + assertEquals(7, root.getChildrenCount()); + + assertEquals(root, layer1.getParentWindow()); + assertEquals(root, secondLayer1.getParentWindow()); + assertEquals(root, layer2.getParentWindow()); + assertEquals(root, layerNeg1.getParentWindow()); + assertEquals(root, layerNeg2.getParentWindow()); + assertEquals(root, secondLayerNeg1.getParentWindow()); + assertEquals(root, layer0.getParentWindow()); + + assertEquals(layerNeg2, root.getChildAt(0)); + assertEquals(secondLayerNeg1, root.getChildAt(1)); + assertEquals(layerNeg1, root.getChildAt(2)); + assertEquals(layer0, root.getChildAt(3)); + assertEquals(layer1, root.getChildAt(4)); + assertEquals(secondLayer1, root.getChildAt(5)); + assertEquals(layer2, root.getChildAt(6)); + + assertTrue(layer1.mOnParentSetCalled); + assertTrue(secondLayer1.mOnParentSetCalled); + assertTrue(layer2.mOnParentSetCalled); + assertTrue(layerNeg1.mOnParentSetCalled); + assertTrue(layerNeg2.mOnParentSetCalled); + assertTrue(secondLayerNeg1.mOnParentSetCalled); + assertTrue(layer0.mOnParentSetCalled); + } + + @Test + public void testAddChildSetsSurfacePosition() { + try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) { + + final SurfaceControl.Transaction transaction = mock(SurfaceControl.Transaction.class); + mWm.mTransactionFactory = () -> transaction; + + WindowContainer child = new WindowContainer(mWm); + child.setBounds(1, 1, 10, 10); + + verify(transaction, never()).setPosition(any(), anyFloat(), anyFloat()); + top.addChild(child, 0); + verify(transaction, times(1)).setPosition(any(), eq(1.f), eq(1.f)); + } + } + + @Test + public void testAdd_AlreadyHasParent() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + + boolean gotException = false; + try { + child1.addChildWindow(child2); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + + gotException = false; + try { + root.addChildWindow(child2); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + } + + @Test + public void testHasChild() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + + assertEquals(2, root.getChildrenCount()); + assertEquals(2, child1.getChildrenCount()); + assertEquals(1, child2.getChildrenCount()); + + assertTrue(root.hasChild(child1)); + assertTrue(root.hasChild(child2)); + assertTrue(root.hasChild(child11)); + assertTrue(root.hasChild(child12)); + assertTrue(root.hasChild(child21)); + + assertTrue(child1.hasChild(child11)); + assertTrue(child1.hasChild(child12)); + assertFalse(child1.hasChild(child21)); + + assertTrue(child2.hasChild(child21)); + assertFalse(child2.hasChild(child11)); + assertFalse(child2.hasChild(child12)); + } + + @Test + public void testRemoveImmediately() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + + assertNotNull(child12.getParentWindow()); + child12.removeImmediately(); + assertNull(child12.getParentWindow()); + assertEquals(1, child1.getChildrenCount()); + assertFalse(child1.hasChild(child12)); + assertFalse(root.hasChild(child12)); + + assertTrue(root.hasChild(child2)); + assertNotNull(child2.getParentWindow()); + child2.removeImmediately(); + assertNull(child2.getParentWindow()); + assertNull(child21.getParentWindow()); + assertEquals(0, child2.getChildrenCount()); + assertEquals(1, root.getChildrenCount()); + assertFalse(root.hasChild(child2)); + assertFalse(root.hasChild(child21)); + + assertTrue(root.hasChild(child1)); + assertTrue(root.hasChild(child11)); + + root.removeImmediately(); + assertEquals(0, root.getChildrenCount()); + } + + @Test + public void testRemoveImmediately_WithController() { + final WindowContainer container = new WindowContainer(mWm); + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + + container.setController(controller); + assertEquals(controller, container.getController()); + assertEquals(container, controller.mContainer); + + container.removeImmediately(); + assertNull(container.getController()); + assertNull(controller.mContainer); + } + + @Test + public void testSetController() { + final WindowContainerController controller = new WindowContainerController<>(null, mWm); + final WindowContainer container = new WindowContainer(mWm); + + container.setController(controller); + assertEquals(controller, container.getController()); + assertEquals(container, controller.mContainer); + + // Assert we can't change the controller to another one once set + boolean gotException = false; + try { + container.setController(new WindowContainerController<>(null, mWm)); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + + // Assert that we can set the controller to null. + container.setController(null); + assertNull(container.getController()); + assertNull(controller.mContainer); + } + + @Test + public void testAddChildByIndex() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child = root.addChildWindow(); + + final TestWindowContainer child2 = builder.setLayer(1).build(); + final TestWindowContainer child3 = builder.setLayer(2).build(); + final TestWindowContainer child4 = builder.setLayer(3).build(); + + // Test adding at top. + root.addChild(child2, POSITION_TOP); + assertEquals(child2, root.getChildAt(root.getChildrenCount() - 1)); + + // Test adding at bottom. + root.addChild(child3, POSITION_BOTTOM); + assertEquals(child3, root.getChildAt(0)); + + // Test adding in the middle. + root.addChild(child4, 1); + assertEquals(child3, root.getChildAt(0)); + assertEquals(child4, root.getChildAt(1)); + assertEquals(child, root.getChildAt(2)); + assertEquals(child2, root.getChildAt(3)); + } + + @Test + public void testPositionChildAt() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child3 = root.addChildWindow(); + + // Test position at top. + root.positionChildAt(POSITION_TOP, child1, false /* includingParents */); + assertEquals(child1, root.getChildAt(root.getChildrenCount() - 1)); + + // Test position at bottom. + root.positionChildAt(POSITION_BOTTOM, child1, false /* includingParents */); + assertEquals(child1, root.getChildAt(0)); + + // Test position in the middle. + root.positionChildAt(1, child3, false /* includingParents */); + assertEquals(child1, root.getChildAt(0)); + assertEquals(child3, root.getChildAt(1)); + assertEquals(child2, root.getChildAt(2)); + } + + @Test + public void testPositionChildAtIncludeParents() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + final TestWindowContainer child13 = child1.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + final TestWindowContainer child22 = child2.addChildWindow(); + final TestWindowContainer child23 = child2.addChildWindow(); + + // Test moving to top. + child1.positionChildAt(POSITION_TOP, child11, true /* includingParents */); + assertEquals(child12, child1.getChildAt(0)); + assertEquals(child13, child1.getChildAt(1)); + assertEquals(child11, child1.getChildAt(2)); + assertEquals(child2, root.getChildAt(0)); + assertEquals(child1, root.getChildAt(1)); + + // Test moving to bottom. + child1.positionChildAt(POSITION_BOTTOM, child11, true /* includingParents */); + assertEquals(child11, child1.getChildAt(0)); + assertEquals(child12, child1.getChildAt(1)); + assertEquals(child13, child1.getChildAt(2)); + assertEquals(child1, root.getChildAt(0)); + assertEquals(child2, root.getChildAt(1)); + + // Test moving to middle, includeParents shouldn't do anything. + child2.positionChildAt(1, child21, true /* includingParents */); + assertEquals(child11, child1.getChildAt(0)); + assertEquals(child12, child1.getChildAt(1)); + assertEquals(child13, child1.getChildAt(2)); + assertEquals(child22, child2.getChildAt(0)); + assertEquals(child21, child2.getChildAt(1)); + assertEquals(child23, child2.getChildAt(2)); + assertEquals(child1, root.getChildAt(0)); + assertEquals(child2, root.getChildAt(1)); + } + + @Test + public void testPositionChildAtInvalid() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + + boolean gotException = false; + try { + // Check response to negative position. + root.positionChildAt(-1, child1, false /* includingParents */); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + + gotException = false; + try { + // Check response to position that's bigger than child number. + root.positionChildAt(3, child1, false /* includingParents */); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + } + + @Test + public void testIsAnimating() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(builder.setIsAnimating(true)); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(builder.setIsAnimating(true)); + final TestWindowContainer child21 = child2.addChildWindow(); + + assertFalse(root.isAnimating()); + assertTrue(child1.isAnimating()); + assertTrue(child11.isAnimating()); + assertTrue(child12.isAnimating()); + assertFalse(child2.isAnimating()); + assertFalse(child21.isAnimating()); + + assertTrue(root.isSelfOrChildAnimating()); + assertTrue(child1.isSelfOrChildAnimating()); + assertFalse(child11.isSelfOrChildAnimating()); + assertTrue(child12.isSelfOrChildAnimating()); + assertFalse(child2.isSelfOrChildAnimating()); + assertFalse(child21.isSelfOrChildAnimating()); + } + + @Test + public void testIsVisible() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(builder.setIsVisible(true)); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(builder.setIsVisible(true)); + final TestWindowContainer child21 = child2.addChildWindow(); + + assertFalse(root.isVisible()); + assertTrue(child1.isVisible()); + assertFalse(child11.isVisible()); + assertTrue(child12.isVisible()); + assertFalse(child2.isVisible()); + assertFalse(child21.isVisible()); + } + + @Test + public void testOverrideConfigurationAncestorNotification() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer grandparent = builder.setLayer(0).build(); + + final TestWindowContainer parent = grandparent.addChildWindow(); + final TestWindowContainer child = parent.addChildWindow(); + child.onOverrideConfigurationChanged(new Configuration()); + + assertTrue(grandparent.mOnDescendantOverrideCalled); + } + + @Test + public void testRemoveChild() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + + assertTrue(root.hasChild(child2)); + assertTrue(root.hasChild(child21)); + root.removeChild(child2); + assertFalse(root.hasChild(child2)); + assertFalse(root.hasChild(child21)); + assertNull(child2.getParentWindow()); + + boolean gotException = false; + assertTrue(root.hasChild(child11)); + try { + // Can only detach our direct children. + root.removeChild(child11); + } catch (IllegalArgumentException e) { + gotException = true; + } + assertTrue(gotException); + } + + @Test + public void testGetOrientation_childSpecified() { + testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_LANDSCAPE, + SCREEN_ORIENTATION_LANDSCAPE); + testGetOrientation_childSpecifiedConfig(false, SCREEN_ORIENTATION_UNSET, + SCREEN_ORIENTATION_UNSPECIFIED); + } + + private void testGetOrientation_childSpecifiedConfig(boolean childVisible, int childOrientation, + int expectedOrientation) { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + root.setFillsParent(true); + + builder.setIsVisible(childVisible); + + if (childOrientation != SCREEN_ORIENTATION_UNSET) { + builder.setOrientation(childOrientation); + } + + final TestWindowContainer child1 = root.addChildWindow(builder); + child1.setFillsParent(true); + + assertEquals(expectedOrientation, root.getOrientation()); + } + + @Test + public void testGetOrientation_Unset() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); + // Unspecified well because we didn't specify anything... + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, root.getOrientation()); + } + + @Test + public void testGetOrientation_InvisibleParentUnsetVisibleChildren() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); + + builder.setIsVisible(false).setLayer(-1); + final TestWindowContainer invisible = root.addChildWindow(builder); + builder.setIsVisible(true).setLayer(-2); + final TestWindowContainer invisibleChild1VisibleAndSet = invisible.addChildWindow(builder); + invisibleChild1VisibleAndSet.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + // Landscape well because the container is visible and that is what we set on it above. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisibleChild1VisibleAndSet.getOrientation()); + // Landscape because even though the container isn't visible it has a child that is + // specifying it can influence the orientation by being visible. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, invisible.getOrientation()); + // Landscape because the grandchild is visible and therefore can participate. + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); + + builder.setIsVisible(true).setLayer(-3); + final TestWindowContainer visibleUnset = root.addChildWindow(builder); + visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); + assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnset.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, root.getOrientation()); + } + + @Test + public void testGetOrientation_setBehind() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); + + builder.setIsVisible(true).setLayer(-1); + final TestWindowContainer visibleUnset = root.addChildWindow(builder); + visibleUnset.setOrientation(SCREEN_ORIENTATION_UNSET); + + builder.setIsVisible(true).setLayer(-2); + final TestWindowContainer visibleUnsetChild1VisibleSetBehind = + visibleUnset.addChildWindow(builder); + visibleUnsetChild1VisibleSetBehind.setOrientation(SCREEN_ORIENTATION_BEHIND); + // Setting to visible behind will be used by the parents if there isn't another other + // container behind this one that has an orientation set. + assertEquals(SCREEN_ORIENTATION_BEHIND, + visibleUnsetChild1VisibleSetBehind.getOrientation()); + assertEquals(SCREEN_ORIENTATION_BEHIND, visibleUnset.getOrientation()); + assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); + } + + @Test + public void testGetOrientation_fillsParent() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).setIsVisible(true).build(); + + builder.setIsVisible(true).setLayer(-1); + final TestWindowContainer visibleUnset = root.addChildWindow(builder); + visibleUnset.setOrientation(SCREEN_ORIENTATION_BEHIND); + + builder.setLayer(1).setIsVisible(true); + final TestWindowContainer visibleUnspecifiedRootChild = root.addChildWindow(builder); + visibleUnspecifiedRootChild.setFillsParent(false); + visibleUnspecifiedRootChild.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); + // Unset because the child doesn't fill the parent. May as well be invisible... + assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); + // The parent uses whatever orientation is set behind this container since it doesn't fill + // the parent. + assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); + + // Test case of child filling its parent, but its parent isn't filling its own parent. + builder.setLayer(2).setIsVisible(true); + final TestWindowContainer visibleUnspecifiedRootChildChildFillsParent = + visibleUnspecifiedRootChild.addChildWindow(builder); + visibleUnspecifiedRootChildChildFillsParent.setOrientation( + SCREEN_ORIENTATION_PORTRAIT); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, + visibleUnspecifiedRootChildChildFillsParent.getOrientation()); + assertEquals(SCREEN_ORIENTATION_UNSET, visibleUnspecifiedRootChild.getOrientation()); + assertEquals(SCREEN_ORIENTATION_BEHIND, root.getOrientation()); + + + visibleUnspecifiedRootChild.setFillsParent(true); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, visibleUnspecifiedRootChild.getOrientation()); + assertEquals(SCREEN_ORIENTATION_PORTRAIT, root.getOrientation()); + } + + @Test + public void testCompareTo() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.setLayer(0).build(); + + final TestWindowContainer child1 = root.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + + final TestWindowContainer child2 = root.addChildWindow(); + final TestWindowContainer child21 = child2.addChildWindow(); + final TestWindowContainer child22 = child2.addChildWindow(); + final TestWindowContainer child222 = child22.addChildWindow(); + final TestWindowContainer child223 = child22.addChildWindow(); + final TestWindowContainer child2221 = child222.addChildWindow(); + final TestWindowContainer child2222 = child222.addChildWindow(); + final TestWindowContainer child2223 = child222.addChildWindow(); + + final TestWindowContainer root2 = builder.setLayer(0).build(); + + assertEquals(0, root.compareTo(root)); + assertEquals(-1, child1.compareTo(child2)); + assertEquals(1, child2.compareTo(child1)); + + boolean inTheSameTree = true; + try { + root.compareTo(root2); + } catch (IllegalArgumentException e) { + inTheSameTree = false; + } + assertFalse(inTheSameTree); + + assertEquals(-1, child1.compareTo(child11)); + assertEquals(1, child21.compareTo(root)); + assertEquals(1, child21.compareTo(child12)); + assertEquals(-1, child11.compareTo(child2)); + assertEquals(1, child2221.compareTo(child11)); + assertEquals(-1, child2222.compareTo(child223)); + assertEquals(1, child2223.compareTo(child21)); + } + + @Test + public void testPrefixOrderIndex() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.build(); + + final TestWindowContainer child1 = root.addChildWindow(); + + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + + final TestWindowContainer child2 = root.addChildWindow(); + + final TestWindowContainer child21 = child2.addChildWindow(); + final TestWindowContainer child22 = child2.addChildWindow(); + + final TestWindowContainer child221 = child22.addChildWindow(); + final TestWindowContainer child222 = child22.addChildWindow(); + final TestWindowContainer child223 = child22.addChildWindow(); + + final TestWindowContainer child23 = child2.addChildWindow(); + + assertEquals(0, root.getPrefixOrderIndex()); + assertEquals(1, child1.getPrefixOrderIndex()); + assertEquals(2, child11.getPrefixOrderIndex()); + assertEquals(3, child12.getPrefixOrderIndex()); + assertEquals(4, child2.getPrefixOrderIndex()); + assertEquals(5, child21.getPrefixOrderIndex()); + assertEquals(6, child22.getPrefixOrderIndex()); + assertEquals(7, child221.getPrefixOrderIndex()); + assertEquals(8, child222.getPrefixOrderIndex()); + assertEquals(9, child223.getPrefixOrderIndex()); + assertEquals(10, child23.getPrefixOrderIndex()); + } + + @Test + public void testPrefixOrder_addEntireSubtree() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.build(); + final TestWindowContainer subtree = builder.build(); + final TestWindowContainer subtree2 = builder.build(); + + final TestWindowContainer child1 = subtree.addChildWindow(); + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child2 = subtree2.addChildWindow(); + final TestWindowContainer child3 = subtree2.addChildWindow(); + subtree.addChild(subtree2, 1); + root.addChild(subtree, 0); + + assertEquals(0, root.getPrefixOrderIndex()); + assertEquals(1, subtree.getPrefixOrderIndex()); + assertEquals(2, child1.getPrefixOrderIndex()); + assertEquals(3, child11.getPrefixOrderIndex()); + assertEquals(4, subtree2.getPrefixOrderIndex()); + assertEquals(5, child2.getPrefixOrderIndex()); + assertEquals(6, child3.getPrefixOrderIndex()); + } + + @Test + public void testPrefixOrder_remove() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.build(); + + final TestWindowContainer child1 = root.addChildWindow(); + + final TestWindowContainer child11 = child1.addChildWindow(); + final TestWindowContainer child12 = child1.addChildWindow(); + + final TestWindowContainer child2 = root.addChildWindow(); + + assertEquals(0, root.getPrefixOrderIndex()); + assertEquals(1, child1.getPrefixOrderIndex()); + assertEquals(2, child11.getPrefixOrderIndex()); + assertEquals(3, child12.getPrefixOrderIndex()); + assertEquals(4, child2.getPrefixOrderIndex()); + + root.removeChild(child1); + + assertEquals(1, child2.getPrefixOrderIndex()); + } + + /** + * Ensure children of a {@link WindowContainer} do not have + * {@link WindowContainer#onParentResize()} called when {@link WindowContainer#onParentResize()} + * is invoked with overridden bounds. + */ + @Test + public void testOnParentResizePropagation() { + final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); + final TestWindowContainer root = builder.build(); + + final TestWindowContainer child = root.addChildWindow(); + child.setBounds(new Rect(1, 1, 2, 2)); + + final TestWindowContainer grandChild = mock(TestWindowContainer.class); + + child.addChildWindow(grandChild); + root.onResize(); + + // Make sure the child does not propagate resize through onParentResize when bounds are set. + verify(grandChild, never()).onParentResize(); + + child.removeChild(grandChild); + + child.setBounds(null); + child.addChildWindow(grandChild); + root.onResize(); + + // Make sure the child propagates resize through onParentResize when no bounds set. + verify(grandChild, times(1)).onParentResize(); + } + + /* Used so we can gain access to some protected members of the {@link WindowContainer} class */ + private static class TestWindowContainer extends WindowContainer { + private final int mLayer; + private boolean mIsAnimating; + private boolean mIsVisible; + private boolean mFillsParent; + private Integer mOrientation; + + private boolean mOnParentSetCalled; + private boolean mOnDescendantOverrideCalled; + + /** + * Compares 2 window layers and returns -1 if the first is lesser than the second in terms + * of z-order and 1 otherwise. + */ + private static final Comparator SUBLAYER_COMPARATOR = (w1, w2) -> { + final int layer1 = w1.mLayer; + final int layer2 = w2.mLayer; + if (layer1 < layer2 || (layer1 == layer2 && layer2 < 0)) { + // We insert the child window into the list ordered by the mLayer. For same layers, + // the negative one should go below others; the positive one should go above others. + return -1; + } + if (layer1 == layer2) return 0; + return 1; + }; + + TestWindowContainer(WindowManagerService wm, int layer, boolean isAnimating, + boolean isVisible, Integer orientation) { + super(wm); + + mLayer = layer; + mIsAnimating = isAnimating; + mIsVisible = isVisible; + mFillsParent = true; + mOrientation = orientation; + } + + TestWindowContainer getParentWindow() { + return (TestWindowContainer) getParent(); + } + + int getChildrenCount() { + return mChildren.size(); + } + + TestWindowContainer addChildWindow(TestWindowContainer child) { + addChild(child, SUBLAYER_COMPARATOR); + return child; + } + + TestWindowContainer addChildWindow(TestWindowContainerBuilder childBuilder) { + TestWindowContainer child = childBuilder.build(); + addChild(child, SUBLAYER_COMPARATOR); + return child; + } + + TestWindowContainer addChildWindow() { + return addChildWindow(new TestWindowContainerBuilder(mService).setLayer(1)); + } + + @Override + void onParentSet() { + mOnParentSetCalled = true; + } + + @Override + void onDescendantOverrideConfigurationChanged() { + mOnDescendantOverrideCalled = true; + super.onDescendantOverrideConfigurationChanged(); + } + + @Override + boolean isSelfAnimating() { + return mIsAnimating; + } + + @Override + boolean isVisible() { + return mIsVisible; + } + + @Override + int getOrientation(int candidate) { + return mOrientation != null ? mOrientation : super.getOrientation(candidate); + } + + @Override + int getOrientation() { + return getOrientation(super.mOrientation); + } + + @Override + boolean fillsParent() { + return mFillsParent; + } + + void setFillsParent(boolean fillsParent) { + mFillsParent = fillsParent; + } + } + + private static class TestWindowContainerBuilder { + private final WindowManagerService mWm; + private int mLayer; + private boolean mIsAnimating; + private boolean mIsVisible; + private Integer mOrientation; + + TestWindowContainerBuilder(WindowManagerService wm) { + mWm = wm; + mLayer = 0; + mIsAnimating = false; + mIsVisible = false; + mOrientation = null; + } + + TestWindowContainerBuilder setLayer(int layer) { + mLayer = layer; + return this; + } + + TestWindowContainerBuilder setIsAnimating(boolean isAnimating) { + mIsAnimating = isAnimating; + return this; + } + + TestWindowContainerBuilder setIsVisible(boolean isVisible) { + mIsVisible = isVisible; + return this; + } + + TestWindowContainerBuilder setOrientation(int orientation) { + mOrientation = orientation; + return this; + } + + TestWindowContainer build() { + return new TestWindowContainer(mWm, mLayer, mIsAnimating, mIsVisible, mOrientation); + } + } + + private static class MockSurfaceBuildingContainer extends WindowContainer + implements AutoCloseable { + private final SurfaceSession mSession = new SurfaceSession(); + + MockSurfaceBuildingContainer(WindowManagerService wm) { + super(wm); + } + + static class MockSurfaceBuilder extends SurfaceControl.Builder { + MockSurfaceBuilder(SurfaceSession ss) { + super(ss); + } + + @Override + public SurfaceControl build() { + return mock(SurfaceControl.class); + } + } + + @Override + SurfaceControl.Builder makeChildSurface(WindowContainer child) { + return new MockSurfaceBuilder(mSession); + } + + @Override + public void close() { + mSession.kill(); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java new file mode 100644 index 000000000000..4b666f538ea2 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.Matchers.eq; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +import java.util.function.Consumer; + +/** + * Tests for {@link WindowContainer#forAllWindows} and various implementations. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowContainerTraversalTests + */ +@SmallTest +@Presubmit +public class WindowContainerTraversalTests extends WindowTestsBase { + + @Test + public void testDockedDividerPosition() { + final WindowState splitScreenWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); + + mDisplayContent.mInputMethodTarget = splitScreenWindow; + + Consumer c = mock(Consumer.class); + mDisplayContent.forAllWindows(c, false); + + verify(c).accept(eq(mDockedDividerWindow)); + verify(c).accept(eq(mImeWindow)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java new file mode 100644 index 000000000000..b3e90debc84b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -0,0 +1,522 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; +import static android.view.DisplayCutout.fromBoundingRect; +import static android.view.WindowManager.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + +import static org.junit.Assert.assertEquals; + +import android.app.ActivityManager.TaskDescription; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.view.DisplayInfo; +import android.view.Gravity; +import android.view.IWindow; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowFrameTests + */ +@SmallTest +@Presubmit +public class WindowFrameTests extends WindowTestsBase { + + private WindowToken mWindowToken; + private final IWindow mIWindow = new TestIWindow(); + private final Rect mEmptyRect = new Rect(); + + static class WindowStateWithTask extends WindowState { + final Task mTask; + boolean mDockedResizingForTest = false; + WindowStateWithTask(WindowManagerService wm, IWindow iWindow, WindowToken windowToken, + WindowManager.LayoutParams attrs, Task t) { + super(wm, null, iWindow, windowToken, null, 0, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + mTask = t; + } + + @Override + Task getTask() { + return mTask; + } + + @Override + boolean isDockedResizing() { + return mDockedResizingForTest; + } + } + + private static class TaskWithBounds extends Task { + final Rect mBounds; + final Rect mInsetBounds = new Rect(); + boolean mFullscreenForTest = true; + + TaskWithBounds(TaskStack stack, WindowManagerService wm, Rect bounds) { + super(0, stack, 0, wm, 0, false, new TaskDescription(), null); + mBounds = bounds; + setBounds(bounds); + } + + @Override + public Rect getBounds() { + return mBounds; + } + + @Override + public void getBounds(Rect out) { + out.set(mBounds); + } + + @Override + public void getOverrideBounds(Rect outBounds) { + outBounds.set(mBounds); + } + @Override + void getTempInsetBounds(Rect outBounds) { + outBounds.set(mInsetBounds); + } + @Override + boolean isFullscreen() { + return mFullscreenForTest; + } + } + + TaskStack mStubStack; + + @Before + public void setUp() throws Exception { + mWindowToken = WindowTestUtils.createTestAppWindowToken( + mWm.getDefaultDisplayContentLocked()); + mStubStack = new TaskStack(mWm, 0, null); + } + + // Do not use this function directly in the tests below. Instead, use more explicit function + // such as assertFlame(). + private void assertRect(Rect rect, int left, int top, int right, int bottom) { + assertEquals(left, rect.left); + assertEquals(top, rect.top); + assertEquals(right, rect.right); + assertEquals(bottom, rect.bottom); + } + + private void assertContentInset(WindowState w, int left, int top, int right, int bottom) { + assertRect(w.getContentInsets(), left, top, right, bottom); + } + + private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) { + assertRect(w.getVisibleInsets(), left, top, right, bottom); + } + + private void assertStableInset(WindowState w, int left, int top, int right, int bottom) { + assertRect(w.getStableInsets(), left, top, right, bottom); + } + + private void assertFrame(WindowState w, int left, int top, int right, int bottom) { + assertRect(w.getFrameLw(), left, top, right, bottom); + } + + private void assertContentFrame(WindowState w, Rect expectedRect) { + assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + expectedRect.bottom); + } + + private void assertVisibleFrame(WindowState w, Rect expectedRect) { + assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + expectedRect.bottom); + } + + private void assertStableFrame(WindowState w, Rect expectedRect) { + assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right, + expectedRect.bottom); + } + + private void assertPolicyCrop(WindowStateWithTask w, int left, int top, int right, int bottom) { + Rect policyCrop = new Rect(); + w.calculatePolicyCrop(policyCrop); + assertRect(policyCrop, left, top, right, bottom); + } + + @Test + public void testLayoutInFullscreenTaskInsets() { + // fullscreen task doesn't use bounds for computeFrame + final Task task = new TaskWithBounds(mStubStack, mWm, null); + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final int bottomContentInset = 100; + final int topContentInset = 50; + final int bottomVisibleInset = 30; + final int topVisibleInset = 70; + final int leftStableInset = 20; + final int rightStableInset = 90; + + // With no insets or system decor all the frames incoming from PhoneWindowManager + // are identical. + final Rect pf = new Rect(0, 0, 1000, 1000); + final Rect df = pf; + final Rect of = df; + final Rect cf = new Rect(pf); + // Produce some insets + cf.top += 50; + cf.bottom -= 100; + final Rect vf = new Rect(pf); + vf.top += topVisibleInset; + vf.bottom -= bottomVisibleInset; + final Rect sf = new Rect(pf); + sf.left += leftStableInset; + sf.right -= rightStableInset; + + final Rect dcf = pf; + // When mFrame extends past cf, the content insets are + // the difference between mFrame and ContentFrame. Visible + // and stable frames work the same way. + w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); + w.computeFrameLw(); + assertFrame(w, 0, 0, 1000, 1000); + assertContentInset(w, 0, topContentInset, 0, bottomContentInset); + assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset); + assertStableInset(w, leftStableInset, 0, rightStableInset, 0); + assertContentFrame(w, cf); + assertVisibleFrame(w, vf); + assertStableFrame(w, sf); + // On the other hand getFrame() doesn't extend past cf we won't get any insets + w.mAttrs.x = 100; + w.mAttrs.y = 100; + w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT + w.mRequestedWidth = 100; + w.mRequestedHeight = 100; + w.computeFrameLw(); + assertFrame(w, 100, 100, 200, 200); + assertContentInset(w, 0, 0, 0, 0); + // In this case the frames are shrunk to the window frame. + assertContentFrame(w, w.getFrameLw()); + assertVisibleFrame(w, w.getFrameLw()); + assertStableFrame(w, w.getFrameLw()); + } + + @Test + public void testLayoutInFullscreenTaskNoInsets() { + // fullscreen task doesn't use bounds for computeFrame + final Task task = new TaskWithBounds(mStubStack, mWm, null); + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + // With no insets or system decor all the frames incoming from PhoneWindowManager + // are identical. + final Rect pf = new Rect(0, 0, 1000, 1000); + + // Here the window has FILL_PARENT, FILL_PARENT + // so we expect it to fill the entire available frame. + w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf, pf, pf); + w.computeFrameLw(); + assertFrame(w, 0, 0, 1000, 1000); + + // It can select various widths and heights within the bounds. + // Strangely the window attribute width is ignored for normal windows + // and we use mRequestedWidth/mRequestedHeight + w.mAttrs.width = 300; + w.mAttrs.height = 300; + w.computeFrameLw(); + // Explicit width and height without requested width/height + // gets us nothing. + assertFrame(w, 0, 0, 0, 0); + + w.mRequestedWidth = 300; + w.mRequestedHeight = 300; + w.computeFrameLw(); + // With requestedWidth/Height we can freely choose our size within the + // parent bounds. + assertFrame(w, 0, 0, 300, 300); + + // With FLAG_SCALED though, requestedWidth/height is used to control + // the unscaled surface size, and mAttrs.width/height becomes the + // layout controller. + w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED; + w.mRequestedHeight = -1; + w.mRequestedWidth = -1; + w.mAttrs.width = 100; + w.mAttrs.height = 100; + w.computeFrameLw(); + assertFrame(w, 0, 0, 100, 100); + w.mAttrs.flags = 0; + + // But sizes too large will be clipped to the containing frame + w.mRequestedWidth = 1200; + w.mRequestedHeight = 1200; + w.computeFrameLw(); + assertFrame(w, 0, 0, 1000, 1000); + + // Before they are clipped though windows will be shifted + w.mAttrs.x = 300; + w.mAttrs.y = 300; + w.mRequestedWidth = 1000; + w.mRequestedHeight = 1000; + w.computeFrameLw(); + assertFrame(w, 0, 0, 1000, 1000); + + // If there is room to move around in the parent frame the window will be shifted according + // to gravity. + w.mAttrs.x = 0; + w.mAttrs.y = 0; + w.mRequestedWidth = 300; + w.mRequestedHeight = 300; + w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; + w.computeFrameLw(); + assertFrame(w, 700, 0, 1000, 300); + w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; + w.computeFrameLw(); + assertFrame(w, 700, 700, 1000, 1000); + // Window specified x and y are interpreted as offsets in the opposite + // direction of gravity + w.mAttrs.x = 100; + w.mAttrs.y = 100; + w.computeFrameLw(); + assertFrame(w, 600, 600, 900, 900); + } + + @Test + public void testLayoutNonfullscreenTask() { + final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo(); + final int logicalWidth = displayInfo.logicalWidth; + final int logicalHeight = displayInfo.logicalHeight; + + final int taskLeft = logicalWidth / 4; + final int taskTop = logicalHeight / 4; + final int taskRight = logicalWidth / 4 * 3; + final int taskBottom = logicalHeight / 4 * 3; + final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); + final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds); + task.mFullscreenForTest = false; + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); + w.computeFrameLw(); + // For non fullscreen tasks the containing frame is based off the + // task bounds not the parent frame. + assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); + assertContentFrame(w, taskBounds); + assertContentInset(w, 0, 0, 0, 0); + + pf.set(0, 0, logicalWidth, logicalHeight); + // We still produce insets against the containing frame the same way. + final int cfRight = logicalWidth / 2; + final int cfBottom = logicalHeight / 2; + final Rect cf = new Rect(0, 0, cfRight, cfBottom); + windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); + w.computeFrameLw(); + assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); + int contentInsetRight = taskRight - cfRight; + int contentInsetBottom = taskBottom - cfBottom; + assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); + assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, + taskBottom - contentInsetBottom)); + + pf.set(0, 0, logicalWidth, logicalHeight); + // However if we set temp inset bounds, the insets will be computed + // as if our window was laid out there, but it will be laid out according to + // the task bounds. + final int insetLeft = logicalWidth / 5; + final int insetTop = logicalHeight / 5; + final int insetRight = insetLeft + (taskRight - taskLeft); + final int insetBottom = insetTop + (taskBottom - taskTop); + task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom); + windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); + w.computeFrameLw(); + assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); + contentInsetRight = insetRight - cfRight; + contentInsetBottom = insetBottom - cfBottom; + assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); + assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, + taskBottom - contentInsetBottom)); + } + + @Test + public void testCalculatePolicyCrop() { + final WindowStateWithTask w = createWindow( + new TaskWithBounds(mStubStack, mWm, null), MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); + final int logicalWidth = displayInfo.logicalWidth; + final int logicalHeight = displayInfo.logicalHeight; + final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); + final Rect df = pf; + final Rect of = df; + final Rect cf = new Rect(pf); + // Produce some insets + cf.top += displayInfo.logicalWidth / 10; + cf.bottom -= displayInfo.logicalWidth / 5; + final Rect vf = cf; + final Rect sf = vf; + // We use a decor content frame with insets to produce cropping. + Rect dcf = new Rect(cf); + + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); + w.computeFrameLw(); + assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom); + + windowFrames.mDecorFrame.setEmpty(); + // Likewise with no decor frame we would get no crop + w.computeFrameLw(); + assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight); + + // Now we set up a window which doesn't fill the entire decor frame. + // Normally it would be cropped to it's frame but in the case of docked resizing + // we need to account for the fact the windows surface will be made + // fullscreen and thus also make the crop fullscreen. + + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + w.mAttrs.width = logicalWidth / 2; + w.mAttrs.height = logicalHeight / 2; + w.mRequestedWidth = logicalWidth / 2; + w.mRequestedHeight = logicalHeight / 2; + w.computeFrameLw(); + + // Normally the crop is shrunk from the decor frame + // to the computed window frame. + assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2); + + w.mDockedResizingForTest = true; + // But if we are docked resizing it won't be, however we will still be + // shrunk to the decor frame and the display. + assertPolicyCrop(w, 0, 0, + Math.min(pf.width(), displayInfo.logicalWidth), + Math.min(pf.height(), displayInfo.logicalHeight)); + } + + @Test + public void testLayoutLetterboxedWindow() { + // First verify task behavior in multi-window mode. + final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo(); + final int logicalWidth = displayInfo.logicalWidth; + final int logicalHeight = displayInfo.logicalHeight; + + final int taskLeft = logicalWidth / 5; + final int taskTop = logicalHeight / 5; + final int taskRight = logicalWidth / 4 * 3; + final int taskBottom = logicalHeight / 4 * 3; + final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); + final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, taskBounds); + task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom); + task.mFullscreenForTest = false; + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, mEmptyRect); + w.computeFrameLw(); + // For non fullscreen tasks the containing frame is based off the + // task bounds not the parent frame. + assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); + assertContentFrame(w, taskBounds); + assertContentInset(w, 0, 0, 0, 0); + + // Now simulate switch to fullscreen for letterboxed app. + final int xInset = logicalWidth / 10; + final int yInset = logicalWidth / 10; + final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset); + Configuration config = new Configuration(w.mAppToken.getOverrideConfiguration()); + config.windowConfiguration.setBounds(cf); + w.mAppToken.onOverrideConfigurationChanged(config); + pf.set(0, 0, logicalWidth, logicalHeight); + task.mFullscreenForTest = true; + windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); + w.computeFrameLw(); + assertFrame(w, cf.left, cf.top, cf.right, cf.bottom); + assertContentFrame(w, cf); + assertContentInset(w, 0, 0, 0, 0); + } + + @Test + public void testDisplayCutout() { + // Regular fullscreen task and window + final Task task = new TaskWithBounds(mStubStack, mWm, null); + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final Rect pf = new Rect(0, 0, 1000, 2000); + // Create a display cutout of size 50x50, aligned top-center + final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( + fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP), + pf.width(), pf.height()); + + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); + windowFrames.setDisplayCutout(cutout); + w.computeFrameLw(); + + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0); + } + + @Test + public void testDisplayCutout_tempInsetBounds() { + // Regular fullscreen task and window + final TaskWithBounds task = new TaskWithBounds(mStubStack, mWm, + new Rect(0, -500, 1000, 1500)); + task.mFullscreenForTest = false; + task.mInsetBounds.set(0, 0, 1000, 2000); + WindowState w = createWindow(task, MATCH_PARENT, MATCH_PARENT); + w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; + + final Rect pf = new Rect(0, -500, 1000, 1500); + // Create a display cutout of size 50x50, aligned top-center + final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets( + fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP), + pf.width(), pf.height()); + + final WindowFrames windowFrames = w.getWindowFrames(); + windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf); + windowFrames.setDisplayCutout(cutout); + w.computeFrameLw(); + + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0); + assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0); + } + + private WindowStateWithTask createWindow(Task task, int width, int height) { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); + attrs.width = width; + attrs.height = height; + + return new WindowStateWithTask(mWm, mIWindow, mWindowToken, attrs, task); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java new file mode 100644 index 000000000000..4a99172160f5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader; +import static android.view.Display.DEFAULT_DISPLAY; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; + +import android.app.ActivityManagerInternal; +import android.content.Context; +import android.hardware.display.DisplayManagerInternal; +import android.os.PowerManagerInternal; +import android.os.PowerSaveState; +import android.view.Display; +import android.view.InputChannel; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +import com.android.server.LocalServices; +import com.android.server.input.InputManagerService; +import com.android.server.policy.WindowManagerPolicy; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.mockito.invocation.InvocationOnMock; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +/** + * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure + * to properly tear it down after. + * + *

+ * Usage: + *

+ * {@literal @}Rule
+ *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
+ * 
+ */ +public class WindowManagerServiceRule implements TestRule { + + private WindowManagerService mService; + private TestWindowManagerPolicy mPolicy; + // Record all {@link SurfaceControl.Transaction} created while testing and releases native + // resources when test finishes. + private final List> mSurfaceTransactions = new ArrayList<>(); + // Record all {@link SurfaceControl} created while testing and releases native resources when + // test finishes. + private final List> mSurfaceControls = new ArrayList<>(); + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + runWithDexmakerShareClassLoader(this::setUp); + try { + base.evaluate(); + } finally { + tearDown(); + } + } + + private void setUp() { + final Context context = getInstrumentation().getTargetContext(); + + removeServices(); + + LocalServices.addService(DisplayManagerInternal.class, + mock(DisplayManagerInternal.class)); + + LocalServices.addService(PowerManagerInternal.class, + mock(PowerManagerInternal.class)); + final PowerManagerInternal pm = + LocalServices.getService(PowerManagerInternal.class); + doNothing().when(pm).registerLowPowerModeObserver(any()); + PowerSaveState state = new PowerSaveState.Builder().build(); + doReturn(state).when(pm).getLowPowerState(anyInt()); + + LocalServices.addService(ActivityManagerInternal.class, + mock(ActivityManagerInternal.class)); + LocalServices.addService(ActivityTaskManagerInternal.class, + mock(ActivityTaskManagerInternal.class)); + final ActivityTaskManagerInternal atm = + LocalServices.getService(ActivityTaskManagerInternal.class); + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable runnable = invocationOnMock.getArgument(0); + if (runnable != null) { + runnable.run(); + } + return null; + }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt()); + + InputManagerService ims = mock(InputManagerService.class); + // InputChannel is final and can't be mocked. + InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM); + if (input != null && input.length > 1) { + doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt()); + } + + mService = WindowManagerService.main(context, ims, false, false, + mPolicy = new TestWindowManagerPolicy( + WindowManagerServiceRule.this::getWindowManagerService), + new WindowManagerGlobalLock()); + mService.mTransactionFactory = () -> { + final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + mSurfaceTransactions.add(new WeakReference<>(transaction)); + return transaction; + }; + mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) { + @Override + public SurfaceControl build() { + final SurfaceControl control = super.build(); + mSurfaceControls.add(new WeakReference<>(control)); + return control; + } + }; + + mService.onInitReady(); + + final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); + final DisplayWindowController dcw = new DisplayWindowController(display, mService); + // Display creation is driven by the ActivityManagerService via + // ActivityStackSupervisor. We emulate those steps here. + mService.mRoot.createDisplayContent(display, dcw); + } + + private void removeServices() { + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerInternal.class); + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + } + + private void tearDown() { + waitUntilWindowManagerHandlersIdle(); + destroyAllSurfaceTransactions(); + destroyAllSurfaceControls(); + removeServices(); + mService = null; + mPolicy = null; + } + }; + } + + public WindowManagerService getWindowManagerService() { + return mService; + } + + public TestWindowManagerPolicy getWindowManagerPolicy() { + return mPolicy; + } + + public void waitUntilWindowManagerHandlersIdle() { + final WindowManagerService wm = getWindowManagerService(); + if (wm != null) { + wm.mH.runWithScissors(() -> { }, 0); + wm.mAnimationHandler.runWithScissors(() -> { }, 0); + SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0); + } + } + + private void destroyAllSurfaceTransactions() { + for (final WeakReference reference : mSurfaceTransactions) { + final Transaction transaction = reference.get(); + if (transaction != null) { + reference.clear(); + transaction.close(); + } + } + } + + private void destroyAllSurfaceControls() { + for (final WeakReference reference : mSurfaceControls) { + final SurfaceControl control = reference.get(); + if (control != null) { + reference.clear(); + control.destroy(); + } + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java new file mode 100644 index 000000000000..343d35959df4 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 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 com.android.server.wm; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; + +/** + * Build/InstallRun: + * atest FrameworksServicesTests:WindowManagerServiceRuleTest + */ +@Presubmit +@SmallTest +public class WindowManagerServiceRuleTest { + + @Rule + public final WindowManagerServiceRule mRule = new WindowManagerServiceRule(); + + @Test + public void testWindowManagerSetUp() { + assertThat(mRule.getWindowManagerService(), notNullValue()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java new file mode 100644 index 000000000000..7f7803463543 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.hardware.camera2.params.OutputConfiguration.ROTATION_90; +import static android.view.Surface.ROTATION_0; +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; + +import android.graphics.Insets; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; +import android.util.Size; +import android.view.DisplayCutout; +import android.view.SurfaceControl; +import android.view.WindowManager; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import com.android.server.wm.utils.WmDisplayCutout; + +import org.junit.Test; + +import java.util.LinkedList; + +/** + * Tests for the {@link WindowState} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowStateTests + */ +@FlakyTest(bugId = 74078662) +@SmallTest +@Presubmit +public class WindowStateTests extends WindowTestsBase { + + @Test + public void testIsParentWindowHidden() { + final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); + + // parentWindow is initially set to hidden. + assertTrue(parentWindow.mHidden); + assertFalse(parentWindow.isParentWindowHidden()); + assertTrue(child1.isParentWindowHidden()); + assertTrue(child2.isParentWindowHidden()); + + parentWindow.mHidden = false; + assertFalse(parentWindow.isParentWindowHidden()); + assertFalse(child1.isParentWindowHidden()); + assertFalse(child2.isParentWindowHidden()); + } + + @Test + public void testIsChildWindow() { + final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); + final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); + + assertFalse(parentWindow.isChildWindow()); + assertTrue(child1.isChildWindow()); + assertTrue(child2.isChildWindow()); + assertFalse(randomWindow.isChildWindow()); + } + + @Test + public void testHasChild() { + final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1"); + final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11"); + final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12"); + final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2"); + final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21"); + final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); + + assertTrue(win1.hasChild(win11)); + assertTrue(win1.hasChild(win12)); + assertTrue(win2.hasChild(win21)); + + assertFalse(win1.hasChild(win21)); + assertFalse(win1.hasChild(randomWindow)); + + assertFalse(win2.hasChild(win11)); + assertFalse(win2.hasChild(win12)); + assertFalse(win2.hasChild(randomWindow)); + } + + @Test + public void testGetParentWindow() { + final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); + final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); + + assertNull(parentWindow.getParentWindow()); + assertEquals(parentWindow, child1.getParentWindow()); + assertEquals(parentWindow, child2.getParentWindow()); + } + + @Test + public void testOverlayWindowHiddenWhenSuspended() { + final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY, + "overlayWindow")); + overlayWindow.setHiddenWhileSuspended(true); + verify(overlayWindow).hideLw(true, true); + overlayWindow.setHiddenWhileSuspended(false); + verify(overlayWindow).showLw(true, true); + } + + @Test + public void testGetTopParentWindow() { + final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); + final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); + + assertEquals(root, root.getTopParentWindow()); + assertEquals(root, child1.getTopParentWindow()); + assertEquals(child1, child2.getParentWindow()); + assertEquals(root, child2.getTopParentWindow()); + + // Test case were child is detached from parent. + root.removeChild(child1); + assertEquals(child1, child1.getTopParentWindow()); + assertEquals(child1, child2.getParentWindow()); + } + + @Test + public void testIsOnScreen_hiddenByPolicy() { + final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); + window.setHasSurface(true); + assertTrue(window.isOnScreen()); + window.hideLw(false /* doAnimation */); + assertFalse(window.isOnScreen()); + } + + @Test + public void testCanBeImeTarget() { + final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); + final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow"); + + // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being + // an IME target. + appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + + // Make windows visible + appWindow.setHasSurface(true); + imeWindow.setHasSurface(true); + + // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets + assertFalse(appWindow.canBeImeTarget()); + assertFalse(imeWindow.canBeImeTarget()); + + // Add IME target flags + appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); + imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); + + // Visible app window with flags can be IME target while an IME window can never be an IME + // target regardless of its visibility or flags. + assertTrue(appWindow.canBeImeTarget()); + assertFalse(imeWindow.canBeImeTarget()); + + // Make windows invisible + appWindow.hideLw(false /* doAnimation */); + imeWindow.hideLw(false /* doAnimation */); + + // Invisible window can't be IME targets even if they have the right flags. + assertFalse(appWindow.canBeImeTarget()); + assertFalse(imeWindow.canBeImeTarget()); + } + + @Test + public void testGetWindow() { + final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); + final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild"); + final WindowState mediaOverlayChild = createWindow(root, + TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild"); + final WindowState attachedDialogChild = createWindow(root, + TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild"); + final WindowState subPanelChild = createWindow(root, + TYPE_APPLICATION_SUB_PANEL, "subPanelChild"); + final WindowState aboveSubPanelChild = createWindow(root, + TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild"); + + final LinkedList windows = new LinkedList<>(); + + root.getWindow(w -> { + windows.addLast(w); + return false; + }); + + // getWindow should have returned candidate windows in z-order. + assertEquals(aboveSubPanelChild, windows.pollFirst()); + assertEquals(subPanelChild, windows.pollFirst()); + assertEquals(attachedDialogChild, windows.pollFirst()); + assertEquals(root, windows.pollFirst()); + assertEquals(mediaOverlayChild, windows.pollFirst()); + assertEquals(mediaChild, windows.pollFirst()); + assertTrue(windows.isEmpty()); + } + + @Test + public void testPrepareWindowToDisplayDuringRelayout() { + testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/); + + // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON + // before calling prepareWindowToDisplayDuringRelayout for windows with flag in the same + // appWindowToken. + final AppWindowToken appWindowToken = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState first = createWindow(null, TYPE_APPLICATION, appWindowToken, "first"); + final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second"); + second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + + reset(sPowerManagerWrapper); + first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); + assertTrue(appWindowToken.canTurnScreenOn()); + + reset(sPowerManagerWrapper); + second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + assertFalse(appWindowToken.canTurnScreenOn()); + + // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON + // from the same appWindowToken. Only one should trigger the wakeup. + appWindowToken.setCanTurnScreenOn(true); + first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + + reset(sPowerManagerWrapper); + first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + assertFalse(appWindowToken.canTurnScreenOn()); + + reset(sPowerManagerWrapper); + second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyString()); + assertFalse(appWindowToken.canTurnScreenOn()); + + // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an + // appWindowToken. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup + final WindowToken windowToken = WindowTestUtils.createTestWindowToken(FIRST_SUB_WINDOW, + mDisplayContent); + final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken, + "firstWindow"); + final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken, + "secondWindow"); + firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + + reset(sPowerManagerWrapper); + firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + + reset(sPowerManagerWrapper); + secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + } + + @Test + public void testCanAffectSystemUiFlags() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mToken.setHidden(false); + assertTrue(app.canAffectSystemUiFlags()); + app.mToken.setHidden(true); + assertFalse(app.canAffectSystemUiFlags()); + app.mToken.setHidden(false); + app.mAttrs.alpha = 0.0f; + assertFalse(app.canAffectSystemUiFlags()); + } + + @Test + public void testCanAffectSystemUiFlags_disallow() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mToken.setHidden(false); + assertTrue(app.canAffectSystemUiFlags()); + app.getTask().setCanAffectSystemUiFlags(false); + assertFalse(app.canAffectSystemUiFlags()); + } + + @Test + public void testIsSelfOrAncestorWindowAnimating() { + final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); + final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); + assertFalse(child2.isSelfOrAncestorWindowAnimatingExit()); + child2.mAnimatingExit = true; + assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); + child2.mAnimatingExit = false; + root.mAnimatingExit = true; + assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); + } + + @Test + public void testLayoutSeqResetOnReparent() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + app.mLayoutSeq = 1; + mDisplayContent.mLayoutSeq = 1; + + app.onDisplayChanged(mDisplayContent); + + assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq))); + } + + @Test + public void testDisplayIdUpdatedOnReparent() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + // fake a different display + app.mInputWindowHandle.displayId = mDisplayContent.getDisplayId() + 1; + app.onDisplayChanged(mDisplayContent); + + assertThat(app.mInputWindowHandle.displayId, is(mDisplayContent.getDisplayId())); + assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId())); + } + + @Test + public void testSeamlesslyRotateWindow() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + + app.mHasSurface = true; + app.mSurfaceControl = mock(SurfaceControl.class); + try { + app.getFrameLw().set(10, 20, 60, 80); + app.updateSurfacePosition(t); + + app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true); + + assertTrue(app.mSeamlesslyRotated); + + // Verify we un-rotate the window state surface. + Matrix matrix = new Matrix(); + // Un-rotate 90 deg + matrix.setRotate(270); + // Translate it back to origin + matrix.postTranslate(0, mDisplayInfo.logicalWidth); + verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class)); + + // Verify we update the position as well. + float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y}; + matrix.mapPoints(currentSurfacePos); + verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]), + eq(currentSurfacePos[1])); + } finally { + app.mSurfaceControl = null; + app.mHasSurface = false; + } + } + + @Test + public void testDisplayCutoutIsCalculatedRelativeToFrame() { + final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + WindowFrames wf = app.getWindowFrames(); + wf.mParentFrame.set(7, 10, 185, 380); + wf.mDisplayFrame.set(wf.mParentFrame); + final DisplayCutout cutout = new DisplayCutout( + Insets.of(0, 15, 0, 22) /* safeInset */, + null /* boundLeft */, + new Rect(95, 0, 105, 15), + null /* boundRight */, + new Rect(95, 378, 105, 400)); + wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400))); + + app.computeFrameLw(); + assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); + } + + private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { + reset(sPowerManagerWrapper); + final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); + root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + + root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyString()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index 3c877213a0e4..3bd9a89075c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.AppOpsManager.OP_NONE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat; @@ -23,12 +26,21 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import android.app.ActivityManager; +import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.Binder; +import android.os.IBinder; import android.view.Display; +import android.view.IApplicationToken; +import android.view.IWindow; import android.view.Surface; +import android.view.SurfaceControl.Transaction; +import android.view.WindowManager; import org.mockito.invocation.InvocationOnMock; @@ -37,6 +49,7 @@ import org.mockito.invocation.InvocationOnMock; * to WindowManager related test functionality. */ public class WindowTestUtils { + private static int sNextTaskId = 0; /** An extension of {@link DisplayContent} to gain package scoped access. */ public static class TestDisplayContent extends DisplayContent { @@ -54,6 +67,14 @@ public class WindowTestUtils { return null; } + /** + * Stubbing method of non-public parent class isn't supported, so here explicitly overrides. + */ + @Override + DockedStackDividerController getDockedDividerController() { + return null; + } + /** Create a mocked default {@link DisplayContent}. */ public static TestDisplayContent create(Context context) { final TestDisplayContent displayContent = mock(TestDisplayContent.class); @@ -106,6 +127,17 @@ public class WindowTestUtils { return controller; } + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + int userId) { + synchronized (service.mGlobalLock) { + final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, + new ActivityManager.TaskDescription(), null); + stack.addTask(newTask, POSITION_TOP); + return newTask; + } + } + /** * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not * normally be mocked out. @@ -120,4 +152,233 @@ public class WindowTestUtils { // Do nothing. } } + + static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { + synchronized (dc.mService.mGlobalLock) { + return new TestAppWindowToken(dc); + } + } + + /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ + public static class TestAppWindowToken extends AppWindowToken { + boolean mOnTop = false; + private Transaction mPendingTransactionOverride; + + private TestAppWindowToken(DisplayContent dc) { + super(dc.mService, new IApplicationToken.Stub() { + @Override + public String getName() { + return null; + } + }, new ComponentName("", ""), false, dc, true /* fillsParent */); + } + + TestAppWindowToken(WindowManagerService service, IApplicationToken token, + ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, + long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, + int targetSdk, int orientation, int rotationAnimationHint, int configChanges, + boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) { + super(service, token, activityComponent, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, + orientation, rotationAnimationHint, configChanges, launchTaskBehind, + alwaysFocusable, activityRecord); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + + WindowState getFirstChild() { + return mChildren.peekFirst(); + } + + WindowState getLastChild() { + return mChildren.peekLast(); + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + void setIsOnTop(boolean onTop) { + mOnTop = onTop; + } + + @Override + boolean isOnTop() { + return mOnTop; + } + + void setPendingTransaction(Transaction transaction) { + mPendingTransactionOverride = transaction; + } + + @Override + public Transaction getPendingTransaction() { + return mPendingTransactionOverride == null + ? super.getPendingTransaction() + : mPendingTransactionOverride; + } + } + + static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { + return createTestWindowToken(type, dc, false /* persistOnEmpty */); + } + + static TestWindowToken createTestWindowToken(int type, DisplayContent dc, + boolean persistOnEmpty) { + synchronized (dc.mService.mGlobalLock) { + return new TestWindowToken(type, dc, persistOnEmpty); + } + } + + /* Used so we can gain access to some protected members of the {@link WindowToken} class */ + public static class TestWindowToken extends WindowToken { + + private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { + super(dc.mService, mock(IBinder.class), type, persistOnEmpty, dc, + false /* ownerCanManageAppTokens */); + } + + int getWindowsCount() { + return mChildren.size(); + } + + boolean hasWindow(WindowState w) { + return mChildren.contains(w); + } + } + + /* Used so we can gain access to some protected members of the {@link Task} class */ + public static class TestTask extends Task { + boolean mShouldDeferRemoval = false; + boolean mOnDisplayChangedCalled = false; + private boolean mIsAnimating = false; + + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, + int resizeMode, boolean supportsPictureInPicture, + TaskWindowContainerController controller) { + super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, + new ActivityManager.TaskDescription(), controller); + } + + boolean shouldDeferRemoval() { + return mShouldDeferRemoval; + } + + int positionInParent() { + return getParent().mChildren.indexOf(this); + } + + @Override + void onDisplayChanged(DisplayContent dc) { + super.onDisplayChanged(dc); + mOnDisplayChangedCalled = true; + } + + @Override + boolean isSelfAnimating() { + return mIsAnimating; + } + + void setLocalIsAnimating(boolean isAnimating) { + mIsAnimating = isAnimating; + } + } + + /** + * Used so we can gain access to some protected members of {@link TaskWindowContainerController} + * class. + */ + public static class TestTaskWindowContainerController extends TaskWindowContainerController { + + static final TaskWindowContainerListener NOP_LISTENER = new TaskWindowContainerListener() { + @Override + public void registerConfigurationChangeListener( + ConfigurationContainerListener listener) { + } + + @Override + public void unregisterConfigurationChangeListener( + ConfigurationContainerListener listener) { + } + + @Override + public void onSnapshotChanged(ActivityManager.TaskSnapshot snapshot) { + } + + @Override + public void requestResize(Rect bounds, int resizeMode) { + } + }; + + TestTaskWindowContainerController(WindowTestsBase testsBase) { + this(testsBase.createStackControllerOnDisplay(testsBase.mDisplayContent)); + } + + TestTaskWindowContainerController(StackWindowController stackController) { + super(sNextTaskId++, NOP_LISTENER, stackController, 0 /* userId */, null /* bounds */, + RESIZE_MODE_UNRESIZEABLE, false /* supportsPictureInPicture */, true /* toTop*/, + true /* showForAllUsers */, new ActivityManager.TaskDescription(), + stackController.mService); + } + + @Override + TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode, + boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) { + return new TestTask(taskId, stack, userId, mService, resizeMode, + supportsPictureInPicture, this); + } + } + + public static class TestIApplicationToken implements IApplicationToken { + + private final Binder mBinder = new Binder(); + @Override + public IBinder asBinder() { + return mBinder; + } + @Override + public String getName() { + return null; + } + } + + /** Used to track resize reports. */ + public static class TestWindowState extends WindowState { + boolean mResizeReported; + + TestWindowState(WindowManagerService service, Session session, IWindow window, + WindowManager.LayoutParams attrs, WindowToken token) { + super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, + false /* ownerCanAddInternalSystemWindow */); + } + + @Override + void reportResized() { + super.reportResized(); + mResizeReported = true; + } + + @Override + public boolean isGoneForLayoutLw() { + return false; + } + + @Override + void updateResizingWindowIfNeeded() { + // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive + // the system that it can actually update the window. + boolean hadSurface = mHasSurface; + mHasSurface = true; + + super.updateResizingWindowIfNeeded(); + + mHasSurface = hadSurface; + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java new file mode 100644 index 000000000000..b3f56e7ae99f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.AppOpsManager.OP_NONE; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static android.view.View.VISIBLE; +import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.hardware.display.DisplayManagerGlobal; +import android.testing.DexmakerShareClassLoaderRule; +import android.util.Log; +import android.view.Display; +import android.view.DisplayInfo; +import android.view.IWindow; +import android.view.WindowManager; + +import com.android.server.AttributeCache; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; + +import java.util.HashSet; +import java.util.LinkedList; + +/** + * Common base class for window manager unit test classes. + * + * Make sure any requests to WM hold the WM lock if needed b/73966377 + */ +class WindowTestsBase { + private static final String TAG = WindowTestsBase.class.getSimpleName(); + + WindowManagerService mWm; + private final IWindow mIWindow = new TestIWindow(); + private Session mMockSession; + // The default display is removed in {@link #setUp} and then we iterate over all displays to + // make sure we don't collide with any existing display. If we run into no other display, the + // added display should be treated as default. This cannot be the default display + private static int sNextDisplayId = DEFAULT_DISPLAY + 1; + static int sNextStackId = 1000; + + /** Non-default display. */ + DisplayContent mDisplayContent; + DisplayInfo mDisplayInfo = new DisplayInfo(); + WindowState mWallpaperWindow; + WindowState mImeWindow; + WindowState mImeDialogWindow; + WindowState mStatusBarWindow; + WindowState mDockedDividerWindow; + WindowState mNavBarWindow; + WindowState mAppWindow; + WindowState mChildAppWindowAbove; + WindowState mChildAppWindowBelow; + HashSet mCommonWindows; + + @Rule + public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + + @Rule + public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule(); + + static WindowState.PowerManagerWrapper sPowerManagerWrapper; // TODO(roosa): make non-static. + + @BeforeClass + public static void setUpOnceBase() { + AttributeCache.init(getInstrumentation().getTargetContext()); + sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); + } + + @Before + public void setUpBase() { + // If @Before throws an exception, the error isn't logged. This will make sure any failures + // in the set up are clear. This can be removed when b/37850063 is fixed. + try { + mMockSession = mock(Session.class); + + final Context context = getInstrumentation().getTargetContext(); + + mWm = mWmRule.getWindowManagerService(); + beforeCreateDisplay(); + + context.getDisplay().getDisplayInfo(mDisplayInfo); + mDisplayContent = createNewDisplay(); + mWm.mDisplayEnabled = true; + mWm.mDisplayReady = true; + + // Set-up some common windows. + mCommonWindows = new HashSet<>(); + synchronized (mWm.mGlobalLock) { + mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow"); + mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow"); + mDisplayContent.mInputMethodWindow = mImeWindow; + mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, + "mImeDialogWindow"); + mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow"); + mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow"); + mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, + "mDockedDividerWindow"); + mAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "mAppWindow"); + mChildAppWindowAbove = createCommonWindow(mAppWindow, + TYPE_APPLICATION_ATTACHED_DIALOG, + "mChildAppWindowAbove"); + mChildAppWindowBelow = createCommonWindow(mAppWindow, + TYPE_APPLICATION_MEDIA_OVERLAY, + "mChildAppWindowBelow"); + } + // Adding a display will cause freezing the display. Make sure to wait until it's + // unfrozen to not run into race conditions with the tests. + waitUntilHandlersIdle(); + } catch (Exception e) { + Log.e(TAG, "Failed to set up test", e); + throw e; + } + } + + void beforeCreateDisplay() { + // Called before display is created. + } + + @After + public void tearDownBase() { + // If @After throws an exception, the error isn't logged. This will make sure any failures + // in the tear down are clear. This can be removed when b/37850063 is fixed. + try { + // Test may schedule to perform surface placement or other messages. Wait until a + // stable state to clean up for consistency. + waitUntilHandlersIdle(); + + final LinkedList nonCommonWindows = new LinkedList<>(); + + synchronized (mWm.mGlobalLock) { + mWm.mRoot.forAllWindows(w -> { + if (!mCommonWindows.contains(w)) { + nonCommonWindows.addLast(w); + } + }, true /* traverseTopToBottom */); + + while (!nonCommonWindows.isEmpty()) { + nonCommonWindows.pollLast().removeImmediately(); + } + + for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) { + final DisplayContent displayContent = mWm.mRoot.mChildren.get(i); + if (!displayContent.isDefaultDisplay) { + displayContent.removeImmediately(); + } + } + // Remove app transition & window freeze timeout callbacks to prevent unnecessary + // actions after test. + mWm.getDefaultDisplayContentLocked().mAppTransition + .removeAppTransitionTimeoutCallbacks(); + mWm.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT); + mDisplayContent.mInputMethodTarget = null; + } + + // Wait until everything is really cleaned up. + waitUntilHandlersIdle(); + } catch (Exception e) { + Log.e(TAG, "Failed to tear down test", e); + throw e; + } + } + + private WindowState createCommonWindow(WindowState parent, int type, String name) { + synchronized (mWm.mGlobalLock) { + final WindowState win = createWindow(parent, type, name); + mCommonWindows.add(win); + // Prevent common windows from been IMe targets + win.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + return win; + } + } + + /** + * Waits until the main handler for WM has processed all messages. + */ + void waitUntilHandlersIdle() { + mWmRule.waitUntilWindowManagerHandlersIdle(); + } + + private WindowToken createWindowToken( + DisplayContent dc, int windowingMode, int activityType, int type) { + synchronized (mWm.mGlobalLock) { + if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { + return WindowTestUtils.createTestWindowToken(type, dc); + } + + return createAppWindowToken(dc, windowingMode, activityType); + } + } + + AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) { + return createTestAppWindowToken(dc, windowingMode, activityType); + } + + WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int + windowingMode, int activityType) { + final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType, + dc).mContainer; + final Task task = createTaskInStack(stack, 0 /* userId */); + final WindowTestUtils.TestAppWindowToken appWindowToken = + WindowTestUtils.createTestAppWindowToken(dc); + task.addChild(appWindowToken, 0); + return appWindowToken; + } + + WindowState createWindow(WindowState parent, int type, String name) { + synchronized (mWm.mGlobalLock) { + return (parent == null) + ? createWindow(parent, type, mDisplayContent, name) + : createWindow(parent, type, parent.mToken, name); + } + } + + WindowState createWindowOnStack(WindowState parent, int windowingMode, int activityType, + int type, DisplayContent dc, String name) { + synchronized (mWm.mGlobalLock) { + final WindowToken token = createWindowToken(dc, windowingMode, activityType, type); + return createWindow(parent, type, token, name); + } + } + + WindowState createAppWindow(Task task, int type, String name) { + synchronized (mWm.mGlobalLock) { + final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task.addChild(token, 0); + return createWindow(null, type, token, name); + } + } + + WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { + synchronized (mWm.mGlobalLock) { + final WindowToken token = createWindowToken( + dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); + return createWindow(parent, type, token, name); + } + } + + WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, + boolean ownerCanAddInternalSystemWindow) { + synchronized (mWm.mGlobalLock) { + final WindowToken token = createWindowToken( + dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); + return createWindow(parent, type, token, name, 0 /* ownerId */, + ownerCanAddInternalSystemWindow); + } + } + + WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { + synchronized (mWm.mGlobalLock) { + return createWindow(parent, type, token, name, 0 /* ownerId */, + false /* ownerCanAddInternalSystemWindow */); + } + } + + WindowState createWindow(WindowState parent, int type, WindowToken token, String name, + int ownerId, boolean ownerCanAddInternalSystemWindow) { + return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow, + mWm, mMockSession, mIWindow); + } + + static WindowState createWindow(WindowState parent, int type, WindowToken token, + String name, int ownerId, boolean ownerCanAddInternalSystemWindow, + WindowManagerService service, Session session, IWindow iWindow) { + synchronized (service.mGlobalLock) { + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); + attrs.setTitle(name); + + final WindowState w = new WindowState(service, session, iWindow, token, parent, + OP_NONE, + 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow, + sPowerManagerWrapper); + // TODO: Probably better to make this call in the WindowState ctor to avoid errors with + // adding it to the token... + token.addWindow(w); + return w; + } + } + + /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */ + TaskStack createTaskStackOnDisplay(DisplayContent dc) { + synchronized (mWm.mGlobalLock) { + return createStackControllerOnDisplay(dc).mContainer; + } + } + + StackWindowController createStackControllerOnDisplay(DisplayContent dc) { + synchronized (mWm.mGlobalLock) { + return createStackControllerOnStackOnDisplay( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, dc); + } + } + + StackWindowController createStackControllerOnStackOnDisplay( + int windowingMode, int activityType, DisplayContent dc) { + synchronized (mWm.mGlobalLock) { + final Configuration overrideConfig = new Configuration(); + overrideConfig.windowConfiguration.setWindowingMode(windowingMode); + overrideConfig.windowConfiguration.setActivityType(activityType); + final int stackId = ++sNextStackId; + final StackWindowController controller = new StackWindowController(stackId, null, + dc.getDisplayId(), true /* onTop */, new Rect(), mWm); + controller.onOverrideConfigurationChanged(overrideConfig); + return controller; + } + } + + /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ + Task createTaskInStack(TaskStack stack, int userId) { + return WindowTestUtils.createTaskInStack(mWm, stack, userId); + } + + /** Creates a {@link DisplayContent} and adds it to the system. */ + DisplayContent createNewDisplay() { + return createNewDisplay(mDisplayInfo); + } + + /** Creates a {@link DisplayContent} and adds it to the system. */ + DisplayContent createNewDisplay(DisplayInfo displayInfo) { + final int displayId = sNextDisplayId++; + final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, + displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); + synchronized (mWm.mGlobalLock) { + return new DisplayContent(display, mWm, mock(DisplayWindowController.class)); + } + } + + /** + * Creates a {@link DisplayContent} with given display state and adds it to the system. + * + * Unlike {@link #createNewDisplay()} that uses a mock {@link DisplayWindowController} to + * initialize {@link DisplayContent}, this method used real controller object when the test + * need to verify its related flows. + * + * @param displayState For initializing the state of the display. See + * {@link Display#getState()}. + */ + DisplayContent createNewDisplayWithController(int displayState) { + // Leverage main display info & initialize it with display state for given displayId. + DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.copyFrom(mDisplayInfo); + displayInfo.state = displayState; + final int displayId = sNextDisplayId++; + final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, + displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); + final DisplayWindowController dcw = new DisplayWindowController(display, mWm); + synchronized (mWm.mGlobalLock) { + // Display creation is driven by DisplayWindowController via ActivityStackSupervisor. + // We skip those steps here. + return mWm.mRoot.createDisplayContent(display, dcw); + } + } + + /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */ + WindowTestUtils.TestWindowState createWindowState(WindowManager.LayoutParams attrs, + WindowToken token) { + synchronized (mWm.mGlobalLock) { + return new WindowTestUtils.TestWindowState(mWm, mMockSession, mIWindow, attrs, token); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java new file mode 100644 index 000000000000..3048f1a3487b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_TOAST; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Tests for the {@link WindowToken} class. + * + * Build/Install/Run: + * atest FrameworksServicesTests:WindowTokenTests + */ +@FlakyTest(bugId = 74078662) +@SmallTest +@Presubmit +public class WindowTokenTests extends WindowTestsBase { + + @Test + public void testAddWindow() { + final WindowTestUtils.TestWindowToken token = + WindowTestUtils.createTestWindowToken(0, mDisplayContent); + + assertEquals(0, token.getWindowsCount()); + + final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); + final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); + final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); + final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); + final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3"); + + token.addWindow(window1); + // NOTE: Child windows will not be added to the token as window containers can only + // contain/reference their direct children. + token.addWindow(window11); + token.addWindow(window12); + token.addWindow(window2); + token.addWindow(window3); + + // Should not contain the child windows that were added above. + assertEquals(3, token.getWindowsCount()); + assertTrue(token.hasWindow(window1)); + assertFalse(token.hasWindow(window11)); + assertFalse(token.hasWindow(window12)); + assertTrue(token.hasWindow(window2)); + assertTrue(token.hasWindow(window3)); + } + + @Test + public void testChildRemoval() { + final DisplayContent dc = mDisplayContent; + final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken(0, dc); + + assertEquals(token, dc.getWindowToken(token.token)); + + final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); + final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); + + window2.removeImmediately(); + // The token should still be mapped in the display content since it still has a child. + assertEquals(token, dc.getWindowToken(token.token)); + + window1.removeImmediately(); + // The token should have been removed from the display content since it no longer has a + // child. + assertEquals(null, dc.getWindowToken(token.token)); + } + + /** + * Test that a window token isn't orphaned by the system when it is requested to be removed. + * Tokens should only be removed from the system when all their windows are gone. + */ + @Test + public void testTokenRemovalProcess() { + final WindowTestUtils.TestWindowToken token = WindowTestUtils.createTestWindowToken( + TYPE_TOAST, mDisplayContent, true /* persistOnEmpty */); + + // Verify that the token is on the display + assertNotNull(mDisplayContent.getWindowToken(token.token)); + + final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1"); + final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2"); + + mDisplayContent.removeWindowToken(token.token); + // Verify that the token is no longer mapped on the display + assertNull(mDisplayContent.getWindowToken(token.token)); + // Verify that the token is still attached to its parent + assertNotNull(token.getParent()); + // Verify that the token windows are still around. + assertEquals(2, token.getWindowsCount()); + + window1.removeImmediately(); + // Verify that the token is still attached to its parent + assertNotNull(token.getParent()); + // Verify that the other token window is still around. + assertEquals(1, token.getWindowsCount()); + + window2.removeImmediately(); + // Verify that the token is no-longer attached to its parent + assertNull(token.getParent()); + // Verify that the token windows are no longer attached to it. + assertEquals(0, token.getWindowsCount()); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java new file mode 100644 index 000000000000..3dcea75b8ae5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2016 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 com.android.server.wm; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; + +import org.junit.After; +import org.junit.Test; + +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method. + * + * Build/Install/Run: + * atest FrameworksServicesTests:ZOrderingTests + */ +@FlakyTest(bugId = 74078662) +@SmallTest +@Presubmit +public class ZOrderingTests extends WindowTestsBase { + + private static class LayerRecordingTransaction extends SurfaceControl.Transaction { + // We have WM use our Hierarchy recording subclass of SurfaceControl.Builder + // such that we can keep track of the parents of Surfaces as they are constructed. + private final HashMap mParentFor = new HashMap<>(); + HashMap mLayersForControl = new HashMap<>(); + HashMap mRelativeLayersForControl = new HashMap<>(); + + @Override + public SurfaceControl.Transaction setLayer(SurfaceControl sc, int layer) { + mRelativeLayersForControl.remove(sc); + mLayersForControl.put(sc, layer); + return super.setLayer(sc, layer); + } + + @Override + public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, + SurfaceControl relativeTo, + int layer) { + mRelativeLayersForControl.put(sc, relativeTo); + mLayersForControl.put(sc, layer); + return super.setRelativeLayer(sc, relativeTo, layer); + } + + private int getLayer(SurfaceControl sc) { + return mLayersForControl.getOrDefault(sc, 0); + } + + private SurfaceControl getRelativeLayer(SurfaceControl sc) { + return mRelativeLayersForControl.get(sc); + } + + void addParentFor(SurfaceControl child, SurfaceControl parent) { + mParentFor.put(child, parent); + } + + SurfaceControl getParentFor(SurfaceControl child) { + return mParentFor.get(child); + } + + @Override + public void close() { + + } + } + + private static class HierarchyRecorder extends SurfaceControl.Builder { + private LayerRecordingTransaction mTransaction; + private SurfaceControl mPendingParent; + + HierarchyRecorder(SurfaceSession s, LayerRecordingTransaction transaction) { + super(s); + mTransaction = transaction; + } + + @Override + public SurfaceControl.Builder setParent(SurfaceControl sc) { + mPendingParent = sc; + return super.setParent(sc); + } + + @Override + public SurfaceControl build() { + final SurfaceControl sc = super.build(); + mTransaction.addParentFor(sc, mPendingParent); + mPendingParent = null; + return sc; + } + } + + private static class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory { + private LayerRecordingTransaction mTransaction; + + HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) { + mTransaction = transaction; + } + + @Override + public SurfaceControl.Builder make(SurfaceSession s) { + final LayerRecordingTransaction transaction = mTransaction; + return new HierarchyRecorder(s, transaction); + } + } + + private LayerRecordingTransaction mTransaction; + + @Override + void beforeCreateDisplay() { + // We can't use @Before here because it may happen after WindowTestsBase @Before + // which is after construction of the DisplayContent, meaning the HierarchyRecorder + // would miss construction of the top-level layers. + mTransaction = new LayerRecordingTransaction(); + mWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(mTransaction); + mWm.mTransactionFactory = () -> mTransaction; + } + + @After + public void tearDown() { + mTransaction.close(); + } + + private static LinkedList getAncestors(LayerRecordingTransaction t, + SurfaceControl sc) { + LinkedList p = new LinkedList<>(); + SurfaceControl current = sc; + do { + p.addLast(current); + + SurfaceControl rs = t.getRelativeLayer(current); + if (rs != null) { + current = rs; + } else { + current = t.getParentFor(current); + } + } while (current != null); + return p; + } + + + private static void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left, + SurfaceControl right) { + final LinkedList leftParentChain = getAncestors(t, left); + final LinkedList rightParentChain = getAncestors(t, right); + + SurfaceControl leftTop = leftParentChain.peekLast(); + SurfaceControl rightTop = rightParentChain.peekLast(); + while (leftTop != null && rightTop != null && leftTop == rightTop) { + leftParentChain.removeLast(); + rightParentChain.removeLast(); + leftTop = leftParentChain.peekLast(); + rightTop = rightParentChain.peekLast(); + } + + if (rightTop == null) { // right is the parent of left. + assertThat(t.getLayer(leftTop)).isGreaterThan(0); + } else if (leftTop == null) { // left is the parent of right. + assertThat(t.getLayer(rightTop)).isLessThan(0); + } else { + assertThat(t.getLayer(leftTop)).isGreaterThan(t.getLayer(rightTop)); + } + } + + void assertWindowHigher(WindowState left, WindowState right) { + assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl()); + } + + WindowState createWindow(String name) { + return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); + } + + @Test + public void testAssignWindowLayers_ForImeWithNoTarget() { + mDisplayContent.mInputMethodTarget = null; + mDisplayContent.assignChildLayers(mTransaction); + + // The Ime has an higher base layer than app windows and lower base layer than system + // windows, so it should be above app windows and below system windows if there isn't an IME + // target. + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeWithAppTarget() { + final WindowState imeAppTarget = createWindow("imeAppTarget"); + mDisplayContent.mInputMethodTarget = imeAppTarget; + + mDisplayContent.assignChildLayers(mTransaction); + + // Ime should be above all app windows and below system windows if it is targeting an app + // window. + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { + final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, + TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, + "imeAppTargetChildAboveWindow"); + final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget, + TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken, + "imeAppTargetChildBelowWindow"); + + mDisplayContent.mInputMethodTarget = imeAppTarget; + mDisplayContent.assignChildLayers(mTransaction); + + // Ime should be above all app windows except for child windows that are z-ordered above it + // and below system windows if it is targeting an app window. + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { + final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); + final WindowState imeAppTarget = createWindow("imeAppTarget"); + final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); + + mDisplayContent.mInputMethodTarget = imeAppTarget; + mDisplayContent.assignChildLayers(mTransaction); + + // Ime should be above all app windows except for non-fullscreen app window above it and + // below system windows if it is targeting an app window. + assertWindowHigher(mImeWindow, imeAppTarget); + assertWindowHigher(mImeWindow, appBelowImeTarget); + assertWindowHigher(appAboveImeTarget, mImeWindow); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testAssignWindowLayers_ForImeNonAppImeTarget() { + final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, + mDisplayContent, "imeSystemOverlayTarget", + true /* ownerCanAddInternalSystemWindow */); + + mDisplayContent.mInputMethodTarget = imeSystemOverlayTarget; + mDisplayContent.assignChildLayers(mTransaction); + + // The IME target base layer is higher than all window except for the nav bar window, so the + // IME should be above all windows except for the nav bar. + assertWindowHigher(mImeWindow, imeSystemOverlayTarget); + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); + + // The IME has a higher base layer than the status bar so we may expect it to go + // above the status bar once they are both in the Non-App layer, as past versions of this + // test enforced. However this seems like the wrong behavior unless the status bar is the + // IME target. + assertWindowHigher(mNavBarWindow, mImeWindow); + assertWindowHigher(mStatusBarWindow, mImeWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testAssignWindowLayers_ForStatusBarImeTarget() { + mDisplayContent.mInputMethodTarget = mStatusBarWindow; + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(mImeWindow, mChildAppWindowAbove); + assertWindowHigher(mImeWindow, mAppWindow); + assertWindowHigher(mImeWindow, mDockedDividerWindow); + assertWindowHigher(mImeWindow, mStatusBarWindow); + + // And, IME dialogs should always have an higher layer than the IME. + assertWindowHigher(mImeDialogWindow, mImeWindow); + } + + @Test + public void testStackLayers() { + final WindowState anyWindow1 = createWindow("anyWindow"); + final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, + "pinnedStackWindow"); + final WindowState dockedStackWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "dockedStackWindow"); + final WindowState assistantStackWindow = createWindowOnStack(null, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, + mDisplayContent, "assistantStackWindow"); + final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, + mDisplayContent, "homeActivityWindow"); + final WindowState anyWindow2 = createWindow("anyWindow2"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(dockedStackWindow, homeActivityWindow); + assertWindowHigher(assistantStackWindow, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, homeActivityWindow); + assertWindowHigher(anyWindow1, homeActivityWindow); + assertWindowHigher(anyWindow2, homeActivityWindow); + assertWindowHigher(pinnedStackWindow, anyWindow1); + assertWindowHigher(pinnedStackWindow, anyWindow2); + assertWindowHigher(pinnedStackWindow, dockedStackWindow); + assertWindowHigher(pinnedStackWindow, assistantStackWindow); + } + + @Test + public void testAssignWindowLayers_ForSysUiPanels() { + final WindowState navBarPanel = + createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); + final WindowState statusBarPanel = + createWindow(null, TYPE_STATUS_BAR_PANEL, mDisplayContent, "StatusBarPanel"); + final WindowState statusBarSubPanel = + createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel"); + mDisplayContent.assignChildLayers(mTransaction); + + // Ime should be above all app windows and below system windows if it is targeting an app + // window. + assertWindowHigher(navBarPanel, mNavBarWindow); + assertWindowHigher(statusBarPanel, mStatusBarWindow); + assertWindowHigher(statusBarSubPanel, statusBarPanel); + } + + @Test + public void testAssignWindowLayers_ForNegativelyZOrderedSubtype() { + // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA + // then we can drop all negative layering on the windowing side. + + final WindowState anyWindow = createWindow("anyWindow"); + final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, + "TypeApplicationMediaChild"); + final WindowState mediaOverlayChild = createWindow(anyWindow, + TYPE_APPLICATION_MEDIA_OVERLAY, + mDisplayContent, "TypeApplicationMediaOverlayChild"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(anyWindow, mediaOverlayChild); + assertWindowHigher(mediaOverlayChild, child); + } + + @Test + public void testDockedDividerPosition() { + final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, + "pinnedStackWindow"); + final WindowState splitScreenWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, + mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenSecondaryWindow = createWindowOnStack(null, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, + TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow"); + final WindowState assistantStackWindow = createWindowOnStack(null, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, + mDisplayContent, "assistantStackWindow"); + + mDisplayContent.assignChildLayers(mTransaction); + + assertWindowHigher(mDockedDividerWindow, splitScreenWindow); + assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow); + assertWindowHigher(pinnedStackWindow, mDockedDividerWindow); + } +} -- cgit v1.2.3-59-g8ed1b