1 /*
2  * Copyright (C) 2018 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.view.Surface.ROTATION_0;
20 import static android.view.Surface.ROTATION_90;
21 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
23 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
24 
25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
27 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertTrue;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.doNothing;
32 import static org.mockito.Mockito.spy;
33 
34 import android.graphics.Rect;
35 import android.os.Binder;
36 import android.platform.test.annotations.Presubmit;
37 import android.util.SparseArray;
38 import android.view.DisplayInfo;
39 import android.view.InsetsFrameProvider;
40 import android.view.InsetsSource;
41 import android.view.InsetsState;
42 import android.view.PrivacyIndicatorBounds;
43 import android.view.RoundedCorners;
44 import android.view.WindowInsets;
45 
46 import androidx.test.filters.SmallTest;
47 
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.io.PrintWriter;
53 import java.io.StringWriter;
54 
55 /**
56  * Tests for the {@link DisplayPolicy} class.
57  *
58  * Build/Install/Run:
59  *  atest WmTests:DisplayPolicyLayoutTests
60  */
61 @SmallTest
62 @Presubmit
63 @WindowTestsBase.SetupWindows(
64         addWindows = { WindowTestsBase.W_STATUS_BAR, WindowTestsBase.W_NAVIGATION_BAR })
65 @RunWith(WindowTestRunner.class)
66 public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
67 
68     private DisplayFrames mFrames;
69     private WindowState mWindow;
70     private int mRotation = ROTATION_0;
71     private boolean mHasDisplayCutout;
72     private boolean mIsLongEdgeDisplayCutout;
73     private boolean mHasRoundedCorners;
74 
75     private final Rect mDisplayBounds = new Rect();
76 
77     @Before
setUp()78     public void setUp() throws Exception {
79         mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
80 
81         spyOn(mStatusBarWindow);
82         spyOn(mNavBarWindow);
83 
84         // Disabling this call for most tests since it can override the systemUiFlags when called.
85         doNothing().when(mDisplayPolicy).updateSystemBarAttributes();
86 
87         updateDisplayFrames();
88     }
89 
setRotation(int rotation, boolean includingWindows)90     public void setRotation(int rotation, boolean includingWindows) {
91         mRotation = rotation;
92         updateDisplayFrames();
93         if (includingWindows) {
94             mNavBarWindow.getWindowConfiguration().setRotation(rotation);
95             mStatusBarWindow.getWindowConfiguration().setRotation(rotation);
96             mWindow.getWindowConfiguration().setRotation(rotation);
97         }
98     }
99 
addDisplayCutout()100     public void addDisplayCutout() {
101         mHasDisplayCutout = true;
102         updateDisplayFrames();
103     }
104 
addLongEdgeDisplayCutout()105     public void addLongEdgeDisplayCutout() {
106         mHasDisplayCutout = true;
107         mIsLongEdgeDisplayCutout = true;
108         updateDisplayFrames();
109     }
110 
addRoundedCorners()111     public void addRoundedCorners() {
112         mHasRoundedCorners = true;
113         updateDisplayFrames();
114     }
115 
updateDisplayFrames()116     private void updateDisplayFrames() {
117         mFrames = createDisplayFrames(
118                 mDisplayContent.getInsetsStateController().getRawInsetsState());
119         mDisplayBounds.set(0, 0, mFrames.mWidth, mFrames.mHeight);
120         mDisplayContent.mDisplayFrames = mFrames;
121         mDisplayContent.setBounds(mDisplayBounds);
122         mDisplayPolicy.layoutWindowLw(mNavBarWindow, null, mFrames);
123         mDisplayPolicy.layoutWindowLw(mStatusBarWindow, null, mFrames);
124     }
125 
createDisplayFrames(InsetsState insetsState)126     private DisplayFrames createDisplayFrames(InsetsState insetsState) {
127         final DisplayInfo info = displayInfoAndCutoutForRotation(mRotation,
128                 mHasDisplayCutout, mIsLongEdgeDisplayCutout);
129         final RoundedCorners roundedCorners = mHasRoundedCorners
130                 ? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
131                 : RoundedCorners.NO_ROUNDED_CORNERS;
132         return new DisplayFrames(insetsState, info, info.displayCutout, roundedCorners,
133                 new PrivacyIndicatorBounds(), info.displayShape);
134     }
135 
136     @Test
addingWindow_doesNotTamperWithSysuiFlags()137     public void addingWindow_doesNotTamperWithSysuiFlags() {
138         mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
139         addWindow(mWindow);
140 
141         assertEquals(0, mWindow.mAttrs.systemUiVisibility);
142         assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
143     }
144 
145     @Test
addingWindow_withInsetsTypes()146     public void addingWindow_withInsetsTypes() {
147         mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
148 
149         final WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "statusBar");
150         final Binder owner = new Binder();
151         win.mAttrs.providedInsets = new InsetsFrameProvider[] {
152                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars()),
153                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.systemGestures())
154         };
155         addWindow(win);
156         win.getFrame().set(0, 0, 500, 100);
157         win.updateSourceFrame(win.getFrame());
158         mDisplayContent.getInsetsStateController().onPostLayout();
159 
160         assertTrue(win.hasInsetsSourceProvider());
161         final SparseArray<InsetsSourceProvider> providers = win.getInsetsSourceProviders();
162         for (int i = providers.size() - 1; i >= 0; i--) {
163             final InsetsSourceProvider provider = providers.valueAt(i);
164             assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
165         }
166     }
167 
168     /**
169      * Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as
170      * the real one.
171      */
172     @Test
testSimulateLayoutDisplay()173     public void testSimulateLayoutDisplay() {
174         assertSimulateLayoutSameDisplayFrames();
175         setRotation(ROTATION_90, false /* includingWindows */);
176         assertSimulateLayoutSameDisplayFrames();
177         addDisplayCutout();
178         assertSimulateLayoutSameDisplayFrames();
179         addRoundedCorners();
180         assertSimulateLayoutSameDisplayFrames();
181     }
182 
assertSimulateLayoutSameDisplayFrames()183     private void assertSimulateLayoutSameDisplayFrames() {
184         final String prefix = "";
185         final InsetsState simulatedInsetsState = new InsetsState();
186         final DisplayFrames simulatedDisplayFrames = createDisplayFrames(simulatedInsetsState);
187         // Force the display bounds because it is not synced with display frames in policy test.
188         mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted);
189         mDisplayContent.getInsetsStateController().onPostLayout();
190         mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames);
191 
192         final StringWriter realFramesDump = new StringWriter();
193         mFrames.dump(prefix, new PrintWriter(realFramesDump));
194         final StringWriter simulatedFramesDump = new StringWriter();
195         simulatedDisplayFrames.dump(prefix, new PrintWriter(simulatedFramesDump));
196 
197         assertEquals(new ToStringComparatorWrapper<>(realFramesDump),
198                 new ToStringComparatorWrapper<>(simulatedFramesDump));
199 
200         final InsetsState realInsetsState = new InsetsState(
201                 mDisplayContent.getInsetsStateController().getRawInsetsState());
202         // Exclude comparing IME insets because currently the simulated layout only focuses on the
203         // insets from status bar and navigation bar.
204         realInsetsState.removeSource(InsetsSource.ID_IME);
205 
206         assertEquals(new ToStringComparatorWrapper<>(realInsetsState),
207                 new ToStringComparatorWrapper<>(simulatedInsetsState));
208     }
209 
210     @Test
testFixedRotationInsetsSourceFrame()211     public void testFixedRotationInsetsSourceFrame() {
212         mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT;
213         mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH;
214         doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
215                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
216         mWindow.mAboveInsetsState.set(
217                 mDisplayContent.getInsetsStateController().getRawInsetsState());
218         final int statusBarId = mStatusBarWindow.getControllableInsetProvider().getSource().getId();
219         final Rect frame = mWindow.getInsetsState().peekSource(statusBarId).getFrame();
220         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
221         final Rect rotatedFrame = mWindow.getInsetsState().peekSource(statusBarId).getFrame();
222 
223         assertEquals(DISPLAY_WIDTH, frame.width());
224         assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
225     }
226 }
227