• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
22 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
23 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
25 
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
28 
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.anyBoolean;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.verify;
33 
34 import android.app.WindowConfiguration.WindowingMode;
35 import android.content.ComponentName;
36 import android.content.pm.ActivityInfo;
37 import android.content.pm.PackageManager.UserMinAspectRatio;
38 import android.content.res.Configuration;
39 import android.graphics.Rect;
40 import android.view.Surface;
41 
42 import androidx.annotation.CallSuper;
43 import androidx.annotation.NonNull;
44 import androidx.annotation.Nullable;
45 
46 import com.android.server.wm.utils.TestComponentStack;
47 
48 import org.junit.Assert;
49 
50 import java.util.function.Consumer;
51 import java.util.function.Function;
52 import java.util.function.Predicate;
53 
54 /**
55  * Robot implementation for {@link ActivityRecord}.
56  */
57 class AppCompatActivityRobot {
58 
59     private static final int DEFAULT_DISPLAY_WIDTH = 1000;
60     private static final int DEFAULT_DISPLAY_HEIGHT = 2000;
61 
62     private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.0001f;
63     private static final float COMPAT_SCALE_TOLERANCE = 0.0001f;
64 
65     private static final String TEST_COMPONENT_NAME = AppCompatActivityRobot.class.getName();
66 
67     @NonNull
68     private final ActivityTaskManagerService mAtm;
69     @NonNull
70     private final ActivityTaskSupervisor mSupervisor;
71     @NonNull
72     private final TestComponentStack<ActivityRecord> mActivityStack;
73     @NonNull
74     private final TestComponentStack<Task> mTaskStack;
75 
76     private final int mDisplayWidth;
77     private final int mDisplayHeight;
78     private DisplayContent mDisplayContent;
79 
80     @Nullable
81     private Consumer<ActivityRecord> mOnPostActivityCreation;
82 
83     @Nullable
84     private Consumer<DisplayContent> mOnPostDisplayContentCreation;
85 
AppCompatActivityRobot(@onNull WindowManagerService wm, @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor, int displayWidth, int displayHeight, @Nullable Consumer<ActivityRecord> onPostActivityCreation, @Nullable Consumer<DisplayContent> onPostDisplayContentCreation)86     AppCompatActivityRobot(@NonNull WindowManagerService wm,
87             @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor,
88             int displayWidth, int displayHeight,
89             @Nullable Consumer<ActivityRecord> onPostActivityCreation,
90             @Nullable Consumer<DisplayContent> onPostDisplayContentCreation) {
91         mAtm = atm;
92         mSupervisor = supervisor;
93         mDisplayWidth = displayWidth;
94         mDisplayHeight = displayHeight;
95         mActivityStack = new TestComponentStack<>();
96         mTaskStack = new TestComponentStack<>();
97         mOnPostActivityCreation = onPostActivityCreation;
98         mOnPostDisplayContentCreation = onPostDisplayContentCreation;
99         createNewDisplay();
100     }
101 
AppCompatActivityRobot(@onNull WindowManagerService wm, @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor, int displayWidth, int displayHeight)102     AppCompatActivityRobot(@NonNull WindowManagerService wm,
103             @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor,
104             int displayWidth, int displayHeight) {
105         this(wm, atm, supervisor, displayWidth, displayHeight, /* onPostActivityCreation */ null,
106                 /* onPostDisplayContentCreation */ null);
107     }
108 
AppCompatActivityRobot(@onNull WindowManagerService wm, @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor)109     AppCompatActivityRobot(@NonNull WindowManagerService wm,
110             @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor) {
111         this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
112     }
113 
createActivityWithComponent()114     void createActivityWithComponent() {
115         createActivityWithComponentInNewTask(/* inNewTask */ mTaskStack.isEmpty(),
116                 /* inNewDisplay */ false);
117     }
118 
createActivityWithComponentWithoutTask()119     void createActivityWithComponentWithoutTask() {
120         createActivityWithComponentInNewTask(/* inNewTask */ false, /* inNewDisplay */ false);
121     }
122 
createActivityWithComponentInNewTask()123     void createActivityWithComponentInNewTask() {
124         createActivityWithComponentInNewTask(/* inNewTask */ true, /* inNewDisplay */ false);
125     }
126 
createActivityWithComponentInNewTaskAndDisplay()127     void createActivityWithComponentInNewTaskAndDisplay() {
128         createActivityWithComponentInNewTask(/* inNewTask */ true, /* inNewDisplay */ true);
129     }
130 
configureTopActivity(float minAspect, float maxAspect, int screenOrientation, boolean isUnresizable)131     void configureTopActivity(float minAspect, float maxAspect, int screenOrientation,
132             boolean isUnresizable) {
133         prepareLimitedBounds(mActivityStack.top(), minAspect, maxAspect, screenOrientation,
134                 isUnresizable);
135     }
136 
configureUnresizableTopActivity(@ctivityInfo.ScreenOrientation int screenOrientation)137     void configureUnresizableTopActivity(@ActivityInfo.ScreenOrientation int screenOrientation) {
138         configureTopActivity(/* minAspect */ -1, /* maxAspect */ -1, screenOrientation,
139                 /* isUnresizable */ true);
140     }
141 
setDisplayNaturalOrientation(@onfiguration.Orientation int naturalOrientation)142     void setDisplayNaturalOrientation(@Configuration.Orientation int naturalOrientation) {
143         doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation();
144     }
145 
setDisplayIgnoreActivitySizeRestrictions(boolean enabled)146     void setDisplayIgnoreActivitySizeRestrictions(boolean enabled) {
147         doReturn(enabled).when(mDisplayContent).isDisplayIgnoreActivitySizeRestrictions();
148     }
149 
configureTaskBounds(@onNull Rect taskBounds)150     void configureTaskBounds(@NonNull Rect taskBounds) {
151         doReturn(taskBounds).when(mTaskStack.top()).getBounds();
152     }
153 
configureTaskAppBounds(@onNull Rect appBounds)154     void configureTaskAppBounds(@NonNull Rect appBounds) {
155         mTaskStack.top().getWindowConfiguration().setAppBounds(appBounds);
156     }
157 
configureTopActivityBounds(@onNull Rect activityBounds)158     void configureTopActivityBounds(@NonNull Rect activityBounds) {
159         doReturn(activityBounds).when(mActivityStack.top()).getBounds();
160     }
161 
162     @NonNull
top()163     ActivityRecord top() {
164         return mActivityStack.top();
165     }
166 
167     @NonNull
displayContent()168     DisplayContent displayContent() {
169         return mDisplayContent;
170     }
171 
172     @NonNull
getFromTop(int fromTop)173     ActivityRecord getFromTop(int fromTop) {
174         return mActivityStack.getFromTop(fromTop);
175     }
176 
setTaskWindowingMode(@indowingMode int windowingMode)177     void setTaskWindowingMode(@WindowingMode int windowingMode) {
178         mTaskStack.top().setWindowingMode(windowingMode);
179     }
180 
setTaskDisplayAreaWindowingMode(@indowingMode int windowingMode)181     void setTaskDisplayAreaWindowingMode(@WindowingMode int windowingMode) {
182         mTaskStack.top().getDisplayArea().setWindowingMode(windowingMode);
183     }
184 
setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled)185     void setLetterboxedForFixedOrientationAndAspectRatio(boolean enabled) {
186         doReturn(enabled).when(mActivityStack.top().mAppCompatController
187                 .getAspectRatioPolicy()).isLetterboxedForFixedOrientationAndAspectRatio();
188     }
189 
enableFullscreenCameraCompatTreatmentForTopActivity(boolean enabled)190     void enableFullscreenCameraCompatTreatmentForTopActivity(boolean enabled) {
191         if (mDisplayContent.mAppCompatCameraPolicy.hasDisplayRotationCompatPolicy()) {
192             doReturn(enabled).when(
193                     mDisplayContent.mAppCompatCameraPolicy.mDisplayRotationCompatPolicy)
194                         .isTreatmentEnabledForActivity(eq(mActivityStack.top()));
195         }
196     }
197 
setIsCameraRunningAndWindowingModeEligibleFullscreen(boolean enabled)198     void setIsCameraRunningAndWindowingModeEligibleFullscreen(boolean enabled) {
199         doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
200                 .isCameraRunningAndWindowingModeEligible(eq(mActivityStack.top()),
201                         /* mustBeFullscreen= */ eq(true));
202     }
203 
setIsCameraRunningAndWindowingModeEligibleFreeform(boolean enabled)204     void setIsCameraRunningAndWindowingModeEligibleFreeform(boolean enabled) {
205         doReturn(enabled).when(getTopCameraCompatFreeformPolicy())
206                 .isCameraRunningAndWindowingModeEligible(eq(mActivityStack.top()));
207     }
208 
setTopActivityEligibleForOrientationOverride(boolean enabled)209     void setTopActivityEligibleForOrientationOverride(boolean enabled) {
210         doReturn(enabled).when(getTopDisplayRotationCompatPolicy())
211                 .isActivityEligibleForOrientationOverride(eq(mActivityStack.top()));
212     }
213 
setTopActivityInTransition(boolean inTransition)214     void setTopActivityInTransition(boolean inTransition) {
215         doReturn(inTransition).when(mActivityStack.top()).isInTransition();
216     }
217 
setShouldApplyUserMinAspectRatioOverride(boolean enabled)218     void setShouldApplyUserMinAspectRatioOverride(boolean enabled) {
219         doReturn(enabled).when(mActivityStack.top().mAppCompatController
220                 .getAspectRatioOverrides()).shouldApplyUserMinAspectRatioOverride();
221     }
222 
setShouldCreateCompatDisplayInsets(boolean enabled)223     void setShouldCreateCompatDisplayInsets(boolean enabled) {
224         doReturn(enabled).when(mActivityStack.top()).shouldCreateAppCompatDisplayInsets();
225     }
226 
setTopActivityInSizeCompatMode(boolean inScm)227     void setTopActivityInSizeCompatMode(boolean inScm) {
228         doReturn(inScm).when(mActivityStack.top()).inSizeCompatMode();
229     }
230 
setShouldApplyUserFullscreenOverride(boolean enabled)231     void setShouldApplyUserFullscreenOverride(boolean enabled) {
232         doReturn(enabled).when(mActivityStack.top().mAppCompatController
233                 .getAspectRatioOverrides()).shouldApplyUserFullscreenOverride();
234     }
235 
setGetUserMinAspectRatioOverrideCode(@serMinAspectRatio int overrideCode)236     void setGetUserMinAspectRatioOverrideCode(@UserMinAspectRatio int overrideCode) {
237         doReturn(overrideCode).when(mActivityStack.top().mAppCompatController
238                 .getAspectRatioOverrides()).getUserMinAspectRatioOverrideCode();
239     }
240 
setGetUserMinAspectRatioOverrideValue(float overrideValue)241     void setGetUserMinAspectRatioOverrideValue(float overrideValue) {
242         doReturn(overrideValue).when(mActivityStack.top().mAppCompatController
243                 .getAspectRatioOverrides()).getUserMinAspectRatio();
244     }
245 
setShouldRefreshActivityForCameraCompat(boolean enabled)246     void setShouldRefreshActivityForCameraCompat(boolean enabled) {
247         doReturn(enabled).when(mActivityStack.top().mAppCompatController.getCameraOverrides())
248                 .shouldRefreshActivityForCameraCompat();
249     }
250 
setIgnoreOrientationRequest(boolean enabled)251     void setIgnoreOrientationRequest(boolean enabled) {
252         mDisplayContent.setIgnoreOrientationRequest(enabled);
253     }
254 
setTopActivityOrganizedTask()255     void setTopActivityOrganizedTask() {
256         doReturn(mTaskStack.top()).when(mActivityStack.top()).getOrganizedTask();
257     }
258 
setTopTaskInMultiWindowMode(boolean inMultiWindowMode)259     void setTopTaskInMultiWindowMode(boolean inMultiWindowMode) {
260         doReturn(inMultiWindowMode).when(mTaskStack.top()).inMultiWindowMode();
261     }
262 
setTopActivityAsEmbedded(boolean embedded)263     void setTopActivityAsEmbedded(boolean embedded) {
264         doReturn(embedded).when(mActivityStack.top()).isEmbedded();
265     }
266 
setTopActivityHasLetterboxedBounds(boolean letterboxed)267     void setTopActivityHasLetterboxedBounds(boolean letterboxed) {
268         doReturn(letterboxed).when(mActivityStack.top()).areBoundsLetterboxed();
269     }
270 
setTopActivityVisible(boolean isVisible)271     void setTopActivityVisible(boolean isVisible) {
272         doReturn(isVisible).when(mActivityStack.top()).isVisible();
273     }
274 
setTopActivityVisibleRequested(boolean isVisibleRequested)275     void setTopActivityVisibleRequested(boolean isVisibleRequested) {
276         doReturn(isVisibleRequested).when(mActivityStack.top()).isVisibleRequested();
277     }
278 
setTopActivityFillsParent(boolean fillsParent)279     void setTopActivityFillsParent(boolean fillsParent) {
280         doReturn(fillsParent).when(mActivityStack.top()).fillsParent();
281     }
282 
setTopActivityInMultiWindowMode(boolean multiWindowMode)283     void setTopActivityInMultiWindowMode(boolean multiWindowMode) {
284         doReturn(multiWindowMode).when(mActivityStack.top()).inMultiWindowMode();
285         if (multiWindowMode) {
286             doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivityStack.top()).getWindowingMode();
287         }
288     }
289 
setTopActivityInPinnedWindowingMode(boolean pinnedWindowingMode)290     void setTopActivityInPinnedWindowingMode(boolean pinnedWindowingMode) {
291         doReturn(pinnedWindowingMode).when(mActivityStack.top()).inPinnedWindowingMode();
292         if (pinnedWindowingMode) {
293             doReturn(WINDOWING_MODE_PINNED).when(mActivityStack.top()).getWindowingMode();
294         }
295     }
296 
setTopActivityInFreeformWindowingMode(boolean freeformWindowingMode)297     void setTopActivityInFreeformWindowingMode(boolean freeformWindowingMode) {
298         doReturn(freeformWindowingMode).when(mActivityStack.top()).inFreeformWindowingMode();
299         if (freeformWindowingMode) {
300             doReturn(WINDOWING_MODE_FREEFORM).when(mActivityStack.top()).getWindowingMode();
301         }
302     }
303 
setFixedRotationTransformDisplayBounds(@ullable Rect bounds)304     void setFixedRotationTransformDisplayBounds(@Nullable Rect bounds) {
305         doReturn(bounds).when(mActivityStack.top()).getFixedRotationTransformDisplayBounds();
306     }
307 
destroyTopActivity()308     void destroyTopActivity() {
309         mActivityStack.top().removeImmediately();
310     }
311 
destroyActivity(int fromTop)312     void destroyActivity(int fromTop) {
313         mActivityStack.applyTo(/* fromTop */ fromTop, ActivityRecord::removeImmediately);
314     }
315 
createNewDisplay()316     void createNewDisplay() {
317         mDisplayContent = new TestDisplayContent.Builder(mAtm, mDisplayWidth, mDisplayHeight)
318                 .build();
319         onPostDisplayContentCreation(mDisplayContent);
320     }
321 
createNewTask()322     void createNewTask() {
323         final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
324                 .setDisplay(mDisplayContent).build();
325         mTaskStack.push(newTask);
326     }
327 
createNewTaskWithBaseActivity()328     void createNewTaskWithBaseActivity() {
329         final Task newTask = new WindowTestsBase.TaskBuilder(mSupervisor)
330                 .setCreateActivity(true)
331                 // Respect "@ChangeId" according to test package's target sdk.
332                 .setPackage(mAtm.mContext.getPackageName())
333                 .setDisplay(mDisplayContent).build();
334         mTaskStack.push(newTask);
335         pushActivity(newTask.getTopNonFinishingActivity());
336     }
337 
attachTopActivityToTask()338     void attachTopActivityToTask() {
339         mTaskStack.top().addChild(mActivityStack.top());
340     }
341 
applyToTopActivity(Consumer<ActivityRecord> consumer)342     void applyToTopActivity(Consumer<ActivityRecord> consumer) {
343         consumer.accept(mActivityStack.top());
344     }
345 
applyToActivity(int fromTop, @NonNull Consumer<ActivityRecord> consumer)346     void applyToActivity(int fromTop, @NonNull Consumer<ActivityRecord> consumer) {
347         mActivityStack.applyTo(fromTop, consumer);
348     }
349 
applyToAllActivities(@onNull Consumer<ActivityRecord> consumer)350     void applyToAllActivities(@NonNull Consumer<ActivityRecord> consumer) {
351         mActivityStack.applyToAll(consumer);
352     }
353 
rotateDisplayForTopActivity(@urface.Rotation int rotation)354     void rotateDisplayForTopActivity(@Surface.Rotation int rotation) {
355         rotateDisplay(mActivityStack.top().mDisplayContent, rotation);
356     }
357 
configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop)358     void configureTopActivityFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
359         mActivityStack.applyToTop((topActivity) -> {
360             final DisplayRotation r = topActivity.mDisplayContent.getDisplayRotation();
361             doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
362             doReturn(false).when(r)
363                     .isDeviceInPosture(any(DeviceStateController.DeviceState.class),
364                             anyBoolean());
365             if (isHalfFolded) {
366                 doReturn(true).when(r)
367                         .isDeviceInPosture(DeviceStateController.DeviceState.HALF_FOLDED,
368                                 isTabletop);
369             }
370             topActivity.recomputeConfiguration();
371         });
372     }
373 
rotateDisplay(@urface.Rotation DisplayContent display, int rotation)374     private static void rotateDisplay(@Surface.Rotation DisplayContent display, int rotation) {
375         final Configuration c = new Configuration();
376         display.getDisplayRotation().setRotation(rotation);
377         display.computeScreenConfiguration(c);
378         display.onRequestedOverrideConfigurationChanged(c);
379     }
380 
assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate)381     void assertTrueOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
382         mActivityStack.applyTo(fromTop, (activity) -> {
383             Assert.assertTrue(predicate.test(activity));
384         });
385     }
386 
assertFalseOnTopActivity(Predicate<ActivityRecord> predicate)387     void assertFalseOnTopActivity(Predicate<ActivityRecord> predicate) {
388         Assert.assertFalse(predicate.test(mActivityStack.top()));
389     }
390 
assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate)391     void assertFalseOnActivity(int fromTop, Predicate<ActivityRecord> predicate) {
392         mActivityStack.applyTo(fromTop, (activity) -> {
393             Assert.assertFalse(predicate.test(activity));
394         });
395     }
396 
assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter)397     void assertNotNullOnTopActivity(Function<ActivityRecord, Object> getter) {
398         Assert.assertNotNull(getter.apply(mActivityStack.top()));
399     }
400 
assertNullOnTopActivity(Function<ActivityRecord, Object> getter)401     void assertNullOnTopActivity(Function<ActivityRecord, Object> getter) {
402         Assert.assertNull(getter.apply(mActivityStack.top()));
403     }
404 
checkTopActivityRecomputedConfiguration()405     void checkTopActivityRecomputedConfiguration() {
406         verify(mActivityStack.top()).recomputeConfiguration();
407     }
408 
checkTopActivityConfigOrientation(@onfiguration.Orientation int orientation)409     void checkTopActivityConfigOrientation(@Configuration.Orientation int orientation) {
410         Assert.assertEquals(orientation, mActivityStack.top()
411                 .getRequestedConfigurationOrientation());
412     }
413 
checkTopActivityAspectRatios(float minAspectRatio, float maxAspectRatio)414     void checkTopActivityAspectRatios(float minAspectRatio, float maxAspectRatio) {
415         final ActivityRecord topActivity = mActivityStack.top();
416         Assert.assertEquals(minAspectRatio, topActivity.getMinAspectRatio(),
417                 DELTA_ASPECT_RATIO_TOLERANCE);
418         Assert.assertEquals(maxAspectRatio, topActivity.getMaxAspectRatio(),
419                 DELTA_ASPECT_RATIO_TOLERANCE);
420     }
421 
checkTopActivityInSizeCompatMode(boolean inScm)422     void checkTopActivityInSizeCompatMode(boolean inScm) {
423         final ActivityRecord topActivity = mActivityStack.top();
424         Assert.assertEquals(inScm, topActivity.inSizeCompatMode());
425         Assert.assertNotEquals(1f, topActivity.getCompatScale(), COMPAT_SCALE_TOLERANCE);
426     }
427 
launchActivity(float minAspectRatio, float maxAspectRatio, @ActivityInfo.ScreenOrientation int orientation, boolean transparent, boolean withComponent, boolean addToTask)428     void launchActivity(float minAspectRatio, float maxAspectRatio,
429             @ActivityInfo.ScreenOrientation int orientation, boolean transparent,
430             boolean withComponent, boolean addToTask) {
431         final WindowTestsBase.ActivityBuilder
432                 activityBuilder = new WindowTestsBase.ActivityBuilder(mAtm)
433                 .setScreenOrientation(orientation)
434                 .setLaunchedFromUid(0);
435         if (transparent) {
436             activityBuilder.setActivityTheme(android.R.style.Theme_Translucent);
437         }
438         if (withComponent) {
439             // Set the component to be that of the test class in order
440             // to enable compat changes
441             activityBuilder.setComponent(ComponentName.createRelative(mAtm.mContext,
442                     TEST_COMPONENT_NAME));
443         }
444         if (minAspectRatio >= 0) {
445             activityBuilder.setMinAspectRatio(minAspectRatio);
446         }
447         if (maxAspectRatio >= 0) {
448             activityBuilder.setMaxAspectRatio(maxAspectRatio);
449         }
450         final ActivityRecord newActivity = activityBuilder.build();
451         if (addToTask) {
452             if (mTaskStack.isEmpty()) {
453                 createNewTask();
454             }
455             mTaskStack.top().addChild(newActivity);
456         }
457         pushActivity(newActivity);
458     }
459 
460     /**
461      * Specific Robots can override this method to add operation to run on a newly created
462      * {@link ActivityRecord}. Common case is to invoke spyOn().
463      *
464      * @param activity The newly created {@link ActivityRecord}.
465      */
466     @CallSuper
onPostActivityCreation(@onNull ActivityRecord activity)467     void onPostActivityCreation(@NonNull ActivityRecord activity) {
468         if (mOnPostActivityCreation != null) {
469             mOnPostActivityCreation.accept(activity);
470         }
471     }
472 
473     /**
474      * Specific Robots can override this method to add operation to run on a newly created
475      * {@link DisplayContent}. Common case is to invoke spyOn().
476      *
477      * @param displayContent The newly created {@link DisplayContent}.
478      */
479     @CallSuper
onPostDisplayContentCreation(@onNull DisplayContent displayContent)480     void onPostDisplayContentCreation(@NonNull DisplayContent displayContent) {
481         spyOn(mDisplayContent);
482         if (mOnPostDisplayContentCreation != null) {
483             mOnPostDisplayContentCreation.accept(mDisplayContent);
484         }
485     }
486 
createActivityWithComponentInNewTask(boolean inNewTask, boolean inNewDisplay)487     private void createActivityWithComponentInNewTask(boolean inNewTask, boolean inNewDisplay) {
488         if (inNewDisplay) {
489             createNewDisplay();
490         }
491         if (inNewTask) {
492             createNewTask();
493         }
494         final WindowTestsBase.ActivityBuilder activityBuilder =
495                 new WindowTestsBase.ActivityBuilder(mAtm).setOnTop(true)
496                 // Set the component to be that of the test class in order
497                 // to enable compat changes
498                 .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME));
499         if (!mTaskStack.isEmpty()) {
500             // We put the Activity in the current task if any.
501             activityBuilder.setTask(mTaskStack.top());
502         }
503         pushActivity(activityBuilder.build());
504     }
505 
506     /**
507      * Setups activity with restriction on its bounds, such as maxAspect, minAspect,
508      * fixed orientation, and/or whether it is resizable.
509      */
prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect, @ActivityInfo.ScreenOrientation int screenOrientation, boolean isUnresizable)510     private void prepareLimitedBounds(ActivityRecord activity, float minAspect, float maxAspect,
511             @ActivityInfo.ScreenOrientation int screenOrientation, boolean isUnresizable) {
512         activity.info.resizeMode = isUnresizable
513                 ? RESIZE_MODE_UNRESIZEABLE
514                 : RESIZE_MODE_RESIZEABLE;
515         final Task task = activity.getTask();
516         if (task != null) {
517             // Update the Task resize value as activity will follow the task.
518             task.mResizeMode = activity.info.resizeMode;
519             task.getRootActivity().info.resizeMode = activity.info.resizeMode;
520         }
521         activity.setVisibleRequested(true);
522         if (maxAspect >= 0) {
523             activity.info.setMaxAspectRatio(maxAspect);
524         }
525         if (minAspect >= 0) {
526             activity.info.setMinAspectRatio(minAspect);
527         }
528         if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
529             activity.info.screenOrientation = screenOrientation;
530             activity.setRequestedOrientation(screenOrientation);
531         }
532         // Make sure to use the provided configuration to construct the size compat fields.
533         activity.mAppCompatController.getSizeCompatModePolicy().clearSizeCompatMode();
534         activity.ensureActivityConfiguration();
535         // Make sure the display configuration reflects the change of activity.
536         if (activity.mDisplayContent.updateOrientation()) {
537             activity.mDisplayContent.sendNewConfiguration();
538         }
539     }
540 
getTopDisplayRotationCompatPolicy()541     private DisplayRotationCompatPolicy getTopDisplayRotationCompatPolicy() {
542         return mActivityStack.top().mDisplayContent.mAppCompatCameraPolicy
543                 .mDisplayRotationCompatPolicy;
544     }
545 
getTopCameraCompatFreeformPolicy()546     private CameraCompatFreeformPolicy getTopCameraCompatFreeformPolicy() {
547         return mActivityStack.top().mDisplayContent.mAppCompatCameraPolicy
548                 .mCameraCompatFreeformPolicy;
549     }
550 
551     // We add the activity to the stack and spyOn() on its properties.
pushActivity(@onNull ActivityRecord activity)552     private void pushActivity(@NonNull ActivityRecord activity) {
553         mActivityStack.push(activity);
554         onPostActivityCreation(activity);
555     }
556 }
557