1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
21 import static android.view.Surface.ROTATION_0;
22 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
23 
24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
32 
33 import android.annotation.NonNull;
34 import android.annotation.Nullable;
35 import android.content.res.Configuration;
36 import android.graphics.Insets;
37 import android.graphics.Rect;
38 import android.hardware.display.DisplayManagerGlobal;
39 import android.view.Display;
40 import android.view.DisplayCutout;
41 import android.view.DisplayInfo;
42 
43 import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
44 
45 class TestDisplayContent extends DisplayContent {
46 
47     public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
48 
49     /** Please use the {@link Builder} to create, visible for use in test builder overrides only. */
TestDisplayContent(RootWindowContainer rootWindowContainer, Display display, @NonNull DeviceStateController deviceStateController)50     TestDisplayContent(RootWindowContainer rootWindowContainer, Display display,
51             @NonNull DeviceStateController deviceStateController) {
52         super(display, rootWindowContainer, deviceStateController);
53         // Normally this comes from display-properties as exposed by WM. Without that, just
54         // hard-code to FULLSCREEN for tests.
55         setWindowingMode(WINDOWING_MODE_FULLSCREEN);
56         spyOn(this);
57         forAllTaskDisplayAreas(taskDisplayArea -> {
58             spyOn(taskDisplayArea);
59         });
60         final DisplayRotation displayRotation = getDisplayRotation();
61         spyOn(displayRotation);
62         doAnswer(invocation -> {
63             // Bypass all the rotation animation and display freezing stuff for testing and just
64             // set the rotation we want for the display
65             final int oldRotation = displayRotation.getRotation();
66             final int rotation = displayRotation.rotationForOrientation(
67                     displayRotation.getLastOrientation(), oldRotation);
68             if (oldRotation == rotation) {
69                 return false;
70             }
71             setLayoutNeeded();
72             displayRotation.setRotation(rotation);
73             return true;
74         }).when(displayRotation).updateRotationUnchecked(anyBoolean());
75 
76         final InputMonitor inputMonitor = getInputMonitor();
77         spyOn(inputMonitor);
78         doNothing().when(inputMonitor).resumeDispatchingLw(any());
79 
80         final InsetsPolicy insetsPolicy = getInsetsPolicy();
81         WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getPermanentControlTarget());
82         WindowTestsBase.suppressInsetsAnimation(insetsPolicy.getTransientControlTarget());
83 
84         // For devices that set the sysprop ro.bootanim.set_orientation_<display_id>
85         // See DisplayRotation#readDefaultDisplayRotation for context.
86         // Without that, meaning of height and width in context of the tests can be swapped if
87         // the default rotation is 90 or 270.
88         displayRotation.setRotation(ROTATION_0);
89     }
90 
91     public static class Builder {
92         private final DisplayInfo mInfo;
93         private boolean mCanRotate = true;
94         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
95         private int mPosition = POSITION_BOTTOM;
96         protected final ActivityTaskManagerService mService;
97         private boolean mSystemDecorations = false;
98         private int mStatusBarHeight = 0;
99         private SettingsEntry mOverrideSettings;
100         @NonNull
101         private DeviceStateController mDeviceStateController = mock(DeviceStateController.class);
102 
Builder(ActivityTaskManagerService service, int width, int height)103         Builder(ActivityTaskManagerService service, int width, int height) {
104             mService = service;
105             mInfo = new DisplayInfo();
106             mService.mContext.getDisplay().getDisplayInfo(mInfo);
107             mInfo.logicalWidth = width;
108             mInfo.logicalHeight = height;
109             mInfo.logicalDensityDpi = DEFAULT_LOGICAL_DISPLAY_DENSITY;
110             mInfo.displayCutout = null;
111             // Set unique ID so physical display overrides are not inheritted from
112             // DisplayWindowSettings.
113             mInfo.uniqueId = generateUniqueId();
114         }
Builder(ActivityTaskManagerService service, DisplayInfo info)115         Builder(ActivityTaskManagerService service, DisplayInfo info) {
116             mService = service;
117             mInfo = info;
118             // Set unique ID so physical display overrides are not inheritted from
119             // DisplayWindowSettings.
120             mInfo.uniqueId = generateUniqueId();
121         }
generateUniqueId()122         private String generateUniqueId() {
123             return "TEST_DISPLAY_CONTENT_" + System.currentTimeMillis();
124         }
setOverrideSettings(@ullable SettingsEntry overrideSettings)125         Builder setOverrideSettings(@Nullable SettingsEntry overrideSettings) {
126             mOverrideSettings = overrideSettings;
127             return this;
128         }
setSystemDecorations(boolean yes)129         Builder setSystemDecorations(boolean yes) {
130             mSystemDecorations = yes;
131             return this;
132         }
setPosition(int position)133         Builder setPosition(int position) {
134             mPosition = position;
135             return this;
136         }
setUniqueId(String uniqueId)137         Builder setUniqueId(String uniqueId) {
138             mInfo.uniqueId = uniqueId;
139             return this;
140         }
setType(int type)141         Builder setType(int type) {
142             mInfo.type = type;
143             return this;
144         }
setOwnerUid(int ownerUid)145         Builder setOwnerUid(int ownerUid) {
146             mInfo.ownerUid = ownerUid;
147             return this;
148         }
setCutout(int left, int top, int right, int bottom)149         Builder setCutout(int left, int top, int right, int bottom) {
150             final int cutoutFillerSize = 80;
151             Rect boundLeft = left != 0 ? new Rect(0, 0, left, cutoutFillerSize) : null;
152             Rect boundTop = top != 0 ? new Rect(0, 0, cutoutFillerSize, top) : null;
153             Rect boundRight = right != 0 ? new Rect(mInfo.logicalWidth - right, 0,
154                     mInfo.logicalWidth, cutoutFillerSize) : null;
155             Rect boundBottom = bottom != 0
156                     ? new Rect(0, mInfo.logicalHeight - bottom, cutoutFillerSize,
157                     mInfo.logicalHeight) : null;
158 
159             mInfo.displayCutout = new DisplayCutout(
160                     Insets.of(left, top, right, bottom),
161                     boundLeft, boundTop, boundRight, boundBottom);
162             return this;
163         }
setNotch(int height)164         Builder setNotch(int height) {
165             return setCutout(0, height, 0, 0);
166         }
setStatusBarHeight(int height)167         Builder setStatusBarHeight(int height) {
168             mStatusBarHeight = height;
169             return this;
170         }
setCanRotate(boolean canRotate)171         Builder setCanRotate(boolean canRotate) {
172             mCanRotate = canRotate;
173             return this;
174         }
setWindowingMode(int windowingMode)175         Builder setWindowingMode(int windowingMode) {
176             mWindowingMode = windowingMode;
177             return this;
178         }
setDensityDpi(int dpi)179         Builder setDensityDpi(int dpi) {
180             mInfo.logicalDensityDpi = dpi;
181             return this;
182         }
183 
setDeviceStateController(@onNull DeviceStateController deviceStateController)184         Builder setDeviceStateController(@NonNull DeviceStateController deviceStateController) {
185             mDeviceStateController = deviceStateController;
186             return this;
187         }
createInternal(Display display)188         TestDisplayContent createInternal(Display display) {
189             return new TestDisplayContent(mService.mRootWindowContainer, display,
190                     mDeviceStateController);
191         }
build()192         TestDisplayContent build() {
193             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
194 
195             if (mOverrideSettings != null) {
196                 mService.mWindowManager.mDisplayWindowSettingsProvider
197                         .updateOverrideSettings(mInfo, mOverrideSettings);
198             }
199 
200             final int displayId = SystemServicesTestRule.sNextDisplayId++;
201             mInfo.displayId = displayId;
202             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
203                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
204             final TestDisplayContent newDisplay = createInternal(display);
205             // disable the normal system decorations
206             final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
207             spyOn(displayPolicy);
208             if (mSystemDecorations) {
209                 doReturn(true).when(newDisplay).supportsSystemDecorations();
210                 doReturn(true).when(displayPolicy).hasNavigationBar();
211                 doReturn(NAV_BAR_BOTTOM).when(displayPolicy).navigationBarPosition(anyInt());
212             } else {
213                 doReturn(false).when(displayPolicy).hasNavigationBar();
214                 doReturn(false).when(displayPolicy).hasStatusBar();
215                 doReturn(false).when(newDisplay).supportsSystemDecorations();
216             }
217             // Update the display policy to make the screen fully turned on so animation is allowed
218             displayPolicy.screenTurningOn(null /* screenOnListener */);
219             displayPolicy.finishKeyguardDrawn();
220             displayPolicy.finishWindowsDrawn();
221             displayPolicy.finishScreenTurningOn();
222             if (mStatusBarHeight > 0) {
223                 doReturn(true).when(displayPolicy).hasStatusBar();
224             }
225             Configuration c = new Configuration();
226             newDisplay.computeScreenConfiguration(c);
227             c.windowConfiguration.setWindowingMode(mWindowingMode);
228             newDisplay.onRequestedOverrideConfigurationChanged(c);
229             if (!mCanRotate) {
230                 final DisplayRotation displayRotation = newDisplay.getDisplayRotation();
231                 doReturn(true).when(displayRotation).isFixedToUserRotation();
232             }
233             // Please add stubbing before this line. Services will start using this display in other
234             // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
235             // reduces chance of races, but still doesn't eliminate race conditions.
236             mService.mRootWindowContainer.addChild(newDisplay, mPosition);
237 
238             // Set the default focused TDA.
239             newDisplay.onLastFocusedTaskDisplayAreaChanged(newDisplay.getDefaultTaskDisplayArea());
240 
241             return newDisplay;
242         }
243     }
244 }
245