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 
22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
29 
30 import static org.mockito.ArgumentMatchers.any;
31 
32 import android.content.res.Configuration;
33 import android.graphics.Insets;
34 import android.graphics.Rect;
35 import android.hardware.display.DisplayManagerGlobal;
36 import android.view.Display;
37 import android.view.DisplayCutout;
38 import android.view.DisplayInfo;
39 
40 class TestDisplayContent extends DisplayContent {
41 
42     public static final int DEFAULT_LOGICAL_DISPLAY_DENSITY = 300;
43 
44     /** Please use the {@link Builder} to create, visible for use in test builder overrides only. */
TestDisplayContent(RootWindowContainer rootWindowContainer, Display display)45     TestDisplayContent(RootWindowContainer rootWindowContainer, Display display) {
46         super(display, rootWindowContainer);
47         // Normally this comes from display-properties as exposed by WM. Without that, just
48         // hard-code to FULLSCREEN for tests.
49         setWindowingMode(WINDOWING_MODE_FULLSCREEN);
50         spyOn(this);
51         forAllTaskDisplayAreas(taskDisplayArea -> {
52             spyOn(taskDisplayArea);
53         });
54         final DisplayRotation displayRotation = getDisplayRotation();
55         spyOn(displayRotation);
56         doAnswer(invocation -> {
57             // Bypass all the rotation animation and display freezing stuff for testing and just
58             // set the rotation we want for the display
59             final int oldRotation = displayRotation.getRotation();
60             final int rotation = displayRotation.rotationForOrientation(
61                     displayRotation.getLastOrientation(), oldRotation);
62             if (oldRotation == rotation) {
63                 return false;
64             }
65             setLayoutNeeded();
66             displayRotation.setRotation(rotation);
67             return true;
68         }).when(displayRotation).updateRotationUnchecked(anyBoolean());
69 
70         final InputMonitor inputMonitor = getInputMonitor();
71         spyOn(inputMonitor);
72         doNothing().when(inputMonitor).resumeDispatchingLw(any());
73     }
74 
75     public static class Builder {
76         private final DisplayInfo mInfo;
77         private boolean mCanRotate = true;
78         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
79         private int mPosition = POSITION_BOTTOM;
80         protected final ActivityTaskManagerService mService;
81         private boolean mSystemDecorations = false;
82         private int mStatusBarHeight = 0;
83 
Builder(ActivityTaskManagerService service, int width, int height)84         Builder(ActivityTaskManagerService service, int width, int height) {
85             mService = service;
86             mInfo = new DisplayInfo();
87             mService.mContext.getDisplay().getDisplayInfo(mInfo);
88             mInfo.logicalWidth = width;
89             mInfo.logicalHeight = height;
90             mInfo.logicalDensityDpi = DEFAULT_LOGICAL_DISPLAY_DENSITY;
91             mInfo.displayCutout = null;
92             // Set unique ID so physical display overrides are not inheritted from
93             // DisplayWindowSettings.
94             mInfo.uniqueId = generateUniqueId();
95         }
Builder(ActivityTaskManagerService service, DisplayInfo info)96         Builder(ActivityTaskManagerService service, DisplayInfo info) {
97             mService = service;
98             mInfo = info;
99             // Set unique ID so physical display overrides are not inheritted from
100             // DisplayWindowSettings.
101             mInfo.uniqueId = generateUniqueId();
102         }
generateUniqueId()103         private String generateUniqueId() {
104             return "TEST_DISPLAY_CONTENT_" + System.currentTimeMillis();
105         }
setSystemDecorations(boolean yes)106         Builder setSystemDecorations(boolean yes) {
107             mSystemDecorations = yes;
108             return this;
109         }
setPosition(int position)110         Builder setPosition(int position) {
111             mPosition = position;
112             return this;
113         }
setUniqueId(String uniqueId)114         Builder setUniqueId(String uniqueId) {
115             mInfo.uniqueId = uniqueId;
116             return this;
117         }
setType(int type)118         Builder setType(int type) {
119             mInfo.type = type;
120             return this;
121         }
setOwnerUid(int ownerUid)122         Builder setOwnerUid(int ownerUid) {
123             mInfo.ownerUid = ownerUid;
124             return this;
125         }
setNotch(int height)126         Builder setNotch(int height) {
127             mInfo.displayCutout = new DisplayCutout(
128                     Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
129             return this;
130         }
setStatusBarHeight(int height)131         Builder setStatusBarHeight(int height) {
132             mStatusBarHeight = height;
133             return this;
134         }
setCanRotate(boolean canRotate)135         Builder setCanRotate(boolean canRotate) {
136             mCanRotate = canRotate;
137             return this;
138         }
setWindowingMode(int windowingMode)139         Builder setWindowingMode(int windowingMode) {
140             mWindowingMode = windowingMode;
141             return this;
142         }
setDensityDpi(int dpi)143         Builder setDensityDpi(int dpi) {
144             mInfo.logicalDensityDpi = dpi;
145             return this;
146         }
createInternal(Display display)147         TestDisplayContent createInternal(Display display) {
148             return new TestDisplayContent(mService.mRootWindowContainer, display);
149         }
build()150         TestDisplayContent build() {
151             SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
152 
153             final int displayId = SystemServicesTestRule.sNextDisplayId++;
154             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
155                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
156             final TestDisplayContent newDisplay = createInternal(display);
157             // disable the normal system decorations
158             final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
159             spyOn(displayPolicy);
160             if (mSystemDecorations) {
161                 doReturn(true).when(newDisplay).supportsSystemDecorations();
162                 doReturn(true).when(displayPolicy).hasNavigationBar();
163             } else {
164                 doReturn(false).when(displayPolicy).hasNavigationBar();
165                 doReturn(false).when(displayPolicy).hasStatusBar();
166                 doReturn(false).when(newDisplay).supportsSystemDecorations();
167             }
168             // Update the display policy to make the screen fully turned on so animation is allowed
169             displayPolicy.screenTurnedOn(null /* screenOnListener */);
170             displayPolicy.finishKeyguardDrawn();
171             displayPolicy.finishWindowsDrawn();
172             displayPolicy.finishScreenTurningOn();
173             if (mStatusBarHeight > 0) {
174                 doReturn(true).when(displayPolicy).hasStatusBar();
175                 doAnswer(invocation -> {
176                     Rect inOutInsets = (Rect) invocation.getArgument(0);
177                     inOutInsets.top = mStatusBarHeight;
178                     return null;
179                 }).when(displayPolicy).convertNonDecorInsetsToStableInsets(any(), anyInt());
180             }
181             Configuration c = new Configuration();
182             newDisplay.computeScreenConfiguration(c);
183             c.windowConfiguration.setWindowingMode(mWindowingMode);
184             newDisplay.onRequestedOverrideConfigurationChanged(c);
185             if (!mCanRotate) {
186                 final DisplayRotation displayRotation = newDisplay.getDisplayRotation();
187                 doReturn(true).when(displayRotation).isFixedToUserRotation();
188             }
189             // Please add stubbing before this line. Services will start using this display in other
190             // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
191             // reduces chance of races, but still doesn't eliminate race conditions.
192             mService.mRootWindowContainer.addChild(newDisplay, mPosition);
193 
194             // Set the default focused TDA.
195             newDisplay.onLastFocusedTaskDisplayAreaChanged(newDisplay.getDefaultTaskDisplayArea());
196 
197             return newDisplay;
198         }
199     }
200 }
201