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