• 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.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