1 /*
2  * Copyright (C) 2020 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.display;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.any;
24 import static org.mockito.Mockito.eq;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.reset;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.app.PropertyInvalidatedCache;
31 import android.graphics.Point;
32 import android.util.SparseArray;
33 import android.view.Display;
34 import android.view.DisplayInfo;
35 import android.view.Surface;
36 import android.view.SurfaceControl;
37 
38 import androidx.test.filters.SmallTest;
39 
40 import com.android.server.display.layout.Layout;
41 
42 import org.junit.Before;
43 import org.junit.Ignore;
44 import org.junit.Test;
45 
46 import java.io.InputStream;
47 import java.io.OutputStream;
48 
49 @SmallTest
50 public class LogicalDisplayTest {
51     private static final int DISPLAY_ID = 0;
52     private static final int LAYER_STACK = 0;
53     private static final int DISPLAY_WIDTH = 100;
54     private static final int DISPLAY_HEIGHT = 200;
55     private static final int MODE_ID = 1;
56 
57     private LogicalDisplay mLogicalDisplay;
58     private DisplayDevice mDisplayDevice;
59     private DisplayDeviceRepository mDeviceRepo;
60     private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo();
61 
62     @Before
setUp()63     public void setUp() {
64         // Share classloader to allow package private access.
65         System.setProperty("dexmaker.share_classloader", "true");
66         mDisplayDevice = mock(DisplayDevice.class);
67         mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
68 
69         mDisplayDeviceInfo.copyFrom(new DisplayDeviceInfo());
70         mDisplayDeviceInfo.width = DISPLAY_WIDTH;
71         mDisplayDeviceInfo.height = DISPLAY_HEIGHT;
72         mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
73         mDisplayDeviceInfo.modeId = MODE_ID;
74         mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID,
75                 DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)};
76         when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo);
77 
78         // Disable binder caches in this process.
79         PropertyInvalidatedCache.disableForTestMode();
80 
81         mDeviceRepo = new DisplayDeviceRepository(
82                 new DisplayManagerService.SyncRoot(),
83                 new PersistentDataStore(new PersistentDataStore.Injector() {
84                     @Override
85                     public InputStream openRead() {
86                         return null;
87                     }
88 
89                     @Override
90                     public OutputStream startWrite() {
91                         return null;
92                     }
93 
94                     @Override
95                     public void finishWrite(OutputStream os, boolean success) {}
96                 }));
97         mDeviceRepo.onDisplayDeviceEvent(mDisplayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED);
98         mLogicalDisplay.updateLocked(mDeviceRepo);
99     }
100 
101     @Test
testGetDisplayPosition()102     public void testGetDisplayPosition() {
103         Point expectedPosition = new Point();
104 
105         SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
106         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
107         assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
108 
109         expectedPosition.set(20, 40);
110         mLogicalDisplay.setDisplayOffsetsLocked(20, 40);
111         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
112         assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
113 
114         DisplayInfo displayInfo = new DisplayInfo();
115         displayInfo.logicalWidth = DISPLAY_WIDTH;
116         displayInfo.logicalHeight = DISPLAY_HEIGHT;
117         // Rotation doesn't matter when the FLAG_ROTATES_WITH_CONTENT is absent.
118         displayInfo.rotation = Surface.ROTATION_90;
119         mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo);
120         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
121         assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
122 
123         expectedPosition.set(40, -20);
124         mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
125         mLogicalDisplay.updateLocked(mDeviceRepo);
126         displayInfo.logicalWidth = DISPLAY_HEIGHT;
127         displayInfo.logicalHeight = DISPLAY_WIDTH;
128         displayInfo.rotation = Surface.ROTATION_90;
129         mLogicalDisplay.setDisplayInfoOverrideFromWindowManagerLocked(displayInfo);
130         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
131         assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
132     }
133 
134     // TODO: b/288880734 - fix test after display tests migration
135     @Test
136     @Ignore
testDisplayInputFlags()137     public void testDisplayInputFlags() {
138         SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
139         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
140         verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
141         reset(t);
142 
143         mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
144         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
145         verify(t).setDisplayFlags(any(), eq(0));
146         reset(t);
147 
148         mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
149         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
150         verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
151         reset(t);
152 
153         mLogicalDisplay.setEnabledLocked(false);
154         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
155         verify(t).setDisplayFlags(any(), eq(0));
156         reset(t);
157 
158         mLogicalDisplay.setEnabledLocked(true);
159         mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
160         mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
161         verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT));
162         reset(t);
163     }
164 
165     @Test
testRearDisplaysArePresentationDisplaysThatDestroyContentOnRemoval()166     public void testRearDisplaysArePresentationDisplaysThatDestroyContentOnRemoval() {
167         // Assert that the display isn't a presentation display by default, with a default remove
168         // mode
169         assertEquals(0, mLogicalDisplay.getDisplayInfoLocked().flags);
170         assertEquals(Display.REMOVE_MODE_MOVE_CONTENT_TO_PRIMARY,
171                 mLogicalDisplay.getDisplayInfoLocked().removeMode);
172 
173         // Update position and test to see that it's been updated to a rear, presentation display
174         // that destroys content on removal
175         mLogicalDisplay.setDevicePositionLocked(Layout.Display.POSITION_REAR);
176         mLogicalDisplay.updateLocked(mDeviceRepo);
177         assertEquals(Display.FLAG_REAR | Display.FLAG_PRESENTATION,
178                 mLogicalDisplay.getDisplayInfoLocked().flags);
179         assertEquals(Display.REMOVE_MODE_DESTROY_CONTENT,
180                 mLogicalDisplay.getDisplayInfoLocked().removeMode);
181 
182         // And then check the unsetting the position resets both
183         mLogicalDisplay.setDevicePositionLocked(Layout.Display.POSITION_UNKNOWN);
184         mLogicalDisplay.updateLocked(mDeviceRepo);
185         assertEquals(0, mLogicalDisplay.getDisplayInfoLocked().flags);
186         assertEquals(Display.REMOVE_MODE_MOVE_CONTENT_TO_PRIMARY,
187                 mLogicalDisplay.getDisplayInfoLocked().removeMode);
188     }
189 
190     @Test
testUpdateLayoutLimitedRefreshRate()191     public void testUpdateLayoutLimitedRefreshRate() {
192         SurfaceControl.RefreshRateRange layoutLimitedRefreshRate =
193                 new SurfaceControl.RefreshRateRange(0, 120);
194         DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
195         mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate);
196         DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
197         // Display info should only be updated when updateLocked is called
198         assertEquals(info2, info1);
199 
200         mLogicalDisplay.updateLocked(mDeviceRepo);
201         DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
202         assertNotEquals(info3, info2);
203         assertEquals(layoutLimitedRefreshRate, info3.layoutLimitedRefreshRate);
204     }
205 
206     @Test
testUpdateLayoutLimitedRefreshRate_setsDirtyFlag()207     public void testUpdateLayoutLimitedRefreshRate_setsDirtyFlag() {
208         SurfaceControl.RefreshRateRange layoutLimitedRefreshRate =
209                 new SurfaceControl.RefreshRateRange(0, 120);
210         assertFalse(mLogicalDisplay.isDirtyLocked());
211 
212         mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate);
213         assertTrue(mLogicalDisplay.isDirtyLocked());
214 
215         mLogicalDisplay.updateLocked(mDeviceRepo);
216         assertFalse(mLogicalDisplay.isDirtyLocked());
217     }
218 
219     @Test
testUpdateRefreshRateThermalThrottling()220     public void testUpdateRefreshRateThermalThrottling() {
221         SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
222         refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
223         DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
224         mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges);
225         DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
226         // Display info should only be updated when updateLocked is called
227         assertEquals(info2, info1);
228 
229         mLogicalDisplay.updateLocked(mDeviceRepo);
230         DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
231         assertNotEquals(info3, info2);
232         assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling));
233     }
234 
235     @Test
testUpdateRefreshRateThermalThrottling_setsDirtyFlag()236     public void testUpdateRefreshRateThermalThrottling_setsDirtyFlag() {
237         SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
238         refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
239         assertFalse(mLogicalDisplay.isDirtyLocked());
240 
241         mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges);
242         assertTrue(mLogicalDisplay.isDirtyLocked());
243 
244         mLogicalDisplay.updateLocked(mDeviceRepo);
245         assertFalse(mLogicalDisplay.isDirtyLocked());
246     }
247 
248     @Test
testUpdateDisplayGroupIdLocked()249     public void testUpdateDisplayGroupIdLocked() {
250         int newId = 999;
251         DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
252         mLogicalDisplay.updateDisplayGroupIdLocked(newId);
253         DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
254         // Display info should only be updated when updateLocked is called
255         assertEquals(info2, info1);
256 
257         mLogicalDisplay.updateLocked(mDeviceRepo);
258         DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
259         assertNotEquals(info3, info2);
260         assertEquals(newId, info3.displayGroupId);
261     }
262 
263     @Test
testUpdateDisplayGroupIdLocked_setsDirtyFlag()264     public void testUpdateDisplayGroupIdLocked_setsDirtyFlag() {
265         assertFalse(mLogicalDisplay.isDirtyLocked());
266 
267         mLogicalDisplay.updateDisplayGroupIdLocked(99);
268         assertTrue(mLogicalDisplay.isDirtyLocked());
269 
270         mLogicalDisplay.updateLocked(mDeviceRepo);
271         assertFalse(mLogicalDisplay.isDirtyLocked());
272     }
273 
274     @Test
testSetThermalBrightnessThrottlingDataId()275     public void testSetThermalBrightnessThrottlingDataId() {
276         String brightnessThrottlingDataId = "throttling_data_id";
277         DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
278         mLogicalDisplay.setThermalBrightnessThrottlingDataIdLocked(brightnessThrottlingDataId);
279         DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
280         // Display info should only be updated when updateLocked is called
281         assertEquals(info2, info1);
282 
283         mLogicalDisplay.updateLocked(mDeviceRepo);
284         DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
285         assertNotEquals(info3, info2);
286         assertEquals(brightnessThrottlingDataId, info3.thermalBrightnessThrottlingDataId);
287     }
288 
289     @Test
testSetThermalBrightnessThrottlingDataId_setsDirtyFlag()290     public void testSetThermalBrightnessThrottlingDataId_setsDirtyFlag() {
291         assertFalse(mLogicalDisplay.isDirtyLocked());
292 
293         mLogicalDisplay.setThermalBrightnessThrottlingDataIdLocked("99");
294         assertTrue(mLogicalDisplay.isDirtyLocked());
295 
296         mLogicalDisplay.updateLocked(mDeviceRepo);
297         assertFalse(mLogicalDisplay.isDirtyLocked());
298     }
299 }
300