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