• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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