Home | History | Annotate | Download | only in wm
      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.wm;
     18 
     19 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
     20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
     21 import static android.server.wm.ComponentNameUtils.getActivityName;
     22 import static android.server.wm.StateLogger.log;
     23 import static android.server.wm.StateLogger.logAlways;
     24 import static android.server.wm.UiDeviceUtils.pressSleepButton;
     25 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
     26 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
     27 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
     28 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
     29 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
     30 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
     31 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COMMAND;
     32 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COUNT;
     33 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
     34 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
     35 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY;
     36 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
     37 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
     38 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS;
     39 import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
     40 import static android.view.Display.DEFAULT_DISPLAY;
     41 import static android.view.Display.INVALID_DISPLAY;
     42 
     43 import static androidx.test.InstrumentationRegistry.getInstrumentation;
     44 
     45 import static org.hamcrest.MatcherAssert.assertThat;
     46 import static org.hamcrest.Matchers.hasSize;
     47 import static org.junit.Assert.assertEquals;
     48 import static org.junit.Assert.assertTrue;
     49 import static org.junit.Assert.fail;
     50 
     51 import android.content.ComponentName;
     52 import android.content.Context;
     53 import android.content.res.Configuration;
     54 import android.graphics.Rect;
     55 import android.os.Bundle;
     56 import android.os.SystemClock;
     57 import android.provider.Settings;
     58 import android.server.wm.ActivityManagerState.ActivityDisplay;
     59 import android.server.wm.CommandSession.ActivitySession;
     60 import android.server.wm.CommandSession.ActivitySessionClient;
     61 import android.server.wm.settings.SettingsSession;
     62 import android.util.Size;
     63 import android.util.SparseBooleanArray;
     64 import android.view.WindowManager;
     65 
     66 import androidx.annotation.NonNull;
     67 import androidx.annotation.Nullable;
     68 
     69 import com.android.compatibility.common.util.SystemUtil;
     70 import com.android.compatibility.common.util.TestUtils;
     71 import org.junit.Before;
     72 
     73 import java.util.ArrayList;
     74 import java.util.Collections;
     75 import java.util.List;
     76 import java.util.function.Consumer;
     77 import java.util.regex.Matcher;
     78 import java.util.regex.Pattern;
     79 
     80 /**
     81  * Base class for ActivityManager display tests.
     82  *
     83  * @see DisplayTests
     84  * @see MultiDisplayKeyguardTests
     85  * @see MultiDisplayLockedKeyguardTests
     86  */
     87 public class MultiDisplayTestBase extends ActivityManagerTestBase {
     88 
     89     static final int CUSTOM_DENSITY_DPI = 222;
     90     private static final int INVALID_DENSITY_DPI = -1;
     91     protected Context mTargetContext;
     92 
     93     @Before
     94     @Override
     95     public void setUp() throws Exception {
     96         super.setUp();
     97         mTargetContext = getInstrumentation().getTargetContext();
     98     }
     99 
    100     ActivityDisplay getDisplayState(int displayId) {
    101         return getDisplayState(getDisplaysStates(), displayId);
    102     }
    103 
    104     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
    105         for (ActivityDisplay display : displays) {
    106             if (display.mId == displayId) {
    107                 return display;
    108             }
    109         }
    110         return null;
    111     }
    112 
    113     /** Return the display state with width, height, dpi. Always not default display. */
    114     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
    115             int dpi) {
    116         for (ActivityDisplay display : displays) {
    117             if (display.mId == DEFAULT_DISPLAY) {
    118                 continue;
    119             }
    120             final Configuration config = display.mFullConfiguration;
    121             if (config.densityDpi == dpi && config.screenWidthDp == width
    122                     && config.screenHeightDp == height) {
    123                 return display;
    124             }
    125         }
    126         return null;
    127     }
    128 
    129     List<ActivityDisplay> getDisplaysStates() {
    130         mAmWmState.getAmState().computeState();
    131         return mAmWmState.getAmState().getDisplays();
    132     }
    133 
    134     /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
    135     List<ActivityDisplay> findNewDisplayStates(List<ActivityDisplay> oldDisplays,
    136             List<ActivityDisplay> newDisplays) {
    137         final ArrayList<ActivityDisplay> result = new ArrayList<>();
    138 
    139         for (ActivityDisplay newDisplay : newDisplays) {
    140             if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
    141                 result.add(newDisplay);
    142             }
    143         }
    144 
    145         return result;
    146     }
    147 
    148     public static class ReportedDisplayMetrics {
    149         private static final String WM_SIZE = "wm size";
    150         private static final String WM_DENSITY = "wm density";
    151         private static final Pattern PHYSICAL_SIZE =
    152                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
    153         private static final Pattern OVERRIDE_SIZE =
    154                 Pattern.compile("Override size: (\\d+)x(\\d+)");
    155         private static final Pattern PHYSICAL_DENSITY =
    156                 Pattern.compile("Physical density: (\\d+)");
    157         private static final Pattern OVERRIDE_DENSITY =
    158                 Pattern.compile("Override density: (\\d+)");
    159 
    160         @NonNull
    161         final Size physicalSize;
    162         final int physicalDensity;
    163 
    164         @Nullable
    165         final Size overrideSize;
    166         @Nullable
    167         final Integer overrideDensity;
    168 
    169         /** Get physical and override display metrics from WM for specified display. */
    170         public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
    171             return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
    172                             + executeShellCommand(WM_DENSITY + " -d " + displayId));
    173         }
    174 
    175         void setDisplayMetrics(final Size size, final int density) {
    176             setSize(size);
    177             setDensity(density);
    178         }
    179 
    180         void restoreDisplayMetrics() {
    181             if (overrideSize != null) {
    182                 setSize(overrideSize);
    183             } else {
    184                 executeShellCommand(WM_SIZE + " reset");
    185             }
    186             if (overrideDensity != null) {
    187                 setDensity(overrideDensity);
    188             } else {
    189                 executeShellCommand(WM_DENSITY + " reset");
    190             }
    191         }
    192 
    193         private void setSize(final Size size) {
    194             executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
    195         }
    196 
    197         private void setDensity(final int density) {
    198             executeShellCommand(WM_DENSITY + " " + density);
    199         }
    200 
    201         /** Get display size that WM operates with. */
    202         public Size getSize() {
    203             return overrideSize != null ? overrideSize : physicalSize;
    204         }
    205 
    206         /** Get density that WM operates with. */
    207         int getDensity() {
    208             return overrideDensity != null ? overrideDensity : physicalDensity;
    209         }
    210 
    211         private ReportedDisplayMetrics(final String lines) {
    212             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
    213             assertTrue("Physical display size must be reported", matcher.find());
    214             log(matcher.group());
    215             physicalSize = new Size(
    216                     Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
    217 
    218             matcher = PHYSICAL_DENSITY.matcher(lines);
    219             assertTrue("Physical display density must be reported", matcher.find());
    220             log(matcher.group());
    221             physicalDensity = Integer.parseInt(matcher.group(1));
    222 
    223             matcher = OVERRIDE_SIZE.matcher(lines);
    224             if (matcher.find()) {
    225                 log(matcher.group());
    226                 overrideSize = new Size(
    227                         Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
    228             } else {
    229                 overrideSize = null;
    230             }
    231 
    232             matcher = OVERRIDE_DENSITY.matcher(lines);
    233             if (matcher.find()) {
    234                 log(matcher.group());
    235                 overrideDensity = Integer.parseInt(matcher.group(1));
    236             } else {
    237                 overrideDensity = null;
    238             }
    239         }
    240     }
    241 
    242     protected void tapOnDisplayCenter(int displayId) {
    243         final Rect bounds = mAmWmState.getWmState().getDisplay(displayId).getDisplayRect();
    244         tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
    245     }
    246 
    247     /**
    248      * This class should only be used when you need to test virtual display created by a
    249      * non-privileged app.
    250      * Or when you need to test on simulated display.
    251      *
    252      * If you need to test virtual display created by a privileged app, please use
    253      * {@link ExternalDisplaySession} instead.
    254      */
    255     public class VirtualDisplaySession implements AutoCloseable {
    256         private int mDensityDpi = CUSTOM_DENSITY_DPI;
    257         private boolean mLaunchInSplitScreen = false;
    258         private boolean mCanShowWithInsecureKeyguard = false;
    259         private boolean mPublicDisplay = false;
    260         private boolean mResizeDisplay = true;
    261         private boolean mShowSystemDecorations = false;
    262         private boolean mPresentationDisplay = false;
    263         private ComponentName mLaunchActivity = null;
    264         private boolean mSimulateDisplay = false;
    265         private boolean mMustBeCreated = true;
    266         private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */);
    267 
    268         private boolean mVirtualDisplayCreated = false;
    269         private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
    270                 new OverlayDisplayDevicesSession(mContext);
    271 
    272         VirtualDisplaySession setDensityDpi(int densityDpi) {
    273             mDensityDpi = densityDpi;
    274             return this;
    275         }
    276 
    277         VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
    278             mLaunchInSplitScreen = launchInSplitScreen;
    279             return this;
    280         }
    281 
    282         VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
    283             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
    284             return this;
    285         }
    286 
    287         VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
    288             mPublicDisplay = publicDisplay;
    289             return this;
    290         }
    291 
    292         VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
    293             mResizeDisplay = resizeDisplay;
    294             return this;
    295         }
    296 
    297         VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
    298             mShowSystemDecorations = showSystemDecorations;
    299             return this;
    300         }
    301 
    302         VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) {
    303             mPresentationDisplay = presentationDisplay;
    304             return this;
    305         }
    306 
    307         VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
    308             mLaunchActivity = launchActivity;
    309             return this;
    310         }
    311 
    312         public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
    313             mSimulateDisplay = simulateDisplay;
    314             return this;
    315         }
    316 
    317         VirtualDisplaySession setSimulationDisplaySize(int width, int height) {
    318             mSimulationDisplaySize = new Size(width, height);
    319             return this;
    320         }
    321 
    322         VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
    323             mMustBeCreated = mustBeCreated;
    324             return this;
    325         }
    326 
    327         @Nullable
    328         public ActivityDisplay createDisplay() throws Exception {
    329             return createDisplays(1).stream().findFirst().orElse(null);
    330         }
    331 
    332         @NonNull
    333         List<ActivityDisplay> createDisplays(int count) throws Exception {
    334             if (mSimulateDisplay) {
    335                 return simulateDisplay();
    336             } else {
    337                 return createVirtualDisplays(count);
    338             }
    339         }
    340 
    341         void resizeDisplay() {
    342             executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
    343                     + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
    344         }
    345 
    346         @Override
    347         public void close() throws Exception {
    348             mOverlayDisplayDeviceSession.close();
    349             if (mVirtualDisplayCreated) {
    350                 destroyVirtualDisplays();
    351                 mVirtualDisplayCreated = false;
    352             }
    353         }
    354 
    355         /**
    356          * Simulate new display.
    357          * <pre>
    358          * <code>mDensityDpi</code> provide custom density for the display.
    359          * </pre>
    360          * @return {@link ActivityDisplay} of newly created display.
    361          */
    362         private List<ActivityDisplay> simulateDisplay() throws Exception {
    363             final List<ActivityDisplay> originalDs = getDisplaysStates();
    364 
    365             // Create virtual display with custom density dpi and specified size.
    366             mOverlayDisplayDeviceSession.set(mSimulationDisplaySize + "/" + mDensityDpi);
    367             final List<ActivityDisplay> newDisplays = assertAndGetNewDisplays(1, originalDs);
    368 
    369             if (mShowSystemDecorations) {
    370                 for (ActivityDisplay display : newDisplays) {
    371                     mOverlayDisplayDeviceSession.addAndConfigDisplayState(display,
    372                             true /* requestShowSysDecors */, true /* requestShowIme */);
    373                 }
    374             }
    375             return newDisplays;
    376         }
    377 
    378         /**
    379          * Create new virtual display.
    380          * <pre>
    381          * <code>mDensityDpi</code> provide custom density for the display.
    382          * <code>mLaunchInSplitScreen</code> start
    383          *     {@link android.server.wm.app.VirtualDisplayActivity} to side from
    384          *     {@link android.server.wm.app.LaunchingActivity} on primary display.
    385          * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
    386          *     showing an insecure keyguard.
    387          * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
    388          * <code>mPublicDisplay</code> make display public.
    389          * <code>mResizeDisplay</code> should resize display when surface size changes.
    390          * <code>LaunchActivity</code> should launch test activity immediately after display
    391          *     creation.
    392          * </pre>
    393          * @param displayCount number of displays to be created.
    394          * @return A list of {@link ActivityDisplay} that represent newly created displays.
    395          * @throws Exception
    396          */
    397         private List<ActivityDisplay> createVirtualDisplays(int displayCount) {
    398             // Start an activity that is able to create virtual displays.
    399             if (mLaunchInSplitScreen) {
    400                 getLaunchActivityBuilder()
    401                         .setToSide(true)
    402                         .setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
    403                         .execute();
    404             } else {
    405                 launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
    406             }
    407             mAmWmState.computeState(false /* compareTaskAndStackBounds */,
    408                     new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
    409             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
    410             mAmWmState.assertFocusedActivity("Focus must be on virtual display host activity",
    411                     VIRTUAL_DISPLAY_ACTIVITY);
    412             final List<ActivityDisplay> originalDS = getDisplaysStates();
    413 
    414             // Create virtual display with custom density dpi.
    415             final StringBuilder createVirtualDisplayCommand = new StringBuilder(
    416                     getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
    417                     .append(" -f 0x20000000")
    418                     .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
    419             if (mDensityDpi != INVALID_DENSITY_DPI) {
    420                 createVirtualDisplayCommand
    421                         .append(" --ei " + KEY_DENSITY_DPI + " ")
    422                         .append(mDensityDpi);
    423             }
    424             createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
    425                     .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
    426                     .append(mCanShowWithInsecureKeyguard)
    427                     .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
    428                     .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay)
    429                     .append(" --ez " + KEY_SHOW_SYSTEM_DECORATIONS + " ")
    430                     .append(mShowSystemDecorations)
    431                     .append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay);
    432             if (mLaunchActivity != null) {
    433                 createVirtualDisplayCommand
    434                         .append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
    435                         .append(getActivityName(mLaunchActivity));
    436             }
    437             executeShellCommand(createVirtualDisplayCommand.toString());
    438             mVirtualDisplayCreated = true;
    439 
    440             return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
    441         }
    442 
    443         /**
    444          * Destroy existing virtual display.
    445          */
    446         void destroyVirtualDisplays() {
    447             final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
    448                     + " -f 0x20000000"
    449                     + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
    450             executeShellCommand(destroyVirtualDisplayCommand);
    451             waitForDisplaysDestroyed();
    452         }
    453 
    454         private void waitForDisplaysDestroyed() {
    455             for (int retry = 1; retry <= 5; retry++) {
    456                 if (!isHostedVirtualDisplayPresent()) {
    457                     return;
    458                 }
    459                 logAlways("Waiting for hosted displays destruction... retry=" + retry);
    460                 SystemClock.sleep(500);
    461             }
    462             fail("Waiting for hosted displays destruction failed.");
    463         }
    464 
    465         private boolean isHostedVirtualDisplayPresent() {
    466             mAmWmState.computeState(true);
    467             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
    468                     d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
    469         }
    470 
    471         /**
    472          * Wait for desired number of displays to be created and get their properties.
    473          * @param newDisplayCount expected display count, -1 if display should not be created.
    474          * @param originalDS display states before creation of new display(s).
    475          * @return list of new displays, empty list if no new display is created.
    476          */
    477         private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
    478                 List<ActivityDisplay> originalDS) {
    479             final int originalDisplayCount = originalDS.size();
    480 
    481             // Wait for the display(s) to be created and get configurations.
    482             final List<ActivityDisplay> ds = getDisplayStateAfterChange(
    483                     originalDisplayCount + newDisplayCount);
    484             if (newDisplayCount != -1) {
    485                 assertEquals("New virtual display(s) must be created",
    486                         originalDisplayCount + newDisplayCount, ds.size());
    487             } else {
    488                 assertEquals("New virtual display must not be created",
    489                         originalDisplayCount, ds.size());
    490                 return Collections.emptyList();
    491             }
    492 
    493             // Find the newly added display(s).
    494             final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
    495             assertThat("New virtual display must be created",
    496                     newDisplays, hasSize(newDisplayCount));
    497 
    498             return newDisplays;
    499         }
    500     }
    501 
    502     // TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
    503     protected class VirtualDisplayLauncher extends VirtualDisplaySession {
    504         private final ActivitySessionClient mActivitySessionClient = new ActivitySessionClient();
    505 
    506         ActivitySession launchActivityOnDisplay(ComponentName activityName,
    507                 ActivityDisplay display) {
    508             return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */,
    509                     true /* withShellPermission */, true /* waitForLaunch */);
    510         }
    511 
    512         ActivitySession launchActivityOnDisplay(ComponentName activityName,
    513                 ActivityDisplay display, Consumer<Bundle> extrasConsumer,
    514                 boolean withShellPermission, boolean waitForLaunch) {
    515             return launchActivity(builder -> builder
    516                     // VirtualDisplayActivity is in different package. If the display is not public,
    517                     // it requires shell permission to launch activity ({@see com.android.server.wm.
    518                     // ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay}).
    519                     .setWithShellPermission(withShellPermission)
    520                     .setWaitForLaunched(waitForLaunch)
    521                     .setIntentExtra(extrasConsumer)
    522                     .setTargetActivity(activityName)
    523                     .setDisplayId(display.mId));
    524         }
    525 
    526         ActivitySession launchActivity(Consumer<LaunchActivityBuilder> setupBuilder) {
    527             final LaunchActivityBuilder builder = getLaunchActivityBuilder()
    528                     .setUseInstrumentation();
    529             setupBuilder.accept(builder);
    530             return mActivitySessionClient.startActivity(builder);
    531         }
    532 
    533         @Override
    534         public void close() throws Exception {
    535             super.close();
    536             mActivitySessionClient.close();
    537         }
    538     }
    539 
    540     /** Helper class to save, set, and restore overlay_display_devices preference. */
    541     private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
    542         private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>();
    543         private final WindowManager mWm;
    544 
    545         OverlayDisplayDevicesSession(Context context) {
    546             super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
    547                     Settings.Global::getString,
    548                     Settings.Global::putString);
    549             mWm = context.getSystemService(WindowManager.class);
    550         }
    551 
    552         void addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors,
    553                 boolean requestShowIme) {
    554             SystemUtil.runWithShellPermissionIdentity(() -> {
    555                 final boolean showSystemDecors = mWm.shouldShowSystemDecors(display.mId);
    556                 final boolean showIme = mWm.shouldShowIme(display.mId);
    557                 mDisplayStates.add(new OverlayDisplayState(display.mId, showSystemDecors, showIme));
    558                 if (requestShowSysDecors != showSystemDecors) {
    559                     mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
    560                     TestUtils.waitUntil("Waiting for display show system decors",
    561                             5 /* timeoutSecond */,
    562                             () -> mWm.shouldShowSystemDecors(display.mId) == requestShowSysDecors);
    563                 }
    564                 if (requestShowIme != showIme) {
    565                     mWm.setShouldShowIme(display.mId, requestShowIme);
    566                     TestUtils.waitUntil("Waiting for display show Ime",
    567                             5 /* timeoutSecond */,
    568                             () -> mWm.shouldShowIme(display.mId) == requestShowIme);
    569                 }
    570             });
    571         }
    572 
    573         private void restoreDisplayStates() {
    574             mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> {
    575                 mWm.setShouldShowSystemDecors(state.mId, state.mShouldShowSystemDecors);
    576                 mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
    577 
    578                 // Only need to wait the last flag to be set.
    579                 TestUtils.waitUntil("Waiting for the show IME flag to be set",
    580                         5 /* timeoutSecond */,
    581                         () -> mWm.shouldShowIme(state.mId) == state.mShouldShowIme);
    582             }));
    583         }
    584 
    585         @Override
    586         public void close() throws Exception {
    587             // Need to restore display state before display is destroyed.
    588             restoreDisplayStates();
    589             super.close();
    590         }
    591 
    592         private class OverlayDisplayState {
    593             int mId;
    594             boolean mShouldShowSystemDecors;
    595             boolean mShouldShowIme;
    596 
    597             OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme) {
    598                 mId = displayId;
    599                 mShouldShowSystemDecors = showSysDecors;
    600                 mShouldShowIme = showIme;
    601             }
    602         }
    603     }
    604 
    605     /** Wait for provided number of displays and report their configurations. */
    606     List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
    607         List<ActivityDisplay> ds = getDisplaysStates();
    608 
    609         int retriesLeft = 5;
    610         while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
    611             log("***Waiting for the correct number of displays...");
    612             try {
    613                 Thread.sleep(1000);
    614             } catch (InterruptedException e) {
    615                 log(e.toString());
    616             }
    617             ds = getDisplaysStates();
    618         }
    619 
    620         return ds;
    621     }
    622 
    623     private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
    624         if (displays.size() != expectedDisplayCount) {
    625             return false;
    626         }
    627         for (ActivityDisplay display : displays) {
    628             if (display.mOverrideConfiguration.densityDpi == 0) {
    629                 return false;
    630             }
    631         }
    632         return true;
    633     }
    634 
    635     /** Checks if the device supports multi-display. */
    636     protected boolean supportsMultiDisplay() {
    637         return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
    638     }
    639 
    640     /**
    641      * This class is used when you need to test virtual display created by a privileged app.
    642      *
    643      * If you need to test virtual display created by a non-privileged app or when you need to test
    644      * on simulated display, please use {@link VirtualDisplaySession} instead.
    645      */
    646     public class ExternalDisplaySession implements AutoCloseable {
    647 
    648         private boolean mCanShowWithInsecureKeyguard = false;
    649         private boolean mPublicDisplay = false;
    650         private boolean mShowSystemDecorations = false;
    651 
    652         private int mDisplayId = INVALID_DISPLAY;
    653 
    654         @Nullable
    655         private VirtualDisplayHelper mExternalDisplayHelper;
    656 
    657         ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
    658             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
    659             return this;
    660         }
    661 
    662         ExternalDisplaySession setPublicDisplay(boolean publicDisplay) {
    663             mPublicDisplay = publicDisplay;
    664             return this;
    665         }
    666 
    667         ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
    668             mShowSystemDecorations = showSystemDecorations;
    669             return this;
    670         }
    671 
    672         /**
    673          * Creates a private virtual display with insecure keyguard flags set.
    674          */
    675         ActivityDisplay createVirtualDisplay() throws Exception {
    676             final List<ActivityDisplay> originalDS = getDisplaysStates();
    677             final int originalDisplayCount = originalDS.size();
    678 
    679             mExternalDisplayHelper = new VirtualDisplayHelper();
    680             mExternalDisplayHelper
    681                     .setPublicDisplay(mPublicDisplay)
    682                     .setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard)
    683                     .setShowSystemDecorations(mShowSystemDecorations)
    684                     .createAndWaitForDisplay();
    685 
    686             // Wait for the virtual display to be created and get configurations.
    687             final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
    688             assertEquals("New virtual display must be created", originalDisplayCount + 1,
    689                     ds.size());
    690 
    691             // Find the newly added display.
    692             final ActivityDisplay newDisplay = findNewDisplayStates(originalDS, ds).get(0);
    693             mDisplayId = newDisplay.mId;
    694             return newDisplay;
    695         }
    696 
    697         void turnDisplayOff() {
    698             if (mExternalDisplayHelper == null) {
    699                 throw new RuntimeException("No external display created");
    700             }
    701             mExternalDisplayHelper.turnDisplayOff();
    702         }
    703 
    704         void turnDisplayOn() {
    705             if (mExternalDisplayHelper == null) {
    706                 throw new RuntimeException("No external display created");
    707             }
    708             mExternalDisplayHelper.turnDisplayOn();
    709         }
    710 
    711         @Override
    712         public void close() throws Exception {
    713             if (mExternalDisplayHelper != null) {
    714                 mExternalDisplayHelper.releaseDisplay();
    715                 mExternalDisplayHelper = null;
    716 
    717                 waitForHostedDisplayDestroyed();
    718                 mDisplayId = INVALID_DISPLAY;
    719             }
    720         }
    721 
    722         private void waitForHostedDisplayDestroyed() {
    723             for (int retry = 1; retry <= 5; retry++) {
    724                 if (!isHostedVirtualDisplayPresent()) {
    725                     return;
    726                 }
    727                 logAlways("Waiting for hosted displays destruction... retry=" + retry);
    728                 SystemClock.sleep(500);
    729             }
    730             fail("Waiting for hosted displays destruction failed.");
    731         }
    732 
    733         private boolean isHostedVirtualDisplayPresent() {
    734             mAmWmState.computeState(true);
    735             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
    736                     d -> d.getDisplayId() == mDisplayId);
    737         }
    738     }
    739 
    740     public static class PrimaryDisplayStateSession implements AutoCloseable {
    741 
    742         void turnScreenOff() {
    743             setPrimaryDisplayState(false);
    744         }
    745 
    746         @Override
    747         public void close() throws Exception {
    748             setPrimaryDisplayState(true);
    749         }
    750 
    751         /** Turns the primary display on/off by pressing the power key */
    752         private void setPrimaryDisplayState(boolean wantOn) {
    753             if (wantOn) {
    754                 pressWakeupButton();
    755             } else {
    756                 pressSleepButton();
    757             }
    758             VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
    759         }
    760     }
    761 }
    762