Home | History | Annotate | Download | only in am
      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.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
     20 import static android.server.am.ComponentNameUtils.getActivityName;
     21 import static android.server.am.Components.VIRTUAL_DISPLAY_ACTIVITY;
     22 import static android.server.am.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
     23 import static android.server.am.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
     24 import static android.server.am.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
     25 import static android.server.am.Components.VirtualDisplayActivity
     26         .KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
     27 import static android.server.am.Components.VirtualDisplayActivity.KEY_COMMAND;
     28 import static android.server.am.Components.VirtualDisplayActivity.KEY_COUNT;
     29 import static android.server.am.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
     30 import static android.server.am.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
     31 import static android.server.am.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
     32 import static android.server.am.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
     33 import static android.server.am.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
     34 import static android.server.am.StateLogger.log;
     35 import static android.server.am.StateLogger.logAlways;
     36 import static android.view.Display.DEFAULT_DISPLAY;
     37 
     38 import static org.hamcrest.MatcherAssert.assertThat;
     39 import static org.hamcrest.Matchers.hasSize;
     40 import static org.junit.Assert.assertEquals;
     41 import static org.junit.Assert.assertTrue;
     42 import static org.junit.Assert.fail;
     43 
     44 import android.content.ComponentName;
     45 import android.content.res.Configuration;
     46 import android.os.SystemClock;
     47 import android.provider.Settings;
     48 import android.server.am.ActivityManagerState.ActivityDisplay;
     49 import android.server.am.settings.SettingsSession;
     50 import androidx.annotation.NonNull;
     51 import androidx.annotation.Nullable;
     52 import android.util.Size;
     53 
     54 import java.util.ArrayList;
     55 import java.util.Collections;
     56 import java.util.List;
     57 import java.util.regex.Matcher;
     58 import java.util.regex.Pattern;
     59 
     60 /**
     61  * Base class for ActivityManager display tests.
     62  *
     63  * @see ActivityManagerDisplayTests
     64  * @see ActivityManagerDisplayLockedKeyguardTests
     65  */
     66 class ActivityManagerDisplayTestBase extends ActivityManagerTestBase {
     67 
     68     static final int CUSTOM_DENSITY_DPI = 222;
     69     private static final int INVALID_DENSITY_DPI = -1;
     70 
     71     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
     72         for (ActivityDisplay display : displays) {
     73             if (display.mId == displayId) {
     74                 return display;
     75             }
     76         }
     77         return null;
     78     }
     79 
     80     /** Return the display state with width, height, dpi. Always not default display. */
     81     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
     82             int dpi) {
     83         for (ActivityDisplay display : displays) {
     84             if (display.mId == DEFAULT_DISPLAY) {
     85                 continue;
     86             }
     87             final Configuration config = display.mFullConfiguration;
     88             if (config.densityDpi == dpi && config.screenWidthDp == width
     89                     && config.screenHeightDp == height) {
     90                 return display;
     91             }
     92         }
     93         return null;
     94     }
     95 
     96     List<ActivityDisplay> getDisplaysStates() {
     97         mAmWmState.getAmState().computeState();
     98         return mAmWmState.getAmState().getDisplays();
     99     }
    100 
    101     /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
    102     List<ActivityDisplay> findNewDisplayStates(List<ActivityDisplay> oldDisplays,
    103             List<ActivityDisplay> newDisplays) {
    104         final ArrayList<ActivityDisplay> result = new ArrayList<>();
    105 
    106         for (ActivityDisplay newDisplay : newDisplays) {
    107             if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
    108                 result.add(newDisplay);
    109             }
    110         }
    111 
    112         return result;
    113     }
    114 
    115     static class ReportedDisplayMetrics {
    116         private static final String WM_SIZE = "wm size";
    117         private static final String WM_DENSITY = "wm density";
    118         private static final Pattern PHYSICAL_SIZE =
    119                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
    120         private static final Pattern OVERRIDE_SIZE =
    121                 Pattern.compile("Override size: (\\d+)x(\\d+)");
    122         private static final Pattern PHYSICAL_DENSITY =
    123                 Pattern.compile("Physical density: (\\d+)");
    124         private static final Pattern OVERRIDE_DENSITY =
    125                 Pattern.compile("Override density: (\\d+)");
    126 
    127         @NonNull
    128         final Size physicalSize;
    129         final int physicalDensity;
    130 
    131         @Nullable
    132         final Size overrideSize;
    133         @Nullable
    134         final Integer overrideDensity;
    135 
    136         /** Get physical and override display metrics from WM. */
    137         static ReportedDisplayMetrics getDisplayMetrics() {
    138             return new ReportedDisplayMetrics(
    139                     executeShellCommand(WM_SIZE) + executeShellCommand(WM_DENSITY));
    140         }
    141 
    142         void setDisplayMetrics(final Size size, final int density) {
    143             setSize(size);
    144             setDensity(density);
    145         }
    146 
    147         void restoreDisplayMetrics() {
    148             if (overrideSize != null) {
    149                 setSize(overrideSize);
    150             } else {
    151                 executeShellCommand(WM_SIZE + " reset");
    152             }
    153             if (overrideDensity != null) {
    154                 setDensity(overrideDensity);
    155             } else {
    156                 executeShellCommand(WM_DENSITY + " reset");
    157             }
    158         }
    159 
    160         private void setSize(final Size size) {
    161             executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
    162         }
    163 
    164         private void setDensity(final int density) {
    165             executeShellCommand(WM_DENSITY + " " + density);
    166         }
    167 
    168         /** Get display size that WM operates with. */
    169         Size getSize() {
    170             return overrideSize != null ? overrideSize : physicalSize;
    171         }
    172 
    173         /** Get density that WM operates with. */
    174         int getDensity() {
    175             return overrideDensity != null ? overrideDensity : physicalDensity;
    176         }
    177 
    178         private ReportedDisplayMetrics(final String lines) {
    179             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
    180             assertTrue("Physical display size must be reported", matcher.find());
    181             log(matcher.group());
    182             physicalSize = new Size(
    183                     Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
    184 
    185             matcher = PHYSICAL_DENSITY.matcher(lines);
    186             assertTrue("Physical display density must be reported", matcher.find());
    187             log(matcher.group());
    188             physicalDensity = Integer.parseInt(matcher.group(1));
    189 
    190             matcher = OVERRIDE_SIZE.matcher(lines);
    191             if (matcher.find()) {
    192                 log(matcher.group());
    193                 overrideSize = new Size(
    194                         Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
    195             } else {
    196                 overrideSize = null;
    197             }
    198 
    199             matcher = OVERRIDE_DENSITY.matcher(lines);
    200             if (matcher.find()) {
    201                 log(matcher.group());
    202                 overrideDensity = Integer.parseInt(matcher.group(1));
    203             } else {
    204                 overrideDensity = null;
    205             }
    206         }
    207     }
    208 
    209     protected class VirtualDisplaySession implements AutoCloseable {
    210         private int mDensityDpi = CUSTOM_DENSITY_DPI;
    211         private boolean mLaunchInSplitScreen = false;
    212         private boolean mCanShowWithInsecureKeyguard = false;
    213         private boolean mPublicDisplay = false;
    214         private boolean mResizeDisplay = true;
    215         private ComponentName mLaunchActivity = null;
    216         private boolean mSimulateDisplay = false;
    217         private boolean mMustBeCreated = true;
    218 
    219         private boolean mVirtualDisplayCreated = false;
    220         private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
    221                 new OverlayDisplayDevicesSession();
    222 
    223         VirtualDisplaySession setDensityDpi(int densityDpi) {
    224             mDensityDpi = densityDpi;
    225             return this;
    226         }
    227 
    228         VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
    229             mLaunchInSplitScreen = launchInSplitScreen;
    230             return this;
    231         }
    232 
    233         VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
    234             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
    235             return this;
    236         }
    237 
    238         VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
    239             mPublicDisplay = publicDisplay;
    240             return this;
    241         }
    242 
    243         VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
    244             mResizeDisplay = resizeDisplay;
    245             return this;
    246         }
    247 
    248         VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
    249             mLaunchActivity = launchActivity;
    250             return this;
    251         }
    252 
    253         VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
    254             mSimulateDisplay = simulateDisplay;
    255             return this;
    256         }
    257 
    258         VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
    259             mMustBeCreated = mustBeCreated;
    260             return this;
    261         }
    262 
    263         @Nullable
    264         ActivityDisplay createDisplay() throws Exception {
    265             return createDisplays(1).stream().findFirst().orElse(null);
    266         }
    267 
    268         @NonNull
    269         List<ActivityDisplay> createDisplays(int count) throws Exception {
    270             if (mSimulateDisplay) {
    271                 return simulateDisplay();
    272             } else {
    273                 return createVirtualDisplays(count);
    274             }
    275         }
    276 
    277         void resizeDisplay() {
    278             executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
    279                     + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
    280         }
    281 
    282         @Override
    283         public void close() throws Exception {
    284             mOverlayDisplayDeviceSession.close();
    285             if (mVirtualDisplayCreated) {
    286                 destroyVirtualDisplays();
    287                 mVirtualDisplayCreated = false;
    288             }
    289         }
    290 
    291         /**
    292          * Simulate new display.
    293          * <pre>
    294          * <code>mDensityDpi</code> provide custom density for the display.
    295          * </pre>
    296          * @return {@link ActivityDisplay} of newly created display.
    297          */
    298         private List<ActivityDisplay> simulateDisplay() throws Exception {
    299             final List<ActivityDisplay> originalDs = getDisplaysStates();
    300 
    301             // Create virtual display with custom density dpi.
    302             mOverlayDisplayDeviceSession.set("1024x768/" + mDensityDpi);
    303 
    304             return assertAndGetNewDisplays(1, originalDs);
    305         }
    306 
    307         /**
    308          * Create new virtual display.
    309          * <pre>
    310          * <code>mDensityDpi</code> provide custom density for the display.
    311          * <code>mLaunchInSplitScreen</code> start {@link VirtualDisplayActivity} to side from
    312          *     {@link LaunchingActivity} on primary display.
    313          * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
    314          *     showing an insecure keyguard.
    315          * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
    316          * <code>mPublicDisplay</code> make display public.
    317          * <code>mResizeDisplay</code> should resize display when surface size changes.
    318          * <code>LaunchActivity</code> should launch test activity immediately after display
    319          *     creation.
    320          * </pre>
    321          * @param displayCount number of displays to be created.
    322          * @return A list of {@link ActivityDisplay} that represent newly created displays.
    323          * @throws Exception
    324          */
    325         private List<ActivityDisplay> createVirtualDisplays(int displayCount) {
    326             // Start an activity that is able to create virtual displays.
    327             if (mLaunchInSplitScreen) {
    328                 getLaunchActivityBuilder()
    329                         .setToSide(true)
    330                         .setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
    331                         .execute();
    332             } else {
    333                 launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
    334             }
    335             mAmWmState.computeState(false /* compareTaskAndStackBounds */,
    336                     new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
    337             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
    338             mAmWmState.assertFocusedActivity("Focus must be on virtual display host activity",
    339                     VIRTUAL_DISPLAY_ACTIVITY);
    340             final List<ActivityDisplay> originalDS = getDisplaysStates();
    341 
    342             // Create virtual display with custom density dpi.
    343             final StringBuilder createVirtualDisplayCommand = new StringBuilder(
    344                     getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
    345                     .append(" -f 0x20000000")
    346                     .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
    347             if (mDensityDpi != INVALID_DENSITY_DPI) {
    348                 createVirtualDisplayCommand
    349                         .append(" --ei " + KEY_DENSITY_DPI + " ")
    350                         .append(mDensityDpi);
    351             }
    352             createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
    353                     .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
    354                     .append(mCanShowWithInsecureKeyguard)
    355                     .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
    356                     .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay);
    357             if (mLaunchActivity != null) {
    358                 createVirtualDisplayCommand
    359                         .append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
    360                         .append(getActivityName(mLaunchActivity));
    361             }
    362             executeShellCommand(createVirtualDisplayCommand.toString());
    363             mVirtualDisplayCreated = true;
    364 
    365             return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
    366         }
    367 
    368         /**
    369          * Destroy existing virtual display.
    370          */
    371         void destroyVirtualDisplays() {
    372             final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
    373                     + " -f 0x20000000"
    374                     + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
    375             executeShellCommand(destroyVirtualDisplayCommand);
    376             waitForDisplaysDestroyed();
    377         }
    378 
    379         private void waitForDisplaysDestroyed() {
    380             for (int retry = 1; retry <= 5; retry++) {
    381                 if (!isHostedVirtualDisplayPresent()) {
    382                     return;
    383                 }
    384                 logAlways("Waiting for hosted displays destruction... retry=" + retry);
    385                 SystemClock.sleep(500);
    386             }
    387             fail("Waiting for hosted displays destruction failed.");
    388         }
    389 
    390         private boolean isHostedVirtualDisplayPresent() {
    391             mAmWmState.computeState(true);
    392             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
    393                     d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
    394         }
    395 
    396         /**
    397          * Wait for desired number of displays to be created and get their properties.
    398          * @param newDisplayCount expected display count, -1 if display should not be created.
    399          * @param originalDS display states before creation of new display(s).
    400          * @return list of new displays, empty list if no new display is created.
    401          */
    402         private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
    403                 List<ActivityDisplay> originalDS) {
    404             final int originalDisplayCount = originalDS.size();
    405 
    406             // Wait for the display(s) to be created and get configurations.
    407             final List<ActivityDisplay> ds = getDisplayStateAfterChange(
    408                     originalDisplayCount + newDisplayCount);
    409             if (newDisplayCount != -1) {
    410                 assertEquals("New virtual display(s) must be created",
    411                         originalDisplayCount + newDisplayCount, ds.size());
    412             } else {
    413                 assertEquals("New virtual display must not be created",
    414                         originalDisplayCount, ds.size());
    415                 return Collections.emptyList();
    416             }
    417 
    418             // Find the newly added display(s).
    419             final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
    420             assertThat("New virtual display must be created",
    421                     newDisplays, hasSize(newDisplayCount));
    422 
    423             return newDisplays;
    424         }
    425     }
    426 
    427     /** Helper class to save, set, and restore overlay_display_devices preference. */
    428     private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
    429         OverlayDisplayDevicesSession() {
    430             super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
    431                     Settings.Global::getString,
    432                     Settings.Global::putString);
    433         }
    434     }
    435 
    436     /** Wait for provided number of displays and report their configurations. */
    437     List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
    438         List<ActivityDisplay> ds = getDisplaysStates();
    439 
    440         int retriesLeft = 5;
    441         while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
    442             log("***Waiting for the correct number of displays...");
    443             try {
    444                 Thread.sleep(1000);
    445             } catch (InterruptedException e) {
    446                 log(e.toString());
    447             }
    448             ds = getDisplaysStates();
    449         }
    450 
    451         return ds;
    452     }
    453 
    454     private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
    455         if (displays.size() != expectedDisplayCount) {
    456             return false;
    457         }
    458         for (ActivityDisplay display : displays) {
    459             if (display.mOverrideConfiguration.densityDpi == 0) {
    460                 return false;
    461             }
    462         }
    463         return true;
    464     }
    465 
    466     /** Checks if the device supports multi-display. */
    467     boolean supportsMultiDisplay() {
    468         return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
    469     }
    470 }
    471