| /* |
| * Copyright (C) 2020 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.view.WindowManager.LayoutParams.TYPE_APPLICATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; |
| import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; |
| import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; |
| import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; |
| |
| 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.never; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; |
| import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; |
| import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; |
| import static com.android.server.wm.DisplayArea.Type.ANY; |
| import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS; |
| import static com.android.server.wm.DisplayArea.Type.checkChild; |
| import static com.android.server.wm.DisplayArea.Type.checkSiblings; |
| import static com.android.server.wm.DisplayArea.Type.typeOf; |
| import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; |
| import static com.android.server.wm.WindowContainer.POSITION_TOP; |
| import static com.android.server.wm.testing.Assert.assertThrows; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.argThat; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyZeroInteractions; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.pm.ActivityInfo; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.platform.test.annotations.Presubmit; |
| import android.view.SurfaceControl; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.window.DisplayAreaInfo; |
| import android.window.IDisplayAreaOrganizer; |
| |
| import com.google.android.collect.Lists; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.function.BiFunction; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| |
| /** |
| * Tests for the {@link DisplayArea} container. |
| * |
| * Build/Install/Run: |
| * atest WmTests:DisplayAreaTest |
| */ |
| @Presubmit |
| @RunWith(WindowTestRunner.class) |
| public class DisplayAreaTest extends WindowTestsBase { |
| |
| @Test |
| public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() { |
| DisplayArea<WindowContainer> parent = new DisplayArea<>(mWm, BELOW_TASKS, "Parent"); |
| DisplayArea<WindowContainer> child = new DisplayArea<>(mWm, ANY, "Child"); |
| |
| assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0)); |
| } |
| |
| @Test |
| public void testType_typeOf() { |
| assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(mWm, ABOVE_TASKS, "test"))); |
| assertEquals(ANY, typeOf(new DisplayArea<>(mWm, ANY, "test"))); |
| assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(mWm, BELOW_TASKS, "test"))); |
| |
| assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY))); |
| assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION))); |
| assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER))); |
| |
| assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class))); |
| assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class))); |
| } |
| |
| @Test |
| public void testType_checkSiblings() { |
| checkSiblings(BELOW_TASKS, BELOW_TASKS); |
| checkSiblings(BELOW_TASKS, ANY); |
| checkSiblings(BELOW_TASKS, ABOVE_TASKS); |
| checkSiblings(ANY, ABOVE_TASKS); |
| checkSiblings(ABOVE_TASKS, ABOVE_TASKS); |
| checkSiblings(ANY, ANY); |
| |
| assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS)); |
| assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY)); |
| assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS)); |
| } |
| |
| @Test |
| public void testType_checkChild() { |
| checkChild(ANY, ANY); |
| checkChild(ANY, ABOVE_TASKS); |
| checkChild(ANY, BELOW_TASKS); |
| checkChild(ABOVE_TASKS, ABOVE_TASKS); |
| checkChild(BELOW_TASKS, BELOW_TASKS); |
| |
| assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS)); |
| assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY)); |
| assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS)); |
| assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY)); |
| } |
| |
| @Test |
| public void testAsDisplayArea() { |
| final WindowContainer windowContainer = new WindowContainer(mWm); |
| final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(mWm, ANY, "DA"); |
| final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA", FEATURE_DEFAULT_TASK_CONTAINER); |
| |
| assertThat(windowContainer.asDisplayArea()).isNull(); |
| assertThat(displayArea.asDisplayArea()).isEqualTo(displayArea); |
| assertThat(taskDisplayArea.asDisplayArea()).isEqualTo(taskDisplayArea); |
| } |
| |
| @Test |
| public void testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final Function<TaskDisplayArea, Boolean> callback0 = tda -> false; |
| final Consumer<TaskDisplayArea> callback1 = tda -> { }; |
| final BiFunction<TaskDisplayArea, Integer, Integer> callback2 = (tda, result) -> result; |
| final Function<TaskDisplayArea, TaskDisplayArea> callback3 = tda -> null; |
| |
| // Don't traverse the child if the current DA has type BELOW_TASKS |
| final DisplayArea<WindowContainer> da1 = new DisplayArea<>(mWm, BELOW_TASKS, "DA1"); |
| final DisplayArea<WindowContainer> da2 = new DisplayArea<>(mWm, BELOW_TASKS, "DA2"); |
| root.addChild(da1, POSITION_BOTTOM); |
| da1.addChild(da2, POSITION_TOP); |
| spyOn(da2); |
| |
| da1.forAllTaskDisplayAreas(callback0); |
| da1.forAllTaskDisplayAreas(callback1); |
| da1.reduceOnAllTaskDisplayAreas(callback2, 0); |
| da1.getItemFromTaskDisplayAreas(callback3); |
| |
| verifyZeroInteractions(da2); |
| |
| // Traverse the child if the current DA has type ANY |
| final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3"); |
| final DisplayArea<WindowContainer> da4 = new DisplayArea<>(mWm, ANY, "DA4"); |
| root.addChild(da3, POSITION_TOP); |
| da3.addChild(da4, POSITION_TOP); |
| spyOn(da4); |
| |
| da3.forAllTaskDisplayAreas(callback0); |
| da3.forAllTaskDisplayAreas(callback1); |
| da3.reduceOnAllTaskDisplayAreas(callback2, 0); |
| da3.getItemFromTaskDisplayAreas(callback3); |
| |
| verify(da4).forAllTaskDisplayAreas(callback0, true /* traverseTopToBottom */); |
| verify(da4).forAllTaskDisplayAreas(callback1, true /* traverseTopToBottom */); |
| verify(da4).reduceOnAllTaskDisplayAreas(callback2, 0 /* initValue */, |
| true /* traverseTopToBottom */); |
| verify(da4).getItemFromTaskDisplayAreas( |
| callback3, true /* traverseTopToBottom */); |
| |
| // Don't traverse the child if the current DA has type ABOVE_TASKS |
| final DisplayArea<WindowContainer> da5 = new DisplayArea<>(mWm, ABOVE_TASKS, "DA5"); |
| final DisplayArea<WindowContainer> da6 = new DisplayArea<>(mWm, ABOVE_TASKS, "DA6"); |
| root.addChild(da5, POSITION_TOP); |
| da5.addChild(da6, POSITION_TOP); |
| spyOn(da6); |
| |
| da5.forAllTaskDisplayAreas(callback0); |
| da5.forAllTaskDisplayAreas(callback1); |
| da5.reduceOnAllTaskDisplayAreas(callback2, 0); |
| da5.getItemFromTaskDisplayAreas(callback3); |
| |
| verifyZeroInteractions(da6); |
| } |
| |
| @Test |
| public void testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final DisplayArea<DisplayArea> da1 = |
| new DisplayArea<>(mWm, ANY, "DA1"); |
| final DisplayArea<DisplayArea> da2 = |
| new DisplayArea<>(mWm, ANY, "DA2"); |
| final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); |
| final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA2", FEATURE_VENDOR_FIRST); |
| final TaskDisplayArea tda3 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA3", FEATURE_VENDOR_FIRST + 1); |
| root.addChild(da1, POSITION_TOP); |
| root.addChild(da2, POSITION_TOP); |
| da1.addChild(tda1, POSITION_TOP); |
| da2.addChild(tda2, POSITION_TOP); |
| da2.addChild(tda3, POSITION_TOP); |
| |
| /* The hierarchy looks like this |
| Root |
| - DA1 |
| - TDA1 ------ bottom |
| - DA2 |
| - TDA2 |
| - TDA3 ------ top |
| */ |
| |
| // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>) |
| List<TaskDisplayArea> actualOrder = new ArrayList<>(); |
| root.forAllTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| }); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); |
| |
| // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>, boolean) |
| actualOrder.clear(); |
| root.forAllTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| }, false /* traverseTopToBottom */); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); |
| |
| // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>) |
| actualOrder.clear(); |
| root.forAllTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| return false; |
| }); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); |
| |
| // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>, boolean) |
| actualOrder.clear(); |
| root.forAllTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| return false; |
| }, false /* traverseTopToBottom */); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); |
| |
| // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R) |
| actualOrder.clear(); |
| root.reduceOnAllTaskDisplayAreas((tda, result) -> { |
| actualOrder.add(tda); |
| return result; |
| }, 0 /* initValue */); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); |
| |
| // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R, boolean) |
| actualOrder.clear(); |
| root.reduceOnAllTaskDisplayAreas((tda, result) -> { |
| actualOrder.add(tda); |
| return result; |
| }, 0 /* initValue */, false /* traverseTopToBottom */); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); |
| |
| // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback) |
| actualOrder.clear(); |
| root.getItemFromTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| return null; |
| }); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1)); |
| |
| // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean) |
| actualOrder.clear(); |
| root.getItemFromTaskDisplayAreas(tda -> { |
| actualOrder.add(tda); |
| return null; |
| }, false /* traverseTopToBottom */); |
| |
| assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3)); |
| } |
| |
| @Test |
| public void testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); |
| final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA2", FEATURE_VENDOR_FIRST); |
| root.addChild(tda1, POSITION_TOP); |
| root.addChild(tda2, POSITION_TOP); |
| |
| /* The hierarchy looks like this |
| Root |
| - TDA1 ------ bottom |
| - TDA2 ------ top |
| */ |
| |
| root.forAllTaskDisplayAreas(tda -> { |
| assertThat(tda).isEqualTo(tda2); |
| return true; |
| }); |
| |
| root.forAllTaskDisplayAreas(tda -> { |
| assertThat(tda).isEqualTo(tda1); |
| return true; |
| }, false /* traverseTopToBottom */); |
| } |
| |
| @Test |
| public void testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); |
| final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA2", FEATURE_VENDOR_FIRST); |
| root.addChild(tda1, POSITION_TOP); |
| root.addChild(tda2, POSITION_TOP); |
| |
| /* The hierarchy looks like this |
| Root |
| - TDA1 ------ bottom |
| - TDA2 ------ top |
| */ |
| |
| String accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) -> |
| result + tda.getName(), "" /* initValue */); |
| assertThat(accumulativeName).isEqualTo("TDA2TDA1"); |
| |
| accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) -> |
| result + tda.getName(), "" /* initValue */, false /* traverseTopToBottom */); |
| assertThat(accumulativeName).isEqualTo("TDA1TDA2"); |
| } |
| |
| @Test |
| public void testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER); |
| final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */, |
| mWm, "TDA2", FEATURE_VENDOR_FIRST); |
| root.addChild(tda1, POSITION_TOP); |
| root.addChild(tda2, POSITION_TOP); |
| |
| /* The hierarchy looks like this |
| Root |
| - TDA1 ------ bottom |
| - TDA2 ------ top |
| */ |
| |
| TaskDisplayArea result = root.getItemFromTaskDisplayAreas(tda -> { |
| assertThat(tda).isEqualTo(tda2); |
| return tda; |
| }); |
| |
| assertThat(result).isEqualTo(tda2); |
| |
| result = root.getItemFromTaskDisplayAreas(tda -> { |
| assertThat(tda).isEqualTo(tda1); |
| return tda; |
| }, false /* traverseTopToBottom */); |
| |
| assertThat(result).isEqualTo(tda1); |
| } |
| |
| @Test |
| public void testSetMaxBounds() { |
| final Rect parentBounds = new Rect(0, 0, 100, 100); |
| final Rect childBounds1 = new Rect(parentBounds.left, parentBounds.top, |
| parentBounds.right / 2, parentBounds.bottom); |
| final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top, |
| parentBounds.right, parentBounds.bottom); |
| TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds); |
| TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1); |
| TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2); |
| parentDa.addChild(childDa1, 0); |
| parentDa.addChild(childDa2, 1); |
| |
| assertEquals(parentBounds, parentDa.getMaxBounds()); |
| assertEquals(childBounds1, childDa1.getMaxBounds()); |
| assertEquals(childBounds2, childDa2.getMaxBounds()); |
| |
| final WindowToken windowToken = createWindowToken(TYPE_APPLICATION); |
| childDa1.addChild(windowToken, 0); |
| |
| assertEquals("DisplayArea's children must have the same max bounds as itself", |
| childBounds1, windowToken.getMaxBounds()); |
| } |
| |
| @Test |
| public void testRestrictAppBoundsToOverrideBounds() { |
| final RootDisplayArea root = |
| new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm); |
| final DisplayArea<DisplayArea> da = new DisplayArea<>(mWm, ANY, "Test_DA"); |
| root.addChild(da, POSITION_TOP); |
| final Rect displayBounds = new Rect(0, 0, 1800, 2800); |
| final Rect displayAppBounds = new Rect(0, 100, 1800, 2800); |
| final Rect daBounds = new Rect(0, 1400, 1800, 2800); |
| root.setBounds(displayBounds); |
| |
| // DA inherit parent app bounds. |
| final Configuration displayConfig = new Configuration(); |
| displayConfig.windowConfiguration.setAppBounds(displayAppBounds); |
| root.onRequestedOverrideConfigurationChanged(displayConfig); |
| |
| assertEquals(displayAppBounds, da.getConfiguration().windowConfiguration.getAppBounds()); |
| |
| // Restrict DA appBounds to override Bounds |
| da.setBounds(daBounds); |
| |
| final Rect expectedDaAppBounds = new Rect(daBounds); |
| expectedDaAppBounds.intersect(displayAppBounds); |
| assertEquals(expectedDaAppBounds, da.getConfiguration().windowConfiguration.getAppBounds()); |
| } |
| |
| @Test |
| public void testGetOrientation() { |
| final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test"); |
| final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY); |
| spyOn(token); |
| doReturn(mock(DisplayContent.class)).when(token).getDisplayContent(); |
| doNothing().when(token).setParent(any()); |
| final WindowState win = createWindowState(token); |
| spyOn(win); |
| doNothing().when(win).setParent(any()); |
| win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; |
| token.addChild(win, 0); |
| area.addChild(token); |
| |
| doReturn(true).when(win).isVisible(); |
| |
| assertEquals("Visible window can request orientation", |
| ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, |
| area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)); |
| |
| doReturn(false).when(win).isVisible(); |
| |
| assertEquals("Invisible window cannot request orientation", |
| ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, |
| area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)); |
| } |
| |
| @Test |
| public void testSetIgnoreOrientationRequest() { |
| final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test"); |
| final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY); |
| spyOn(token); |
| doReturn(mock(DisplayContent.class)).when(token).getDisplayContent(); |
| doNothing().when(token).setParent(any()); |
| final WindowState win = createWindowState(token); |
| spyOn(win); |
| doNothing().when(win).setParent(any()); |
| win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; |
| token.addChild(win, 0); |
| area.addChild(token); |
| doReturn(true).when(win).isVisible(); |
| |
| assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation()); |
| |
| area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); |
| |
| assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation()); |
| } |
| |
| @Test |
| public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() { |
| final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); |
| final Task stack = |
| new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); |
| final ActivityRecord activity = stack.getTopNonFinishingActivity(); |
| |
| tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); |
| |
| activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); |
| |
| verify(tda).onDescendantOrientationChanged(any()); |
| verify(mDisplayContent, never()).onDescendantOrientationChanged(any()); |
| |
| tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); |
| activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); |
| |
| verify(tda, times(2)).onDescendantOrientationChanged(any()); |
| verify(mDisplayContent).onDescendantOrientationChanged(any()); |
| } |
| |
| @Test |
| public void testSetIgnoreOrientationRequest_updateOrientationRequestingTaskDisplayArea() { |
| final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); |
| final Task stack = |
| new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); |
| final ActivityRecord activity = stack.getTopNonFinishingActivity(); |
| |
| mDisplayContent.setFocusedApp(activity); |
| assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); |
| |
| // TDA is no longer handling orientation request, clear the last focused TDA. |
| tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); |
| |
| assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isNull(); |
| |
| // TDA now handles orientation request, update last focused TDA based on the focused app. |
| tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); |
| |
| assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda); |
| } |
| |
| @Test |
| public void testDisplayContentUpdateDisplayAreaOrganizers_onDisplayAreaAppeared() { |
| final DisplayArea<WindowContainer> displayArea = new DisplayArea<>( |
| mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST); |
| final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class); |
| spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController); |
| when(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController |
| .getOrganizerByFeature(FEATURE_VENDOR_FIRST)) |
| .thenReturn(mockDisplayAreaOrganizer); |
| |
| mDisplayContent.addChild(displayArea, 0); |
| mDisplayContent.updateDisplayAreaOrganizers(); |
| |
| assertEquals(mockDisplayAreaOrganizer, displayArea.mOrganizer); |
| verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController) |
| .onDisplayAreaAppeared( |
| eq(mockDisplayAreaOrganizer), |
| argThat(it -> it == displayArea && it.getSurfaceControl() != null)); |
| } |
| |
| @Test |
| public void testRemoveImmediately_onDisplayAreaVanished() { |
| final DisplayArea<WindowContainer> displayArea = new DisplayArea<>( |
| mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST); |
| final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class); |
| doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder(); |
| displayArea.mOrganizer = mockDisplayAreaOrganizer; |
| spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController); |
| mDisplayContent.addChild(displayArea, 0); |
| |
| displayArea.removeImmediately(); |
| |
| assertNull(displayArea.mOrganizer); |
| verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController) |
| .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea); |
| } |
| |
| @Test |
| public void testGetDisplayAreaInfo() { |
| final DisplayArea<WindowContainer> displayArea = new DisplayArea<>( |
| mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST); |
| mDisplayContent.addChild(displayArea, 0); |
| final DisplayAreaInfo info = displayArea.getDisplayAreaInfo(); |
| |
| assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken()); |
| assertThat(info.configuration).isEqualTo(displayArea.getConfiguration()); |
| assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId()); |
| assertThat(info.featureId).isEqualTo(displayArea.mFeatureId); |
| assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId); |
| |
| final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); |
| final int tdaIndex = tda.getParent().mChildren.indexOf(tda); |
| final RootDisplayArea root = |
| new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1); |
| mDisplayContent.addChild(root, tdaIndex + 1); |
| displayArea.reparent(root, 0); |
| |
| final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo(); |
| |
| assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId); |
| } |
| |
| @Test |
| public void testRegisterSameFeatureOrganizer_expectThrowsException() { |
| final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class); |
| final IBinder binder = mock(IBinder.class); |
| doReturn(true).when(binder).isBinderAlive(); |
| doReturn(binder).when(mockDisplayAreaOrganizer).asBinder(); |
| final DisplayAreaOrganizerController controller = |
| mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController; |
| controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST); |
| assertThrows(IllegalStateException.class, |
| () -> controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST)); |
| } |
| |
| @Test |
| public void testRegisterUnregisterOrganizer() { |
| final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class); |
| doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder(); |
| final DisplayAreaOrganizerController controller = |
| mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController; |
| controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST); |
| controller.unregisterOrganizer(mockDisplayAreaOrganizer); |
| controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST); |
| } |
| |
| private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { |
| private TestDisplayArea(WindowManagerService wms, Rect bounds) { |
| super(wms, ANY, "half display area"); |
| setBounds(bounds); |
| } |
| |
| @Override |
| SurfaceControl.Builder makeChildSurface(WindowContainer child) { |
| return new MockSurfaceControlBuilder(); |
| } |
| } |
| |
| private WindowState createWindowState(WindowToken token) { |
| return new WindowState(mWm, mock(Session.class), new TestIWindow(), token, |
| null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(), |
| View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */, |
| false /* ownerCanAddInternalSystemWindow */); |
| } |
| |
| private WindowToken createWindowToken(int type) { |
| return new WindowToken.Builder(mWm, new Binder(), type).build(); |
| } |
| } |