1 /*
2  * Copyright (C) 2017 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 android.server.am;
18 
19 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
21 import static android.server.am.StateLogger.logAlways;
22 import static android.support.test.InstrumentationRegistry.getContext;
23 import static android.support.test.InstrumentationRegistry.getInstrumentation;
24 
25 import static org.junit.Assert.fail;
26 
27 import android.graphics.PixelFormat;
28 import android.hardware.display.DisplayManager;
29 import android.hardware.display.VirtualDisplay;
30 import android.media.ImageReader;
31 import android.os.SystemClock;
32 import androidx.annotation.Nullable;
33 
34 import com.android.compatibility.common.util.SystemUtil;
35 
36 import java.io.IOException;
37 import java.util.Objects;
38 import java.util.function.Predicate;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 
42 /**
43  * Helper class to create virtual display.
44  */
45 class VirtualDisplayHelper {
46 
47     private static final String VIRTUAL_DISPLAY_NAME = "CtsVirtualDisplay";
48     /** See {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD}. */
49     private static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
50 
51     private static final Pattern DISPLAY_DEVICE_PATTERN = Pattern.compile(
52             ".*DisplayDeviceInfo\\{\"([^\"]+)\":.*, state (\\S+),.*\\}.*");
53     private static final int DENSITY = 160;
54     private static final int HEIGHT = 480;
55     private static final int WIDTH = 800;
56 
57     private ImageReader mReader;
58     private VirtualDisplay mVirtualDisplay;
59     private boolean mCreated;
60 
createAndWaitForDisplay(boolean requestShowWhenLocked)61     void createAndWaitForDisplay(boolean requestShowWhenLocked) {
62         createVirtualDisplay(requestShowWhenLocked);
63         waitForDisplayState(false /* default */, true /* on */);
64         mCreated = true;
65     }
66 
turnDisplayOff()67     void turnDisplayOff() {
68         mVirtualDisplay.setSurface(null);
69         waitForDisplayState(false /* default */, false /* on */);
70     }
71 
turnDisplayOn()72     void turnDisplayOn() {
73         mVirtualDisplay.setSurface(mReader.getSurface());
74         waitForDisplayState(false /* default */, true /* on */);
75     }
76 
releaseDisplay()77     void releaseDisplay() {
78         if (mCreated) {
79             mVirtualDisplay.release();
80             mReader.close();
81             waitForDisplayCondition(false /* defaultDisplay */, Objects::isNull,
82                     "Waiting for virtual display destroy");
83         }
84         mCreated = false;
85     }
86 
createVirtualDisplay(boolean requestShowWhenLocked)87     private void createVirtualDisplay(boolean requestShowWhenLocked) {
88         mReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2);
89 
90         final DisplayManager displayManager = getContext().getSystemService(DisplayManager.class);
91 
92         int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
93         if (requestShowWhenLocked) {
94             flags |= VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
95         }
96         mVirtualDisplay = displayManager.createVirtualDisplay(
97                 VIRTUAL_DISPLAY_NAME, WIDTH, HEIGHT, DENSITY, mReader.getSurface(), flags);
98     }
99 
waitForDefaultDisplayState(boolean wantOn)100     static void waitForDefaultDisplayState(boolean wantOn) {
101         waitForDisplayState(true /* default */, wantOn);
102     }
103 
waitForDisplayState(boolean defaultDisplay, boolean wantOn)104     private static void waitForDisplayState(boolean defaultDisplay, boolean wantOn) {
105         waitForDisplayCondition(defaultDisplay, state -> state != null && state == wantOn,
106                 "Waiting for " + (defaultDisplay ? "default" : "virtual") + " display "
107                         + (wantOn ? "on" : "off"));
108     }
109 
waitForDisplayCondition(boolean defaultDisplay, Predicate<Boolean> condition, String message)110     private static void waitForDisplayCondition(boolean defaultDisplay,
111             Predicate<Boolean> condition, String message) {
112         for (int retry = 1; retry <= 10; retry++) {
113             if (condition.test(getDisplayState(defaultDisplay))) {
114                 return;
115             }
116             logAlways(message + "... retry=" + retry);
117             SystemClock.sleep(500);
118         }
119         fail(message + " failed");
120     }
121 
122     @Nullable
getDisplayState(boolean defaultDisplay)123     private static Boolean getDisplayState(boolean defaultDisplay) {
124         final String dump = executeShellCommand("dumpsys display");
125         final Predicate<Matcher> displayNameMatcher = defaultDisplay
126                 ? m -> m.group(0).contains("FLAG_DEFAULT_DISPLAY")
127                 : m -> m.group(1).equals(VIRTUAL_DISPLAY_NAME);
128         for (final String line : dump.split("\\n")) {
129             final Matcher matcher = DISPLAY_DEVICE_PATTERN.matcher(line);
130             if (matcher.matches() && displayNameMatcher.test(matcher)) {
131                 return "ON".equals(matcher.group(2));
132             }
133         }
134         return null;
135     }
136 
executeShellCommand(String command)137     private static String executeShellCommand(String command) {
138         try {
139             return SystemUtil.runShellCommand(getInstrumentation(), command);
140         } catch (IOException e) {
141             //bubble it up
142             throw new RuntimeException(e);
143         }
144     }
145 }
146