blob: 32a20ae8d71875050482f4e1d2a438f154ed7cd3 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package android.server.wm;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.StateLogger.log;
import static android.server.wm.StateLogger.logAlways;
import static android.server.wm.UiDeviceUtils.pressSleepButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COMMAND;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COUNT;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS;
import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.server.wm.ActivityManagerState.ActivityDisplay;
import android.server.wm.CommandSession.ActivitySession;
import android.server.wm.CommandSession.ActivitySessionClient;
import android.server.wm.settings.SettingsSession;
import android.util.Size;
import android.util.SparseBooleanArray;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestUtils;
import org.junit.Before;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Base class for ActivityManager display tests.
*
* @see DisplayTests
* @see MultiDisplayKeyguardTests
* @see MultiDisplayLockedKeyguardTests
*/
public class MultiDisplayTestBase extends ActivityManagerTestBase {
static final int CUSTOM_DENSITY_DPI = 222;
private static final int INVALID_DENSITY_DPI = -1;
protected Context mTargetContext;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mTargetContext = getInstrumentation().getTargetContext();
}
ActivityDisplay getDisplayState(int displayId) {
return getDisplayState(getDisplaysStates(), displayId);
}
ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
for (ActivityDisplay display : displays) {
if (display.mId == displayId) {
return display;
}
}
return null;
}
/** Return the display state with width, height, dpi. Always not default display. */
ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
int dpi) {
for (ActivityDisplay display : displays) {
if (display.mId == DEFAULT_DISPLAY) {
continue;
}
final Configuration config = display.mFullConfiguration;
if (config.densityDpi == dpi && config.screenWidthDp == width
&& config.screenHeightDp == height) {
return display;
}
}
return null;
}
List<ActivityDisplay> getDisplaysStates() {
mAmWmState.getAmState().computeState();
return mAmWmState.getAmState().getDisplays();
}
/** Find the display that was not originally reported in oldDisplays and added in newDisplays */
List<ActivityDisplay> findNewDisplayStates(List<ActivityDisplay> oldDisplays,
List<ActivityDisplay> newDisplays) {
final ArrayList<ActivityDisplay> result = new ArrayList<>();
for (ActivityDisplay newDisplay : newDisplays) {
if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
result.add(newDisplay);
}
}
return result;
}
public static class ReportedDisplayMetrics {
private static final String WM_SIZE = "wm size";
private static final String WM_DENSITY = "wm density";
private static final Pattern PHYSICAL_SIZE =
Pattern.compile("Physical size: (\\d+)x(\\d+)");
private static final Pattern OVERRIDE_SIZE =
Pattern.compile("Override size: (\\d+)x(\\d+)");
private static final Pattern PHYSICAL_DENSITY =
Pattern.compile("Physical density: (\\d+)");
private static final Pattern OVERRIDE_DENSITY =
Pattern.compile("Override density: (\\d+)");
@NonNull
final Size physicalSize;
final int physicalDensity;
@Nullable
final Size overrideSize;
@Nullable
final Integer overrideDensity;
/** Get physical and override display metrics from WM for specified display. */
public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
+ executeShellCommand(WM_DENSITY + " -d " + displayId));
}
void setDisplayMetrics(final Size size, final int density) {
setSize(size);
setDensity(density);
}
void restoreDisplayMetrics() {
if (overrideSize != null) {
setSize(overrideSize);
} else {
executeShellCommand(WM_SIZE + " reset");
}
if (overrideDensity != null) {
setDensity(overrideDensity);
} else {
executeShellCommand(WM_DENSITY + " reset");
}
}
private void setSize(final Size size) {
executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
}
private void setDensity(final int density) {
executeShellCommand(WM_DENSITY + " " + density);
}
/** Get display size that WM operates with. */
public Size getSize() {
return overrideSize != null ? overrideSize : physicalSize;
}
/** Get density that WM operates with. */
int getDensity() {
return overrideDensity != null ? overrideDensity : physicalDensity;
}
private ReportedDisplayMetrics(final String lines) {
Matcher matcher = PHYSICAL_SIZE.matcher(lines);
assertTrue("Physical display size must be reported", matcher.find());
log(matcher.group());
physicalSize = new Size(
Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
matcher = PHYSICAL_DENSITY.matcher(lines);
assertTrue("Physical display density must be reported", matcher.find());
log(matcher.group());
physicalDensity = Integer.parseInt(matcher.group(1));
matcher = OVERRIDE_SIZE.matcher(lines);
if (matcher.find()) {
log(matcher.group());
overrideSize = new Size(
Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
} else {
overrideSize = null;
}
matcher = OVERRIDE_DENSITY.matcher(lines);
if (matcher.find()) {
log(matcher.group());
overrideDensity = Integer.parseInt(matcher.group(1));
} else {
overrideDensity = null;
}
}
}
protected void tapOnDisplayCenter(int displayId) {
final Rect bounds = mAmWmState.getWmState().getDisplay(displayId).getDisplayRect();
tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
}
/**
* This class should only be used when you need to test virtual display created by a
* non-privileged app.
* Or when you need to test on simulated display.
*
* If you need to test virtual display created by a privileged app, please use
* {@link ExternalDisplaySession} instead.
*/
public class VirtualDisplaySession implements AutoCloseable {
private int mDensityDpi = CUSTOM_DENSITY_DPI;
private boolean mLaunchInSplitScreen = false;
private boolean mCanShowWithInsecureKeyguard = false;
private boolean mPublicDisplay = false;
private boolean mResizeDisplay = true;
private boolean mShowSystemDecorations = false;
private boolean mPresentationDisplay = false;
private ComponentName mLaunchActivity = null;
private boolean mSimulateDisplay = false;
private boolean mMustBeCreated = true;
private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */);
private boolean mVirtualDisplayCreated = false;
private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
new OverlayDisplayDevicesSession(mContext);
VirtualDisplaySession setDensityDpi(int densityDpi) {
mDensityDpi = densityDpi;
return this;
}
VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
mLaunchInSplitScreen = launchInSplitScreen;
return this;
}
VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
return this;
}
VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
mPublicDisplay = publicDisplay;
return this;
}
VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
mResizeDisplay = resizeDisplay;
return this;
}
VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
mShowSystemDecorations = showSystemDecorations;
return this;
}
VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) {
mPresentationDisplay = presentationDisplay;
return this;
}
VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
mLaunchActivity = launchActivity;
return this;
}
public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
mSimulateDisplay = simulateDisplay;
return this;
}
VirtualDisplaySession setSimulationDisplaySize(int width, int height) {
mSimulationDisplaySize = new Size(width, height);
return this;
}
VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
mMustBeCreated = mustBeCreated;
return this;
}
@Nullable
public ActivityDisplay createDisplay() throws Exception {
return createDisplays(1).stream().findFirst().orElse(null);
}
@NonNull
List<ActivityDisplay> createDisplays(int count) throws Exception {
if (mSimulateDisplay) {
return simulateDisplay();
} else {
return createVirtualDisplays(count);
}
}
void resizeDisplay() {
executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+ " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
}
@Override
public void close() throws Exception {
mOverlayDisplayDeviceSession.close();
if (mVirtualDisplayCreated) {
destroyVirtualDisplays();
mVirtualDisplayCreated = false;
}
}
/**
* Simulate new display.
* <pre>
* <code>mDensityDpi</code> provide custom density for the display.
* </pre>
* @return {@link ActivityDisplay} of newly created display.
*/
private List<ActivityDisplay> simulateDisplay() throws Exception {
final List<ActivityDisplay> originalDs = getDisplaysStates();
// Create virtual display with custom density dpi and specified size.
mOverlayDisplayDeviceSession.set(mSimulationDisplaySize + "/" + mDensityDpi);
final List<ActivityDisplay> newDisplays = assertAndGetNewDisplays(1, originalDs);
if (mShowSystemDecorations) {
for (ActivityDisplay display : newDisplays) {
mOverlayDisplayDeviceSession.addAndConfigDisplayState(display,
true /* requestShowSysDecors */, true /* requestShowIme */);
}
}
return newDisplays;
}
/**
* Create new virtual display.
* <pre>
* <code>mDensityDpi</code> provide custom density for the display.
* <code>mLaunchInSplitScreen</code> start
* {@link android.server.wm.app.VirtualDisplayActivity} to side from
* {@link android.server.wm.app.LaunchingActivity} on primary display.
* <code>mCanShowWithInsecureKeyguard</code> allow showing content when device is
* showing an insecure keyguard.
* <code>mMustBeCreated</code> should assert if the display was or wasn't created.
* <code>mPublicDisplay</code> make display public.
* <code>mResizeDisplay</code> should resize display when surface size changes.
* <code>LaunchActivity</code> should launch test activity immediately after display
* creation.
* </pre>
* @param displayCount number of displays to be created.
* @return A list of {@link ActivityDisplay} that represent newly created displays.
* @throws Exception
*/
private List<ActivityDisplay> createVirtualDisplays(int displayCount) {
// Start an activity that is able to create virtual displays.
if (mLaunchInSplitScreen) {
getLaunchActivityBuilder()
.setToSide(true)
.setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
.execute();
} else {
launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
}
mAmWmState.computeState(false /* compareTaskAndStackBounds */,
new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
mAmWmState.assertFocusedActivity("Focus must be on virtual display host activity",
VIRTUAL_DISPLAY_ACTIVITY);
final List<ActivityDisplay> originalDS = getDisplaysStates();
// Create virtual display with custom density dpi.
final StringBuilder createVirtualDisplayCommand = new StringBuilder(
getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
.append(" -f 0x20000000")
.append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
if (mDensityDpi != INVALID_DENSITY_DPI) {
createVirtualDisplayCommand
.append(" --ei " + KEY_DENSITY_DPI + " ")
.append(mDensityDpi);
}
createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
.append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
.append(mCanShowWithInsecureKeyguard)
.append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
.append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay)
.append(" --ez " + KEY_SHOW_SYSTEM_DECORATIONS + " ")
.append(mShowSystemDecorations)
.append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay);
if (mLaunchActivity != null) {
createVirtualDisplayCommand
.append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
.append(getActivityName(mLaunchActivity));
}
executeShellCommand(createVirtualDisplayCommand.toString());
mVirtualDisplayCreated = true;
return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
}
/**
* Destroy existing virtual display.
*/
void destroyVirtualDisplays() {
final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+ " -f 0x20000000"
+ " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
executeShellCommand(destroyVirtualDisplayCommand);
waitForDisplaysDestroyed();
}
private void waitForDisplaysDestroyed() {
for (int retry = 1; retry <= 5; retry++) {
if (!isHostedVirtualDisplayPresent()) {
return;
}
logAlways("Waiting for hosted displays destruction... retry=" + retry);
SystemClock.sleep(500);
}
fail("Waiting for hosted displays destruction failed.");
}
private boolean isHostedVirtualDisplayPresent() {
mAmWmState.computeState(true);
return mAmWmState.getWmState().getDisplays().stream().anyMatch(
d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
}
/**
* Wait for desired number of displays to be created and get their properties.
* @param newDisplayCount expected display count, -1 if display should not be created.
* @param originalDS display states before creation of new display(s).
* @return list of new displays, empty list if no new display is created.
*/
private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
List<ActivityDisplay> originalDS) {
final int originalDisplayCount = originalDS.size();
// Wait for the display(s) to be created and get configurations.
final List<ActivityDisplay> ds = getDisplayStateAfterChange(
originalDisplayCount + newDisplayCount);
if (newDisplayCount != -1) {
assertEquals("New virtual display(s) must be created",
originalDisplayCount + newDisplayCount, ds.size());
} else {
assertEquals("New virtual display must not be created",
originalDisplayCount, ds.size());
return Collections.emptyList();
}
// Find the newly added display(s).
final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
assertThat("New virtual display must be created",
newDisplays, hasSize(newDisplayCount));
return newDisplays;
}
}
// TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
protected class VirtualDisplayLauncher extends VirtualDisplaySession {
private final ActivitySessionClient mActivitySessionClient = new ActivitySessionClient();
ActivitySession launchActivityOnDisplay(ComponentName activityName,
ActivityDisplay display) {
return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */,
true /* withShellPermission */, true /* waitForLaunch */);
}
ActivitySession launchActivityOnDisplay(ComponentName activityName,
ActivityDisplay display, Consumer<Bundle> extrasConsumer,
boolean withShellPermission, boolean waitForLaunch) {
return launchActivity(builder -> builder
// VirtualDisplayActivity is in different package. If the display is not public,
// it requires shell permission to launch activity ({@see com.android.server.wm.
// ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay}).
.setWithShellPermission(withShellPermission)
.setWaitForLaunched(waitForLaunch)
.setIntentExtra(extrasConsumer)
.setTargetActivity(activityName)
.setDisplayId(display.mId));
}
ActivitySession launchActivity(Consumer<LaunchActivityBuilder> setupBuilder) {
final LaunchActivityBuilder builder = getLaunchActivityBuilder()
.setUseInstrumentation();
setupBuilder.accept(builder);
return mActivitySessionClient.startActivity(builder);
}
@Override
public void close() throws Exception {
super.close();
mActivitySessionClient.close();
}
}
/** Helper class to save, set, and restore overlay_display_devices preference. */
private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>();
private final WindowManager mWm;
OverlayDisplayDevicesSession(Context context) {
super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
Settings.Global::getString,
Settings.Global::putString);
mWm = context.getSystemService(WindowManager.class);
}
void addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors,
boolean requestShowIme) {
SystemUtil.runWithShellPermissionIdentity(() -> {
final boolean showSystemDecors = mWm.shouldShowSystemDecors(display.mId);
final boolean showIme = mWm.shouldShowIme(display.mId);
mDisplayStates.add(new OverlayDisplayState(display.mId, showSystemDecors, showIme));
if (requestShowSysDecors != showSystemDecors) {
mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
TestUtils.waitUntil("Waiting for display show system decors",
5 /* timeoutSecond */,
() -> mWm.shouldShowSystemDecors(display.mId) == requestShowSysDecors);
}
if (requestShowIme != showIme) {
mWm.setShouldShowIme(display.mId, requestShowIme);
TestUtils.waitUntil("Waiting for display show Ime",
5 /* timeoutSecond */,
() -> mWm.shouldShowIme(display.mId) == requestShowIme);
}
});
}
private void restoreDisplayStates() {
mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> {
mWm.setShouldShowSystemDecors(state.mId, state.mShouldShowSystemDecors);
mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
// Only need to wait the last flag to be set.
TestUtils.waitUntil("Waiting for the show IME flag to be set",
5 /* timeoutSecond */,
() -> mWm.shouldShowIme(state.mId) == state.mShouldShowIme);
}));
}
@Override
public void close() throws Exception {
// Need to restore display state before display is destroyed.
restoreDisplayStates();
super.close();
}
private class OverlayDisplayState {
int mId;
boolean mShouldShowSystemDecors;
boolean mShouldShowIme;
OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme) {
mId = displayId;
mShouldShowSystemDecors = showSysDecors;
mShouldShowIme = showIme;
}
}
}
/** Wait for provided number of displays and report their configurations. */
List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
List<ActivityDisplay> ds = getDisplaysStates();
int retriesLeft = 5;
while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
log("***Waiting for the correct number of displays...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
log(e.toString());
}
ds = getDisplaysStates();
}
return ds;
}
private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
if (displays.size() != expectedDisplayCount) {
return false;
}
for (ActivityDisplay display : displays) {
if (display.mOverrideConfiguration.densityDpi == 0) {
return false;
}
}
return true;
}
/** Checks if the device supports multi-display. */
protected boolean supportsMultiDisplay() {
return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
}
/**
* This class is used when you need to test virtual display created by a privileged app.
*
* If you need to test virtual display created by a non-privileged app or when you need to test
* on simulated display, please use {@link VirtualDisplaySession} instead.
*/
public class ExternalDisplaySession implements AutoCloseable {
private boolean mCanShowWithInsecureKeyguard = false;
private boolean mPublicDisplay = false;
private boolean mShowSystemDecorations = false;
private int mDisplayId = INVALID_DISPLAY;
@Nullable
private VirtualDisplayHelper mExternalDisplayHelper;
ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
return this;
}
ExternalDisplaySession setPublicDisplay(boolean publicDisplay) {
mPublicDisplay = publicDisplay;
return this;
}
ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
mShowSystemDecorations = showSystemDecorations;
return this;
}
/**
* Creates a private virtual display with insecure keyguard flags set.
*/
ActivityDisplay createVirtualDisplay() throws Exception {
final List<ActivityDisplay> originalDS = getDisplaysStates();
final int originalDisplayCount = originalDS.size();
mExternalDisplayHelper = new VirtualDisplayHelper();
mExternalDisplayHelper
.setPublicDisplay(mPublicDisplay)
.setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard)
.setShowSystemDecorations(mShowSystemDecorations)
.createAndWaitForDisplay();
// Wait for the virtual display to be created and get configurations.
final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
assertEquals("New virtual display must be created", originalDisplayCount + 1,
ds.size());
// Find the newly added display.
final ActivityDisplay newDisplay = findNewDisplayStates(originalDS, ds).get(0);
mDisplayId = newDisplay.mId;
return newDisplay;
}
void turnDisplayOff() {
if (mExternalDisplayHelper == null) {
throw new RuntimeException("No external display created");
}
mExternalDisplayHelper.turnDisplayOff();
}
void turnDisplayOn() {
if (mExternalDisplayHelper == null) {
throw new RuntimeException("No external display created");
}
mExternalDisplayHelper.turnDisplayOn();
}
@Override
public void close() throws Exception {
if (mExternalDisplayHelper != null) {
mExternalDisplayHelper.releaseDisplay();
mExternalDisplayHelper = null;
waitForHostedDisplayDestroyed();
mDisplayId = INVALID_DISPLAY;
}
}
private void waitForHostedDisplayDestroyed() {
for (int retry = 1; retry <= 5; retry++) {
if (!isHostedVirtualDisplayPresent()) {
return;
}
logAlways("Waiting for hosted displays destruction... retry=" + retry);
SystemClock.sleep(500);
}
fail("Waiting for hosted displays destruction failed.");
}
private boolean isHostedVirtualDisplayPresent() {
mAmWmState.computeState(true);
return mAmWmState.getWmState().getDisplays().stream().anyMatch(
d -> d.getDisplayId() == mDisplayId);
}
}
public static class PrimaryDisplayStateSession implements AutoCloseable {
void turnScreenOff() {
setPrimaryDisplayState(false);
}
@Override
public void close() throws Exception {
setPrimaryDisplayState(true);
}
/** Turns the primary display on/off by pressing the power key */
private void setPrimaryDisplayState(boolean wantOn) {
if (wantOn) {
pressWakeupButton();
} else {
pressSleepButton();
}
VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
}
}
}