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