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.ActivityInfo.CONFIG_SCREEN_SIZE; 20 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; 21 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 22 import static android.provider.Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS; 23 import static android.server.wm.StateLogger.log; 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_PRESENTATION_DISPLAY; 35 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY; 36 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY; 37 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS; 38 import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX; 39 import static android.view.Display.DEFAULT_DISPLAY; 40 import static android.view.Display.INVALID_DISPLAY; 41 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; 42 43 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 44 45 import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents; 46 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent; 47 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent; 48 49 import static org.hamcrest.MatcherAssert.assertThat; 50 import static org.hamcrest.Matchers.hasSize; 51 import static org.junit.Assert.assertEquals; 52 import static org.junit.Assert.assertNotEquals; 53 import static org.junit.Assert.assertTrue; 54 55 import android.content.ComponentName; 56 import android.content.Context; 57 import android.content.pm.PackageManager; 58 import android.content.res.Configuration; 59 import android.inputmethodservice.InputMethodService; 60 import android.os.Bundle; 61 import android.provider.Settings; 62 import android.server.wm.CommandSession.ActivitySession; 63 import android.server.wm.CommandSession.ActivitySessionClient; 64 import android.server.wm.WindowManagerState.DisplayContent; 65 import android.server.wm.settings.SettingsSession; 66 import android.util.Pair; 67 import android.util.Size; 68 import android.view.WindowManager; 69 70 import androidx.annotation.NonNull; 71 import androidx.annotation.Nullable; 72 73 import com.android.compatibility.common.util.SystemUtil; 74 import com.android.cts.mockime.ImeEvent; 75 import com.android.cts.mockime.ImeEventStream; 76 import com.android.cts.mockime.ImeEventStreamTestUtils; 77 78 import org.junit.AfterClass; 79 import org.junit.Before; 80 import org.junit.BeforeClass; 81 82 import java.util.ArrayList; 83 import java.util.Collections; 84 import java.util.List; 85 import java.util.Objects; 86 import java.util.concurrent.TimeUnit; 87 import java.util.function.Consumer; 88 import java.util.function.Predicate; 89 import java.util.regex.Matcher; 90 import java.util.regex.Pattern; 91 92 /** 93 * Base class for ActivityManager display tests. 94 * 95 * @see DisplayTests 96 * @see MultiDisplayKeyguardTests 97 * @see MultiDisplayLockedKeyguardTests 98 * @see AppConfigurationTests 99 */ 100 public class MultiDisplayTestBase extends ActivityManagerTestBase { 101 102 private static SettingsSession<String> sImmersiveModeConfirmationSetting; 103 104 static final int CUSTOM_DENSITY_DPI = 222; 105 private static final int INVALID_DENSITY_DPI = -1; 106 protected Context mTargetContext; 107 108 @BeforeClass setUpClass()109 public static void setUpClass() { 110 sImmersiveModeConfirmationSetting = new SettingsSession<>( 111 Settings.Secure.getUriFor(IMMERSIVE_MODE_CONFIRMATIONS), 112 Settings.Secure::getString, Settings.Secure::putString); 113 sImmersiveModeConfirmationSetting.set("confirmed"); 114 } 115 116 @AfterClass tearDownClass()117 public static void tearDownClass() { 118 if (sImmersiveModeConfirmationSetting != null) { 119 sImmersiveModeConfirmationSetting.close(); 120 } 121 } 122 123 @Before 124 @Override setUp()125 public void setUp() throws Exception { 126 super.setUp(); 127 mTargetContext = getInstrumentation().getTargetContext(); 128 } 129 getDisplayState(int displayId)130 DisplayContent getDisplayState(int displayId) { 131 return getDisplayState(getDisplaysStates(), displayId); 132 } 133 getDisplayState(List<DisplayContent> displays, int displayId)134 DisplayContent getDisplayState(List<DisplayContent> displays, int displayId) { 135 for (DisplayContent display : displays) { 136 if (display.mId == displayId) { 137 return display; 138 } 139 } 140 return null; 141 } 142 143 /** Return the display state with width, height, dpi. Always not default display. */ getDisplayState(List<DisplayContent> displays, int width, int height, int dpi)144 DisplayContent getDisplayState(List<DisplayContent> displays, int width, int height, 145 int dpi) { 146 for (DisplayContent display : displays) { 147 if (display.mId == DEFAULT_DISPLAY) { 148 continue; 149 } 150 final Configuration config = display.mFullConfiguration; 151 if (config.densityDpi == dpi && config.screenWidthDp == width 152 && config.screenHeightDp == height) { 153 return display; 154 } 155 } 156 return null; 157 } 158 getDisplaysStates()159 List<DisplayContent> getDisplaysStates() { 160 mWmState.computeState(); 161 return mWmState.getDisplays(); 162 } 163 164 /** Find the display that was not originally reported in oldDisplays and added in newDisplays */ findNewDisplayStates(List<DisplayContent> oldDisplays, List<DisplayContent> newDisplays)165 List<DisplayContent> findNewDisplayStates(List<DisplayContent> oldDisplays, 166 List<DisplayContent> newDisplays) { 167 final ArrayList<DisplayContent> result = new ArrayList<>(); 168 169 for (DisplayContent newDisplay : newDisplays) { 170 if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) { 171 result.add(newDisplay); 172 } 173 } 174 175 return result; 176 } 177 178 public static class ReportedDisplayMetrics { 179 private static final String WM_SIZE = "wm size"; 180 private static final String WM_DENSITY = "wm density"; 181 private static final Pattern PHYSICAL_SIZE = 182 Pattern.compile("Physical size: (\\d+)x(\\d+)"); 183 private static final Pattern OVERRIDE_SIZE = 184 Pattern.compile("Override size: (\\d+)x(\\d+)"); 185 private static final Pattern PHYSICAL_DENSITY = 186 Pattern.compile("Physical density: (\\d+)"); 187 private static final Pattern OVERRIDE_DENSITY = 188 Pattern.compile("Override density: (\\d+)"); 189 190 /** The size of the physical display. */ 191 @NonNull 192 final Size physicalSize; 193 /** The density of the physical display. */ 194 final int physicalDensity; 195 196 /** The pre-existing size override applied to a logical display. */ 197 @Nullable 198 final Size overrideSize; 199 /** The pre-existing density override applied to a logical display. */ 200 @Nullable 201 final Integer overrideDensity; 202 203 final int mDisplayId; 204 205 /** Get physical and override display metrics from WM for specified display. */ getDisplayMetrics(int displayId)206 public static ReportedDisplayMetrics getDisplayMetrics(int displayId) { 207 return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId) 208 + executeShellCommand(WM_DENSITY + " -d " + displayId), displayId); 209 } 210 setDisplayMetrics(final Size size, final int density)211 void setDisplayMetrics(final Size size, final int density) { 212 setSize(size); 213 setDensity(density); 214 } 215 restoreDisplayMetrics()216 void restoreDisplayMetrics() { 217 if (overrideSize != null) { 218 setSize(overrideSize); 219 } else { 220 executeShellCommand(WM_SIZE + " reset -d " + mDisplayId); 221 } 222 if (overrideDensity != null) { 223 setDensity(overrideDensity); 224 } else { 225 executeShellCommand(WM_DENSITY + " reset -d " + mDisplayId); 226 } 227 } 228 setSize(final Size size)229 private void setSize(final Size size) { 230 executeShellCommand( 231 WM_SIZE + " " + size.getWidth() + "x" + size.getHeight() + " -d " + mDisplayId); 232 } 233 setDensity(final int density)234 private void setDensity(final int density) { 235 executeShellCommand(WM_DENSITY + " " + density + " -d " + mDisplayId); 236 } 237 238 /** Get display size that WM operates with. */ getSize()239 public Size getSize() { 240 return overrideSize != null ? overrideSize : physicalSize; 241 } 242 243 /** Get density that WM operates with. */ getDensity()244 int getDensity() { 245 return overrideDensity != null ? overrideDensity : physicalDensity; 246 } 247 ReportedDisplayMetrics(final String lines, int displayId)248 private ReportedDisplayMetrics(final String lines, int displayId) { 249 mDisplayId = displayId; 250 Matcher matcher = PHYSICAL_SIZE.matcher(lines); 251 assertTrue("Physical display size must be reported", matcher.find()); 252 log(matcher.group()); 253 physicalSize = new Size( 254 Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))); 255 256 matcher = PHYSICAL_DENSITY.matcher(lines); 257 assertTrue("Physical display density must be reported", matcher.find()); 258 log(matcher.group()); 259 physicalDensity = Integer.parseInt(matcher.group(1)); 260 261 matcher = OVERRIDE_SIZE.matcher(lines); 262 if (matcher.find()) { 263 log(matcher.group()); 264 overrideSize = new Size( 265 Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2))); 266 } else { 267 overrideSize = null; 268 } 269 270 matcher = OVERRIDE_DENSITY.matcher(lines); 271 if (matcher.find()) { 272 log(matcher.group()); 273 overrideDensity = Integer.parseInt(matcher.group(1)); 274 } else { 275 overrideDensity = null; 276 } 277 } 278 } 279 280 public static class DisplayMetricsSession implements AutoCloseable { 281 private final ReportedDisplayMetrics mInitialDisplayMetrics; 282 private final int mDisplayId; 283 DisplayMetricsSession(int displayId)284 DisplayMetricsSession(int displayId) { 285 mDisplayId = displayId; 286 mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId); 287 } 288 getInitialDisplayMetrics()289 ReportedDisplayMetrics getInitialDisplayMetrics() { 290 return mInitialDisplayMetrics; 291 } 292 getDisplayMetrics()293 ReportedDisplayMetrics getDisplayMetrics() { 294 return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId); 295 } 296 changeAspectRatio(double aspectRatio, int orientation)297 void changeAspectRatio(double aspectRatio, int orientation) { 298 final Size originalSize = mInitialDisplayMetrics.physicalSize; 299 final int smaller = Math.min(originalSize.getWidth(), originalSize.getHeight()); 300 final int larger = (int) (smaller * aspectRatio); 301 Size overrideSize; 302 if (orientation == ORIENTATION_LANDSCAPE) { 303 overrideSize = new Size(larger, smaller); 304 } 305 else { 306 overrideSize = new Size(smaller, larger); 307 } 308 overrideDisplayMetrics(overrideSize, mInitialDisplayMetrics.physicalDensity); 309 } 310 changeDisplayMetrics(double sizeRatio, double densityRatio)311 void changeDisplayMetrics(double sizeRatio, double densityRatio) { 312 // Given a display may already have an override applied before the test is begun, 313 // resize based upon the override. 314 final Size originalSize; 315 final int density; 316 if (mInitialDisplayMetrics.overrideSize != null) { 317 originalSize = mInitialDisplayMetrics.overrideSize; 318 } else { 319 originalSize = mInitialDisplayMetrics.physicalSize; 320 } 321 322 if (mInitialDisplayMetrics.overrideDensity != null) { 323 density = mInitialDisplayMetrics.overrideDensity; 324 } else { 325 density = mInitialDisplayMetrics.physicalDensity; 326 } 327 328 final Size overrideSize = new Size((int)(originalSize.getWidth() * sizeRatio), 329 (int)(originalSize.getHeight() * sizeRatio)); 330 final int overrideDensity = (int)(density * densityRatio); 331 overrideDisplayMetrics(overrideSize, overrideDensity); 332 } 333 overrideDisplayMetrics(final Size size, final int density)334 void overrideDisplayMetrics(final Size size, final int density) { 335 mInitialDisplayMetrics.setDisplayMetrics(size, density); 336 } 337 restoreDisplayMetrics()338 void restoreDisplayMetrics() { 339 mInitialDisplayMetrics.restoreDisplayMetrics(); 340 } 341 342 @Override close()343 public void close() { 344 restoreDisplayMetrics(); 345 } 346 } 347 348 /** @see ObjectTracker#manage(AutoCloseable) */ createManagedDisplayMetricsSession(int displayId)349 protected DisplayMetricsSession createManagedDisplayMetricsSession(int displayId) { 350 return mObjectTracker.manage(new DisplayMetricsSession(displayId)); 351 } 352 353 public static class IgnoreOrientationRequestSession implements AutoCloseable { 354 private static final String WM_SET_IGNORE_ORIENTATION_REQUEST = 355 "wm set-ignore-orientation-request "; 356 private static final String WM_GET_IGNORE_ORIENTATION_REQUEST = 357 "wm get-ignore-orientation-request"; 358 private static final Pattern IGNORE_ORIENTATION_REQUEST_PATTERN = 359 Pattern.compile("ignoreOrientationRequest (true|false) for displayId=\\d+"); 360 361 final int mDisplayId; 362 final boolean mInitialValue; 363 IgnoreOrientationRequestSession(int displayId, boolean value)364 IgnoreOrientationRequestSession(int displayId, boolean value) { 365 mDisplayId = displayId; 366 Matcher matcher = IGNORE_ORIENTATION_REQUEST_PATTERN.matcher( 367 executeShellCommand(WM_GET_IGNORE_ORIENTATION_REQUEST + " -d " + mDisplayId)); 368 assertTrue("get-ignore-orientation-request should match pattern", matcher.find()); 369 mInitialValue = Boolean.parseBoolean(matcher.group(1)); 370 371 executeShellCommand("wm set-ignore-orientation-request true -d " + mDisplayId); 372 executeShellCommand(WM_SET_IGNORE_ORIENTATION_REQUEST + value + " -d " + mDisplayId); 373 } 374 375 @Override close()376 public void close() { 377 executeShellCommand( 378 WM_SET_IGNORE_ORIENTATION_REQUEST + mInitialValue + " -d " + mDisplayId); 379 } 380 } 381 382 /** @see ObjectTracker#manage(AutoCloseable) */ createManagedIgnoreOrientationRequestSession( int displayId, boolean value)383 protected IgnoreOrientationRequestSession createManagedIgnoreOrientationRequestSession( 384 int displayId, boolean value) { 385 return mObjectTracker.manage(new IgnoreOrientationRequestSession(displayId, value)); 386 } 387 waitForDisplayGone(Predicate<DisplayContent> displayPredicate)388 void waitForDisplayGone(Predicate<DisplayContent> displayPredicate) { 389 waitForOrFail("displays to be removed", () -> { 390 mWmState.computeState(); 391 return mWmState.getDisplays().stream().noneMatch(displayPredicate); 392 }); 393 } 394 395 /** @see ObjectTracker#manage(AutoCloseable) */ createManagedVirtualDisplaySession()396 protected VirtualDisplaySession createManagedVirtualDisplaySession() { 397 return mObjectTracker.manage(new VirtualDisplaySession()); 398 } 399 400 /** 401 * This class should only be used when you need to test virtual display created by a 402 * non-privileged app. 403 * Or when you need to test on simulated display. 404 * 405 * If you need to test virtual display created by a privileged app, please use 406 * {@link ExternalDisplaySession} instead. 407 */ 408 public class VirtualDisplaySession implements AutoCloseable { 409 private int mDensityDpi = CUSTOM_DENSITY_DPI; 410 private boolean mLaunchInSplitScreen = false; 411 private boolean mCanShowWithInsecureKeyguard = false; 412 private boolean mPublicDisplay = false; 413 private boolean mResizeDisplay = true; 414 private boolean mShowSystemDecorations = false; 415 private boolean mOwnContentOnly = false; 416 private int mDisplayImePolicy = DISPLAY_IME_POLICY_FALLBACK_DISPLAY; 417 private boolean mPresentationDisplay = false; 418 private boolean mSimulateDisplay = false; 419 private boolean mMustBeCreated = true; 420 private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */); 421 422 private boolean mVirtualDisplayCreated = false; 423 private OverlayDisplayDevicesSession mOverlayDisplayDeviceSession; 424 setDensityDpi(int densityDpi)425 VirtualDisplaySession setDensityDpi(int densityDpi) { 426 mDensityDpi = densityDpi; 427 return this; 428 } 429 setLaunchInSplitScreen(boolean launchInSplitScreen)430 VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) { 431 mLaunchInSplitScreen = launchInSplitScreen; 432 return this; 433 } 434 setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)435 VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) { 436 mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard; 437 return this; 438 } 439 setPublicDisplay(boolean publicDisplay)440 VirtualDisplaySession setPublicDisplay(boolean publicDisplay) { 441 mPublicDisplay = publicDisplay; 442 return this; 443 } 444 setResizeDisplay(boolean resizeDisplay)445 VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) { 446 mResizeDisplay = resizeDisplay; 447 return this; 448 } 449 setShowSystemDecorations(boolean showSystemDecorations)450 VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) { 451 mShowSystemDecorations = showSystemDecorations; 452 return this; 453 } 454 setOwnContentOnly(boolean ownContentOnly)455 VirtualDisplaySession setOwnContentOnly(boolean ownContentOnly) { 456 mOwnContentOnly = ownContentOnly; 457 return this; 458 } 459 460 /** 461 * Sets the policy for how the display should show the ime. 462 * 463 * Set to one of: 464 * <ul> 465 * <li>{@link WindowManager#DISPLAY_IME_POLICY_LOCAL} 466 * <li>{@link WindowManager#DISPLAY_IME_POLICY_FALLBACK_DISPLAY} 467 * <li>{@link WindowManager#DISPLAY_IME_POLICY_HIDE} 468 * </ul> 469 */ setDisplayImePolicy(int displayImePolicy)470 VirtualDisplaySession setDisplayImePolicy(int displayImePolicy) { 471 mDisplayImePolicy = displayImePolicy; 472 return this; 473 } 474 setPresentationDisplay(boolean presentationDisplay)475 VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) { 476 mPresentationDisplay = presentationDisplay; 477 return this; 478 } 479 480 // TODO(b/154565343) move simulate display out of VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay)481 public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) { 482 mSimulateDisplay = simulateDisplay; 483 return this; 484 } 485 setSimulationDisplaySize(int width, int height)486 VirtualDisplaySession setSimulationDisplaySize(int width, int height) { 487 mSimulationDisplaySize = new Size(width, height); 488 return this; 489 } 490 491 @Nullable createDisplay(boolean mustBeCreated)492 public DisplayContent createDisplay(boolean mustBeCreated) { 493 mMustBeCreated = mustBeCreated; 494 final DisplayContent display = createDisplays(1).stream().findFirst().orElse(null); 495 if (mustBeCreated && display == null) { 496 throw new IllegalStateException("No display is created"); 497 } 498 return display; 499 } 500 501 @NonNull createDisplay()502 public DisplayContent createDisplay() { 503 return Objects.requireNonNull(createDisplay(true /* mustBeCreated */)); 504 } 505 506 @NonNull createDisplays(int count)507 List<DisplayContent> createDisplays(int count) { 508 if (mSimulateDisplay) { 509 return simulateDisplays(count); 510 } else { 511 return createVirtualDisplays(count); 512 } 513 } 514 resizeDisplay()515 void resizeDisplay() { 516 if (mSimulateDisplay) { 517 throw new IllegalStateException( 518 "Please use ReportedDisplayMetrics#setDisplayMetrics to resize" 519 + " simulate display"); 520 } 521 executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) 522 + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY); 523 } 524 525 @Override close()526 public void close() { 527 if (mOverlayDisplayDeviceSession != null) { 528 mOverlayDisplayDeviceSession.close(); 529 } 530 if (mVirtualDisplayCreated) { 531 destroyVirtualDisplays(); 532 mVirtualDisplayCreated = false; 533 } 534 } 535 536 /** 537 * Simulate new display. 538 * <pre> 539 * <code>mDensityDpi</code> provide custom density for the display. 540 * </pre> 541 * @return {@link DisplayContent} of newly created display. 542 */ simulateDisplays(int count)543 private List<DisplayContent> simulateDisplays(int count) { 544 mOverlayDisplayDeviceSession = new OverlayDisplayDevicesSession(mContext); 545 mOverlayDisplayDeviceSession.createDisplays(mSimulationDisplaySize, mDensityDpi, 546 mOwnContentOnly, mShowSystemDecorations, count); 547 mOverlayDisplayDeviceSession.configureDisplays(mDisplayImePolicy /* imePolicy */); 548 return mOverlayDisplayDeviceSession.getCreatedDisplays(); 549 } 550 551 /** 552 * Create new virtual display. 553 * <pre> 554 * <code>mDensityDpi</code> provide custom density for the display. 555 * <code>mLaunchInSplitScreen</code> start 556 * {@link android.server.wm.app.VirtualDisplayActivity} to side from 557 * {@link android.server.wm.app.LaunchingActivity} on primary display. 558 * <code>mCanShowWithInsecureKeyguard</code> allow showing content when device is 559 * showing an insecure keyguard. 560 * <code>mMustBeCreated</code> should assert if the display was or wasn't created. 561 * <code>mPublicDisplay</code> make display public. 562 * <code>mResizeDisplay</code> should resize display when surface size changes. 563 * <code>LaunchActivity</code> should launch test activity immediately after display 564 * creation. 565 * </pre> 566 * @param displayCount number of displays to be created. 567 * @return A list of {@link DisplayContent} that represent newly created displays. 568 * @throws Exception 569 */ createVirtualDisplays(int displayCount)570 private List<DisplayContent> createVirtualDisplays(int displayCount) { 571 // Start an activity that is able to create virtual displays. 572 if (mLaunchInSplitScreen) { 573 getLaunchActivityBuilder() 574 .setToSide(true) 575 .setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY) 576 .execute(); 577 final int secondaryTaskId = 578 mWmState.getTaskByActivity(VIRTUAL_DISPLAY_ACTIVITY).mTaskId; 579 mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId); 580 } else { 581 launchActivity(VIRTUAL_DISPLAY_ACTIVITY); 582 } 583 mWmState.computeState( 584 new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY)); 585 mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */); 586 mWmState.assertFocusedActivity("Focus must be on virtual display host activity", 587 VIRTUAL_DISPLAY_ACTIVITY); 588 final List<DisplayContent> originalDS = getDisplaysStates(); 589 590 // Create virtual display with custom density dpi. 591 final StringBuilder createVirtualDisplayCommand = new StringBuilder( 592 getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)) 593 .append(" -f 0x20000000") 594 .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY); 595 if (mDensityDpi != INVALID_DENSITY_DPI) { 596 createVirtualDisplayCommand 597 .append(" --ei " + KEY_DENSITY_DPI + " ") 598 .append(mDensityDpi); 599 } 600 createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount) 601 .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ") 602 .append(mCanShowWithInsecureKeyguard) 603 .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay) 604 .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay) 605 .append(" --ez " + KEY_SHOW_SYSTEM_DECORATIONS + " ") 606 .append(mShowSystemDecorations) 607 .append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay); 608 executeShellCommand(createVirtualDisplayCommand.toString()); 609 mVirtualDisplayCreated = true; 610 611 return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS); 612 } 613 614 /** 615 * Destroy existing virtual display. 616 */ destroyVirtualDisplays()617 void destroyVirtualDisplays() { 618 final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) 619 + " -f 0x20000000" 620 + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY; 621 executeShellCommand(destroyVirtualDisplayCommand); 622 waitForDisplayGone( 623 d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX)); 624 } 625 } 626 627 // TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated. 628 protected class VirtualDisplayLauncher extends VirtualDisplaySession { 629 private final ActivitySessionClient mActivitySessionClient = createActivitySessionClient(); 630 launchActivityOnDisplay(ComponentName activityName, DisplayContent display)631 ActivitySession launchActivityOnDisplay(ComponentName activityName, 632 DisplayContent display) { 633 return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */, 634 true /* withShellPermission */, true /* waitForLaunch */); 635 } 636 launchActivityOnDisplay(ComponentName activityName, DisplayContent display, Consumer<Bundle> extrasConsumer, boolean withShellPermission, boolean waitForLaunch)637 ActivitySession launchActivityOnDisplay(ComponentName activityName, 638 DisplayContent display, Consumer<Bundle> extrasConsumer, 639 boolean withShellPermission, boolean waitForLaunch) { 640 return launchActivity(builder -> builder 641 // VirtualDisplayActivity is in different package. If the display is not public, 642 // it requires shell permission to launch activity ({@see com.android.server.wm. 643 // ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay}). 644 .setWithShellPermission(withShellPermission) 645 .setWaitForLaunched(waitForLaunch) 646 .setIntentExtra(extrasConsumer) 647 .setTargetActivity(activityName) 648 .setDisplayId(display.mId)); 649 } 650 launchActivity(Consumer<LaunchActivityBuilder> setupBuilder)651 ActivitySession launchActivity(Consumer<LaunchActivityBuilder> setupBuilder) { 652 final LaunchActivityBuilder builder = getLaunchActivityBuilder() 653 .setUseInstrumentation(); 654 setupBuilder.accept(builder); 655 return mActivitySessionClient.startActivity(builder); 656 } 657 658 @Override close()659 public void close() { 660 super.close(); 661 mActivitySessionClient.close(); 662 } 663 } 664 665 /** Helper class to save, set, and restore overlay_display_devices preference. */ 666 private class OverlayDisplayDevicesSession extends SettingsSession<String> { 667 /** See display_manager_overlay_display_name. */ 668 private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #"; 669 670 /** See {@link OverlayDisplayAdapter#OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY}. */ 671 private static final String OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY = ",own_content_only"; 672 673 /** 674 * See {@link OverlayDisplayAdapter#OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. 675 */ 676 private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 677 ",should_show_system_decorations"; 678 679 /** The displays which are created by this session. */ 680 private final List<DisplayContent> mDisplays = new ArrayList<>(); 681 /** The configured displays that need to be restored when this session is closed. */ 682 private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>(); 683 private final WindowManager mWm; 684 OverlayDisplayDevicesSession(Context context)685 OverlayDisplayDevicesSession(Context context) { 686 super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES), 687 Settings.Global::getString, 688 Settings.Global::putString); 689 // Remove existing overlay display to avoid display count problem. 690 removeExisting(); 691 mWm = context.getSystemService(WindowManager.class); 692 } 693 getCreatedDisplays()694 List<DisplayContent> getCreatedDisplays() { 695 return new ArrayList<>(mDisplays); 696 } 697 698 @Override set(String value)699 public void set(String value) { 700 final List<DisplayContent> originalDisplays = getDisplaysStates(); 701 super.set(value); 702 final int newDisplayCount = 1 + (int) value.chars().filter(ch -> ch == ';').count(); 703 mDisplays.addAll(assertAndGetNewDisplays(newDisplayCount, originalDisplays)); 704 } 705 706 /** Creates overlay display with custom density dpi, specified size, and test flags. */ createDisplays(Size displaySize, int densityDpi, boolean ownContentOnly, boolean shouldShowSystemDecorations, int count)707 void createDisplays(Size displaySize, int densityDpi, boolean ownContentOnly, 708 boolean shouldShowSystemDecorations, int count) { 709 final StringBuilder builder = new StringBuilder(); 710 for (int i = 0; i < count; i++) { 711 String displaySettingsEntry = displaySize + "/" + densityDpi; 712 if (ownContentOnly) { 713 displaySettingsEntry += OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY; 714 } 715 if (shouldShowSystemDecorations) { 716 displaySettingsEntry += OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 717 } 718 builder.append(displaySettingsEntry); 719 // Creating n displays needs (n - 1) ';'. 720 if (i < count - 1) { 721 builder.append(';'); 722 } 723 } 724 set(builder.toString()); 725 } 726 configureDisplays(int imePolicy)727 void configureDisplays(int imePolicy) { 728 SystemUtil.runWithShellPermissionIdentity(() -> { 729 for (DisplayContent display : mDisplays) { 730 final int oldImePolicy = mWm.getDisplayImePolicy(display.mId); 731 mDisplayStates.add(new OverlayDisplayState(display.mId, oldImePolicy)); 732 if (imePolicy != oldImePolicy) { 733 mWm.setDisplayImePolicy(display.mId, imePolicy); 734 waitForOrFail("display config show-IME to be set", 735 () -> (mWm.getDisplayImePolicy(display.mId) == imePolicy)); 736 } 737 } 738 }); 739 } 740 restoreDisplayStates()741 private void restoreDisplayStates() { 742 mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> { 743 mWm.setDisplayImePolicy(state.mId, state.mImePolicy); 744 745 // Only need to wait the last flag to be set. 746 waitForOrFail("display config show-IME to be restored", 747 () -> (mWm.getDisplayImePolicy(state.mId) == state.mImePolicy)); 748 })); 749 } 750 751 @Override close()752 public void close() { 753 // Need to restore display state before display is destroyed. 754 restoreDisplayStates(); 755 super.close(); 756 // Waiting for restoring to the state before this session was created. 757 waitForDisplayGone(display -> mDisplays.stream() 758 .anyMatch(createdDisplay -> createdDisplay.mId == display.mId)); 759 } 760 removeExisting()761 private void removeExisting() { 762 if (!mHasInitialValue || mInitialValue == null) { 763 // No existing overlay displays. 764 return; 765 } 766 delete(mUri); 767 // Make sure all overlay displays are completely removed. 768 waitForDisplayGone( 769 display -> display.getName().startsWith(OVERLAY_DISPLAY_NAME_PREFIX)); 770 } 771 772 private class OverlayDisplayState { 773 int mId; 774 int mImePolicy; 775 OverlayDisplayState(int displayId, int imePolicy)776 OverlayDisplayState(int displayId, int imePolicy) { 777 mId = displayId; 778 mImePolicy = imePolicy; 779 } 780 } 781 } 782 783 /** Wait for provided number of displays and report their configurations. */ getDisplayStateAfterChange(int expectedDisplayCount)784 List<DisplayContent> getDisplayStateAfterChange(int expectedDisplayCount) { 785 return Condition.waitForResult("the correct number of displays=" + expectedDisplayCount, 786 condition -> condition 787 .setReturnLastResult(true) 788 .setResultSupplier(this::getDisplaysStates) 789 .setResultValidator( 790 displays -> areDisplaysValid(displays, expectedDisplayCount))); 791 } 792 areDisplaysValid(List<DisplayContent> displays, int expectedDisplayCount)793 private boolean areDisplaysValid(List<DisplayContent> displays, int expectedDisplayCount) { 794 if (displays.size() != expectedDisplayCount) { 795 return false; 796 } 797 for (DisplayContent display : displays) { 798 if (display.mOverrideConfiguration.densityDpi == 0) { 799 return false; 800 } 801 } 802 return true; 803 } 804 805 /** 806 * Wait for desired number of displays to be created and get their properties. 807 * 808 * @param newDisplayCount expected display count, -1 if display should not be created. 809 * @param originalDisplays display states before creation of new display(s). 810 * @return list of new displays, empty list if no new display is created. 811 */ assertAndGetNewDisplays(int newDisplayCount, List<DisplayContent> originalDisplays)812 private List<DisplayContent> assertAndGetNewDisplays(int newDisplayCount, 813 List<DisplayContent> originalDisplays) { 814 final int originalDisplayCount = originalDisplays.size(); 815 816 // Wait for the display(s) to be created and get configurations. 817 final List<DisplayContent> ds = getDisplayStateAfterChange( 818 originalDisplayCount + newDisplayCount); 819 if (newDisplayCount != -1) { 820 assertEquals("New virtual display(s) must be created", 821 originalDisplayCount + newDisplayCount, ds.size()); 822 } else { 823 assertEquals("New virtual display must not be created", 824 originalDisplayCount, ds.size()); 825 return Collections.emptyList(); 826 } 827 828 // Find the newly added display(s). 829 final List<DisplayContent> newDisplays = findNewDisplayStates(originalDisplays, ds); 830 assertThat("New virtual display must be created", newDisplays, hasSize(newDisplayCount)); 831 832 return newDisplays; 833 } 834 835 /** A clearer alias of {@link Pair#create(Object, Object)}. */ pair(K k, V v)836 protected <K, V> Pair<K, V> pair(K k, V v) { 837 return new Pair<>(k, v); 838 } 839 assertBothDisplaysHaveResumedActivities( Pair<Integer, ComponentName> firstPair, Pair<Integer, ComponentName> secondPair)840 protected void assertBothDisplaysHaveResumedActivities( 841 Pair<Integer, ComponentName> firstPair, Pair<Integer, ComponentName> secondPair) { 842 assertNotEquals("Displays must be different. First display id: " 843 + firstPair.first, firstPair.first, secondPair.first); 844 mWmState.assertResumedActivities("Both displays must have resumed activities", 845 mapping -> { 846 mapping.put(firstPair.first, firstPair.second); 847 mapping.put(secondPair.first, secondPair.second); 848 }); 849 } 850 851 /** Checks if the device supports multi-display. */ supportsMultiDisplay()852 protected boolean supportsMultiDisplay() { 853 return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS); 854 } 855 856 /** Checks if the device supports wallpaper for multi-display. */ supportsLiveWallpaper()857 protected boolean supportsLiveWallpaper() { 858 return hasDeviceFeature(PackageManager.FEATURE_LIVE_WALLPAPER); 859 } 860 861 /** @see ObjectTracker#manage(AutoCloseable) */ createManagedExternalDisplaySession()862 protected ExternalDisplaySession createManagedExternalDisplaySession() { 863 return mObjectTracker.manage(new ExternalDisplaySession()); 864 } 865 866 @SafeVarargs waitOrderedImeEventsThenAssertImeShown(ImeEventStream stream, int displayId, Predicate<ImeEvent>... conditions)867 final void waitOrderedImeEventsThenAssertImeShown(ImeEventStream stream, int displayId, 868 Predicate<ImeEvent>... conditions) throws Exception { 869 for (Predicate<ImeEvent> condition : conditions) { 870 expectEvent(stream, condition, TimeUnit.SECONDS.toMillis(5) /* eventTimeout */); 871 } 872 // Assert the IME is shown on the expected display. 873 mWmState.waitAndAssertImeWindowShownOnDisplay(displayId); 874 } 875 waitAndAssertImeNoScreenSizeChanged(ImeEventStream stream)876 protected void waitAndAssertImeNoScreenSizeChanged(ImeEventStream stream) { 877 notExpectEvent(stream, event -> "onConfigurationChanged".equals(event.getEventName()) 878 && (event.getArguments().getInt("ConfigUpdates") & CONFIG_SCREEN_SIZE) != 0, 879 TimeUnit.SECONDS.toMillis(1) /* eventTimeout */); 880 } 881 882 /** 883 * Clears all {@link InputMethodService#onConfigurationChanged(Configuration)} events from the 884 * given {@code stream} and returns a forked {@link ImeEventStream}. 885 * 886 * @see ImeEventStreamTestUtils#clearAllEvents(ImeEventStream, String) 887 */ clearOnConfigurationChangedFromStream(ImeEventStream stream)888 protected ImeEventStream clearOnConfigurationChangedFromStream(ImeEventStream stream) { 889 return clearAllEvents(stream, "onConfigurationChanged"); 890 } 891 892 /** 893 * This class is used when you need to test virtual display created by a privileged app. 894 * 895 * If you need to test virtual display created by a non-privileged app or when you need to test 896 * on simulated display, please use {@link VirtualDisplaySession} instead. 897 */ 898 public class ExternalDisplaySession implements AutoCloseable { 899 900 private boolean mCanShowWithInsecureKeyguard = false; 901 private boolean mPublicDisplay = false; 902 private boolean mShowSystemDecorations = false; 903 904 private int mDisplayId = INVALID_DISPLAY; 905 906 @Nullable 907 private VirtualDisplayHelper mExternalDisplayHelper; 908 setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)909 ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) { 910 mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard; 911 return this; 912 } 913 setPublicDisplay(boolean publicDisplay)914 ExternalDisplaySession setPublicDisplay(boolean publicDisplay) { 915 mPublicDisplay = publicDisplay; 916 return this; 917 } 918 919 /** 920 * @deprecated untrusted virtual display won't have system decorations even it has the flag. 921 * Only use this method to verify that. To test secondary display with system decorations, 922 * please use simulated display. 923 */ 924 @Deprecated setShowSystemDecorations(boolean showSystemDecorations)925 ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) { 926 mShowSystemDecorations = showSystemDecorations; 927 return this; 928 } 929 930 /** 931 * Creates a private virtual display with insecure keyguard flags set. 932 */ createVirtualDisplay()933 DisplayContent createVirtualDisplay() { 934 final List<DisplayContent> originalDS = getDisplaysStates(); 935 final int originalDisplayCount = originalDS.size(); 936 937 mExternalDisplayHelper = new VirtualDisplayHelper(); 938 mExternalDisplayHelper 939 .setPublicDisplay(mPublicDisplay) 940 .setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard) 941 .setShowSystemDecorations(mShowSystemDecorations) 942 .createAndWaitForDisplay(); 943 944 // Wait for the virtual display to be created and get configurations. 945 final List<DisplayContent> ds = getDisplayStateAfterChange(originalDisplayCount + 1); 946 assertEquals("New virtual display must be created", originalDisplayCount + 1, 947 ds.size()); 948 949 // Find the newly added display. 950 final DisplayContent newDisplay = findNewDisplayStates(originalDS, ds).get(0); 951 mDisplayId = newDisplay.mId; 952 return newDisplay; 953 } 954 turnDisplayOff()955 void turnDisplayOff() { 956 if (mExternalDisplayHelper == null) { 957 throw new RuntimeException("No external display created"); 958 } 959 mExternalDisplayHelper.turnDisplayOff(); 960 } 961 turnDisplayOn()962 void turnDisplayOn() { 963 if (mExternalDisplayHelper == null) { 964 throw new RuntimeException("No external display created"); 965 } 966 mExternalDisplayHelper.turnDisplayOn(); 967 } 968 969 @Override close()970 public void close() { 971 if (mExternalDisplayHelper != null) { 972 mExternalDisplayHelper.releaseDisplay(); 973 mExternalDisplayHelper = null; 974 975 waitForDisplayGone(d -> d.mId == mDisplayId); 976 mDisplayId = INVALID_DISPLAY; 977 } 978 } 979 } 980 981 public static class PrimaryDisplayStateSession implements AutoCloseable { 982 turnScreenOff()983 void turnScreenOff() { 984 setPrimaryDisplayState(false); 985 } 986 987 @Override close()988 public void close() { 989 setPrimaryDisplayState(true); 990 } 991 992 /** Turns the primary display on/off by pressing the power key */ setPrimaryDisplayState(boolean wantOn)993 private void setPrimaryDisplayState(boolean wantOn) { 994 if (wantOn) { 995 pressWakeupButton(); 996 } else { 997 pressSleepButton(); 998 } 999 VirtualDisplayHelper.waitForDefaultDisplayState(wantOn); 1000 } 1001 } 1002 } 1003