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