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 getDisplayState(List<ActivityDisplay> displays, int displayId)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. */ getDisplayState(List<ActivityDisplay> displays, int width, int height, int dpi)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 getDisplaysStates()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 */ findNewDisplayStates(List<ActivityDisplay> oldDisplays, List<ActivityDisplay> 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. */ getDisplayMetrics()137 static ReportedDisplayMetrics getDisplayMetrics() { 138 return new ReportedDisplayMetrics( 139 executeShellCommand(WM_SIZE) + executeShellCommand(WM_DENSITY)); 140 } 141 setDisplayMetrics(final Size size, final int density)142 void setDisplayMetrics(final Size size, final int density) { 143 setSize(size); 144 setDensity(density); 145 } 146 restoreDisplayMetrics()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 setSize(final Size size)160 private void setSize(final Size size) { 161 executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight()); 162 } 163 setDensity(final int density)164 private void setDensity(final int density) { 165 executeShellCommand(WM_DENSITY + " " + density); 166 } 167 168 /** Get display size that WM operates with. */ getSize()169 Size getSize() { 170 return overrideSize != null ? overrideSize : physicalSize; 171 } 172 173 /** Get density that WM operates with. */ getDensity()174 int getDensity() { 175 return overrideDensity != null ? overrideDensity : physicalDensity; 176 } 177 ReportedDisplayMetrics(final String lines)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 setDensityDpi(int densityDpi)223 VirtualDisplaySession setDensityDpi(int densityDpi) { 224 mDensityDpi = densityDpi; 225 return this; 226 } 227 setLaunchInSplitScreen(boolean launchInSplitScreen)228 VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) { 229 mLaunchInSplitScreen = launchInSplitScreen; 230 return this; 231 } 232 setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)233 VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) { 234 mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard; 235 return this; 236 } 237 setPublicDisplay(boolean publicDisplay)238 VirtualDisplaySession setPublicDisplay(boolean publicDisplay) { 239 mPublicDisplay = publicDisplay; 240 return this; 241 } 242 setResizeDisplay(boolean resizeDisplay)243 VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) { 244 mResizeDisplay = resizeDisplay; 245 return this; 246 } 247 setLaunchActivity(ComponentName launchActivity)248 VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) { 249 mLaunchActivity = launchActivity; 250 return this; 251 } 252 setSimulateDisplay(boolean simulateDisplay)253 VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) { 254 mSimulateDisplay = simulateDisplay; 255 return this; 256 } 257 setMustBeCreated(boolean mustBeCreated)258 VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) { 259 mMustBeCreated = mustBeCreated; 260 return this; 261 } 262 263 @Nullable createDisplay()264 ActivityDisplay createDisplay() throws Exception { 265 return createDisplays(1).stream().findFirst().orElse(null); 266 } 267 268 @NonNull createDisplays(int count)269 List<ActivityDisplay> createDisplays(int count) throws Exception { 270 if (mSimulateDisplay) { 271 return simulateDisplay(); 272 } else { 273 return createVirtualDisplays(count); 274 } 275 } 276 resizeDisplay()277 void resizeDisplay() { 278 executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) 279 + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY); 280 } 281 282 @Override close()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 */ simulateDisplay()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 */ createVirtualDisplays(int displayCount)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 */ destroyVirtualDisplays()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 waitForDisplaysDestroyed()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 isHostedVirtualDisplayPresent()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 */ assertAndGetNewDisplays(int newDisplayCount, List<ActivityDisplay> originalDS)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> { OverlayDisplayDevicesSession()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. */ getDisplayStateAfterChange(int expectedDisplayCount)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 areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount)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. */ supportsMultiDisplay()467 boolean supportsMultiDisplay() { 468 return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS); 469 } 470 } 471