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 setUp()95 public void setUp() throws Exception { 96 super.setUp(); 97 mTargetContext = getInstrumentation().getTargetContext(); 98 } 99 getDisplayState(int displayId)100 ActivityDisplay getDisplayState(int displayId) { 101 return getDisplayState(getDisplaysStates(), displayId); 102 } 103 getDisplayState(List<ActivityDisplay> displays, int displayId)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. */ getDisplayState(List<ActivityDisplay> displays, int width, int height, int dpi)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 getDisplaysStates()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 */ findNewDisplayStates(List<ActivityDisplay> oldDisplays, List<ActivityDisplay> 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. */ getDisplayMetrics(int displayId)170 public static ReportedDisplayMetrics getDisplayMetrics(int displayId) { 171 return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId) 172 + executeShellCommand(WM_DENSITY + " -d " + displayId)); 173 } 174 setDisplayMetrics(final Size size, final int density)175 void setDisplayMetrics(final Size size, final int density) { 176 setSize(size); 177 setDensity(density); 178 } 179 restoreDisplayMetrics()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 setSize(final Size size)193 private void setSize(final Size size) { 194 executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight()); 195 } 196 setDensity(final int density)197 private void setDensity(final int density) { 198 executeShellCommand(WM_DENSITY + " " + density); 199 } 200 201 /** Get display size that WM operates with. */ getSize()202 public Size getSize() { 203 return overrideSize != null ? overrideSize : physicalSize; 204 } 205 206 /** Get density that WM operates with. */ getDensity()207 int getDensity() { 208 return overrideDensity != null ? overrideDensity : physicalDensity; 209 } 210 ReportedDisplayMetrics(final String lines)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 tapOnDisplayCenter(int displayId)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 setDensityDpi(int densityDpi)272 VirtualDisplaySession setDensityDpi(int densityDpi) { 273 mDensityDpi = densityDpi; 274 return this; 275 } 276 setLaunchInSplitScreen(boolean launchInSplitScreen)277 VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) { 278 mLaunchInSplitScreen = launchInSplitScreen; 279 return this; 280 } 281 setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)282 VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) { 283 mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard; 284 return this; 285 } 286 setPublicDisplay(boolean publicDisplay)287 VirtualDisplaySession setPublicDisplay(boolean publicDisplay) { 288 mPublicDisplay = publicDisplay; 289 return this; 290 } 291 setResizeDisplay(boolean resizeDisplay)292 VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) { 293 mResizeDisplay = resizeDisplay; 294 return this; 295 } 296 setShowSystemDecorations(boolean showSystemDecorations)297 VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) { 298 mShowSystemDecorations = showSystemDecorations; 299 return this; 300 } 301 setPresentationDisplay(boolean presentationDisplay)302 VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) { 303 mPresentationDisplay = presentationDisplay; 304 return this; 305 } 306 setLaunchActivity(ComponentName launchActivity)307 VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) { 308 mLaunchActivity = launchActivity; 309 return this; 310 } 311 setSimulateDisplay(boolean simulateDisplay)312 public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) { 313 mSimulateDisplay = simulateDisplay; 314 return this; 315 } 316 setSimulationDisplaySize(int width, int height)317 VirtualDisplaySession setSimulationDisplaySize(int width, int height) { 318 mSimulationDisplaySize = new Size(width, height); 319 return this; 320 } 321 setMustBeCreated(boolean mustBeCreated)322 VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) { 323 mMustBeCreated = mustBeCreated; 324 return this; 325 } 326 327 @Nullable createDisplay()328 public ActivityDisplay createDisplay() throws Exception { 329 return createDisplays(1).stream().findFirst().orElse(null); 330 } 331 332 @NonNull createDisplays(int count)333 List<ActivityDisplay> createDisplays(int count) throws Exception { 334 if (mSimulateDisplay) { 335 return simulateDisplay(); 336 } else { 337 return createVirtualDisplays(count); 338 } 339 } 340 resizeDisplay()341 void resizeDisplay() { 342 executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) 343 + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY); 344 } 345 346 @Override close()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 */ simulateDisplay()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 */ createVirtualDisplays(int displayCount)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 */ destroyVirtualDisplays()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 waitForDisplaysDestroyed()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 isHostedVirtualDisplayPresent()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 */ assertAndGetNewDisplays(int newDisplayCount, List<ActivityDisplay> originalDS)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 launchActivityOnDisplay(ComponentName activityName, ActivityDisplay display)506 ActivitySession launchActivityOnDisplay(ComponentName activityName, 507 ActivityDisplay display) { 508 return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */, 509 true /* withShellPermission */, true /* waitForLaunch */); 510 } 511 launchActivityOnDisplay(ComponentName activityName, ActivityDisplay display, Consumer<Bundle> extrasConsumer, boolean withShellPermission, boolean waitForLaunch)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 launchActivity(Consumer<LaunchActivityBuilder> setupBuilder)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 close()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 OverlayDisplayDevicesSession(Context context)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 addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors, boolean requestShowIme)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 restoreDisplayStates()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 close()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 OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme)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. */ getDisplayStateAfterChange(int expectedDisplayCount)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 areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount)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. */ supportsMultiDisplay()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 setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)657 ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) { 658 mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard; 659 return this; 660 } 661 setPublicDisplay(boolean publicDisplay)662 ExternalDisplaySession setPublicDisplay(boolean publicDisplay) { 663 mPublicDisplay = publicDisplay; 664 return this; 665 } 666 setShowSystemDecorations(boolean showSystemDecorations)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 */ createVirtualDisplay()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 turnDisplayOff()697 void turnDisplayOff() { 698 if (mExternalDisplayHelper == null) { 699 throw new RuntimeException("No external display created"); 700 } 701 mExternalDisplayHelper.turnDisplayOff(); 702 } 703 turnDisplayOn()704 void turnDisplayOn() { 705 if (mExternalDisplayHelper == null) { 706 throw new RuntimeException("No external display created"); 707 } 708 mExternalDisplayHelper.turnDisplayOn(); 709 } 710 711 @Override close()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 waitForHostedDisplayDestroyed()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 isHostedVirtualDisplayPresent()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 turnScreenOff()742 void turnScreenOff() { 743 setPrimaryDisplayState(false); 744 } 745 746 @Override close()747 public void close() throws Exception { 748 setPrimaryDisplayState(true); 749 } 750 751 /** Turns the primary display on/off by pressing the power key */ setPrimaryDisplayState(boolean wantOn)752 private void setPrimaryDisplayState(boolean wantOn) { 753 if (wantOn) { 754 pressWakeupButton(); 755 } else { 756 pressSleepButton(); 757 } 758 VirtualDisplayHelper.waitForDefaultDisplayState(wantOn); 759 } 760 } 761 } 762