Home | History | Annotate | Download | only in am
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package android.server.am;
     18 
     19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
     20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
     21 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
     22 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
     23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
     24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
     25 import static android.server.am.ActivityAndWindowManagersState.dpToPx;
     26 import static android.server.am.ActivityManagerState.STATE_RESUMED;
     27 import static android.server.am.ComponentNameUtils.getWindowName;
     28 import static android.server.am.Components.BROADCAST_RECEIVER_ACTIVITY;
     29 import static android.server.am.Components.BroadcastReceiverActivity.ACTION_TRIGGER_BROADCAST;
     30 import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_BROADCAST_ORIENTATION;
     31 import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_FINISH_BROADCAST;
     32 import static android.server.am.Components.BroadcastReceiverActivity.EXTRA_MOVE_BROADCAST_TO_BACK;
     33 import static android.server.am.Components.DIALOG_WHEN_LARGE_ACTIVITY;
     34 import static android.server.am.Components.LANDSCAPE_ORIENTATION_ACTIVITY;
     35 import static android.server.am.Components.LAUNCHING_ACTIVITY;
     36 import static android.server.am.Components.NIGHT_MODE_ACTIVITY;
     37 import static android.server.am.Components.PORTRAIT_ORIENTATION_ACTIVITY;
     38 import static android.server.am.Components.RESIZEABLE_ACTIVITY;
     39 import static android.server.am.Components.TEST_ACTIVITY;
     40 import static android.server.am.StateLogger.log;
     41 import static android.server.am.StateLogger.logE;
     42 import static android.server.am.translucentapp.Components.TRANSLUCENT_LANDSCAPE_ACTIVITY;
     43 import static android.server.am.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY;
     44 import static android.view.Surface.ROTATION_0;
     45 import static android.view.Surface.ROTATION_180;
     46 import static android.view.Surface.ROTATION_270;
     47 import static android.view.Surface.ROTATION_90;
     48 
     49 import static org.hamcrest.MatcherAssert.assertThat;
     50 import static org.hamcrest.Matchers.lessThan;
     51 import static org.junit.Assert.assertEquals;
     52 import static org.junit.Assert.assertFalse;
     53 import static org.junit.Assert.assertNotEquals;
     54 import static org.junit.Assert.assertNotNull;
     55 import static org.junit.Assert.assertNull;
     56 import static org.junit.Assert.fail;
     57 import static org.junit.Assume.assumeTrue;
     58 
     59 import android.content.ComponentName;
     60 import android.graphics.Rect;
     61 import android.platform.test.annotations.Presubmit;
     62 import android.support.test.filters.FlakyTest;
     63 
     64 import org.junit.Ignore;
     65 import org.junit.Test;
     66 
     67 import java.util.List;
     68 
     69 /**
     70  * Build/Install/Run:
     71  *     atest CtsActivityManagerDeviceTestCases:ActivityManagerAppConfigurationTests
     72  */
     73 public class ActivityManagerAppConfigurationTests extends ActivityManagerTestBase {
     74 
     75     // TODO(b/70247058): Use {@link Context#sendBroadcast(Intent).
     76     // Shell command to finish {@link #BROADCAST_RECEIVER_ACTIVITY}.
     77     private static final String FINISH_ACTIVITY_BROADCAST = "am broadcast -a "
     78             + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_FINISH_BROADCAST + " true";
     79     // Shell command to move {@link #BROADCAST_RECEIVER_ACTIVITY} task to back.
     80     private static final String MOVE_TASK_TO_BACK_BROADCAST = "am broadcast -a "
     81             + ACTION_TRIGGER_BROADCAST + " --ez " + EXTRA_MOVE_BROADCAST_TO_BACK + " true";
     82     // Shell command to request portrait orientation to {@link #BROADCAST_RECEIVER_ACTIVITY}.
     83     private static final String REQUEST_PORTRAIT_BROADCAST = "am broadcast -a "
     84             + ACTION_TRIGGER_BROADCAST + " --ei " + EXTRA_BROADCAST_ORIENTATION + " 1";
     85     private static final String REQUEST_LANDSCAPE_BROADCAST = "am broadcast -a "
     86             + ACTION_TRIGGER_BROADCAST + " --ei " + EXTRA_BROADCAST_ORIENTATION + " 0";
     87 
     88     private static final int SMALL_WIDTH_DP = 426;
     89     private static final int SMALL_HEIGHT_DP = 320;
     90 
     91     /**
     92      * Tests that the WindowManager#getDefaultDisplay() and the Configuration of the Activity
     93      * has an updated size when the Activity is resized from fullscreen to docked state.
     94      *
     95      * The Activity handles configuration changes, so it will not be restarted between resizes.
     96      * On Configuration changes, the Activity logs the Display size and Configuration width
     97      * and heights. The values reported in fullscreen should be larger than those reported in
     98      * docked state.
     99      */
    100     @Test
    101     public void testConfigurationUpdatesWhenResizedFromFullscreen() {
    102         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    103 
    104         LogSeparator logSeparator = separateLogs();
    105         launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
    106         final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    107                 logSeparator);
    108 
    109         logSeparator = separateLogs();
    110         setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    111         final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    112                 logSeparator);
    113 
    114         assertSizesAreSane(fullscreenSizes, dockedSizes);
    115     }
    116 
    117     /**
    118      * Same as {@link #testConfigurationUpdatesWhenResizedFromFullscreen()} but resizing
    119      * from docked state to fullscreen (reverse).
    120      */
    121     @Presubmit
    122     @Test
    123     @FlakyTest(bugId = 71792393)
    124     public void testConfigurationUpdatesWhenResizedFromDockedStack() {
    125         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    126 
    127         LogSeparator logSeparator = separateLogs();
    128         launchActivity(RESIZEABLE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    129         final ReportedSizes dockedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    130                 logSeparator);
    131 
    132         logSeparator = separateLogs();
    133         setActivityTaskWindowingMode(RESIZEABLE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
    134         final ReportedSizes fullscreenSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    135                 logSeparator);
    136 
    137         assertSizesAreSane(fullscreenSizes, dockedSizes);
    138     }
    139 
    140     /**
    141      * Tests whether the Display sizes change when rotating the device.
    142      */
    143     @Test
    144     public void testConfigurationUpdatesWhenRotatingWhileFullscreen() throws Exception {
    145         assumeTrue("Skipping test: no rotation support", supportsRotation());
    146 
    147         try (final RotationSession rotationSession = new RotationSession()) {
    148             rotationSession.set(ROTATION_0);
    149 
    150             final LogSeparator logSeparator = separateLogs();
    151             launchActivity(RESIZEABLE_ACTIVITY,
    152                     WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
    153             final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    154                     logSeparator);
    155 
    156             rotateAndCheckSizes(rotationSession, initialSizes);
    157         }
    158     }
    159 
    160     /**
    161      * Same as {@link #testConfigurationUpdatesWhenRotatingWhileFullscreen()} but when the Activity
    162      * is in the docked stack.
    163      */
    164     @Presubmit
    165     @Test
    166     public void testConfigurationUpdatesWhenRotatingWhileDocked() throws Exception {
    167         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    168 
    169         try (final RotationSession rotationSession = new RotationSession()) {
    170             rotationSession.set(ROTATION_0);
    171 
    172             final LogSeparator logSeparator = separateLogs();
    173             // Launch our own activity to side in case Recents (or other activity to side) doesn't
    174             // support rotation.
    175             launchActivitiesInSplitScreen(
    176                     getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
    177                     getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
    178             // Launch target activity in docked stack.
    179             getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY).execute();
    180             final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    181                     logSeparator);
    182 
    183             rotateAndCheckSizes(rotationSession, initialSizes);
    184         }
    185     }
    186 
    187     /**
    188      * Same as {@link #testConfigurationUpdatesWhenRotatingWhileDocked()} but when the Activity
    189      * is launched to side from docked stack.
    190      */
    191     @Presubmit
    192     @Test
    193     public void testConfigurationUpdatesWhenRotatingToSideFromDocked() throws Exception {
    194         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    195 
    196         try (final RotationSession rotationSession = new RotationSession()) {
    197             rotationSession.set(ROTATION_0);
    198 
    199             final LogSeparator logSeparator = separateLogs();
    200             launchActivitiesInSplitScreen(
    201                     getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
    202                     getLaunchActivityBuilder().setTargetActivity(RESIZEABLE_ACTIVITY));
    203             final ReportedSizes initialSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    204                     logSeparator);
    205 
    206             rotateAndCheckSizes(rotationSession, initialSizes);
    207         }
    208     }
    209 
    210     private void rotateAndCheckSizes(RotationSession rotationSession, ReportedSizes prevSizes)
    211             throws Exception {
    212         final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
    213         for (final int rotation : rotations) {
    214             final LogSeparator logSeparator = separateLogs();
    215             final ActivityManagerState.ActivityTask task =
    216                     mAmWmState.getAmState().getTaskByActivity(RESIZEABLE_ACTIVITY);
    217             final int displayId = mAmWmState.getAmState().getStackById(task.mStackId).mDisplayId;
    218             rotationSession.set(rotation);
    219             final int newDeviceRotation = getDeviceRotation(displayId);
    220             if (newDeviceRotation == INVALID_DEVICE_ROTATION) {
    221                 logE("Got an invalid device rotation value. "
    222                         + "Continuing the test despite of that, but it is likely to fail.");
    223             } else if (rotation != newDeviceRotation) {
    224                 log("This device doesn't support locked user "
    225                         + "rotation mode. Not continuing the rotation checks.");
    226                 return;
    227             }
    228 
    229             final ReportedSizes rotatedSizes = getActivityDisplaySize(RESIZEABLE_ACTIVITY,
    230                     logSeparator);
    231             assertSizesRotate(prevSizes, rotatedSizes,
    232                     // Skip orientation checks if we are not in fullscreen mode.
    233                     task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN);
    234             prevSizes = rotatedSizes;
    235         }
    236     }
    237 
    238     /**
    239      * Tests when activity moved from fullscreen stack to docked and back. Activity will be
    240      * relaunched twice and it should have same config as initial one.
    241      */
    242     @Test
    243     public void testSameConfigurationFullSplitFullRelaunch() {
    244         moveActivityFullSplitFull(TEST_ACTIVITY);
    245     }
    246 
    247     /**
    248      * Same as {@link #testSameConfigurationFullSplitFullRelaunch} but without relaunch.
    249      */
    250     @Presubmit
    251     @Test
    252     public void testSameConfigurationFullSplitFullNoRelaunch() {
    253         moveActivityFullSplitFull(RESIZEABLE_ACTIVITY);
    254     }
    255 
    256     /**
    257      * Launches activity in fullscreen stack, moves to docked stack and back to fullscreen stack.
    258      * Last operation is done in a way which simulates split-screen divider movement maximizing
    259      * docked stack size and then moving task to fullscreen stack - the same way it is done when
    260      * user long-presses overview/recents button to exit split-screen.
    261      * Asserts that initial and final reported sizes in fullscreen stack are the same.
    262      */
    263     private void moveActivityFullSplitFull(ComponentName activityName) {
    264         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    265 
    266         // Launch to fullscreen stack and record size.
    267         LogSeparator logSeparator = separateLogs();
    268         launchActivity(activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
    269         final ReportedSizes initialFullscreenSizes = getActivityDisplaySize(activityName,
    270                 logSeparator);
    271         final Rect displayRect = getDisplayRect(activityName);
    272 
    273         // Move to docked stack.
    274         logSeparator = separateLogs();
    275         setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    276         final ReportedSizes dockedSizes = getActivityDisplaySize(activityName, logSeparator);
    277         assertSizesAreSane(initialFullscreenSizes, dockedSizes);
    278         // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
    279         // will come up.
    280         launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    281         mAmWmState.computeState(false /* compareTaskAndStackBounds */,
    282                 new WaitForValidActivityState.Builder(activityName).build());
    283         final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
    284                 .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    285 
    286         // Resize docked stack to fullscreen size. This will trigger activity relaunch with
    287         // non-empty override configuration corresponding to fullscreen size.
    288         logSeparator = separateLogs();
    289         mAm.resizeStack(stack.mStackId, displayRect);
    290 
    291         // Move activity back to fullscreen stack.
    292         setActivityTaskWindowingMode(activityName,
    293                 WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
    294         final ReportedSizes finalFullscreenSizes = getActivityDisplaySize(activityName,
    295                 logSeparator);
    296 
    297         // After activity configuration was changed twice it must report same size as original one.
    298         assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes);
    299     }
    300 
    301     /**
    302      * Tests when activity moved from docked stack to fullscreen and back. Activity will be
    303      * relaunched twice and it should have same config as initial one.
    304      */
    305     @Test
    306     public void testSameConfigurationSplitFullSplitRelaunch() {
    307         moveActivitySplitFullSplit(TEST_ACTIVITY);
    308     }
    309 
    310     /**
    311      * Same as {@link #testSameConfigurationSplitFullSplitRelaunch} but without relaunch.
    312      */
    313     @Test
    314     public void testSameConfigurationSplitFullSplitNoRelaunch() {
    315         moveActivitySplitFullSplit(RESIZEABLE_ACTIVITY);
    316     }
    317 
    318     /**
    319      * Tests that an activity with the DialogWhenLarge theme can transform properly when in split
    320      * screen.
    321      */
    322     @Presubmit
    323     @Test
    324     public void testDialogWhenLargeSplitSmall() {
    325         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    326 
    327         launchActivity(DIALOG_WHEN_LARGE_ACTIVITY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    328         final ActivityManagerState.ActivityStack stack = mAmWmState.getAmState()
    329                 .getStandardStackByWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    330         final WindowManagerState.Display display =
    331                 mAmWmState.getWmState().getDisplay(stack.mDisplayId);
    332         final int density = display.getDpi();
    333         final int smallWidthPx = dpToPx(SMALL_WIDTH_DP, density);
    334         final int smallHeightPx = dpToPx(SMALL_HEIGHT_DP, density);
    335 
    336         mAm.resizeStack(stack.mStackId, new Rect(0, 0, smallWidthPx, smallHeightPx));
    337         mAmWmState.waitForValidState(
    338                 new WaitForValidActivityState.Builder(DIALOG_WHEN_LARGE_ACTIVITY)
    339                         .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
    340                         .setActivityType(ACTIVITY_TYPE_STANDARD)
    341                         .build());
    342     }
    343 
    344     /**
    345      * Test that device handles consequent requested orientations and displays the activities.
    346      */
    347     @Presubmit
    348     @Test
    349     @FlakyTest(bugId = 71875755)
    350     public void testFullscreenAppOrientationRequests() {
    351         assumeTrue("Skipping test: no rotation support", supportsRotation());
    352 
    353         LogSeparator logSeparator = separateLogs();
    354         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    355         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
    356         ReportedSizes reportedSizes =
    357                 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
    358         assertEquals("portrait activity should be in portrait",
    359                 1 /* portrait */, reportedSizes.orientation);
    360         logSeparator = separateLogs();
    361 
    362         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
    363         mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
    364         reportedSizes =
    365                 getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY, logSeparator);
    366         assertEquals("landscape activity should be in landscape",
    367                 2 /* landscape */, reportedSizes.orientation);
    368         logSeparator = separateLogs();
    369 
    370         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    371         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
    372         reportedSizes =
    373                 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
    374         assertEquals("portrait activity should be in portrait",
    375                 1 /* portrait */, reportedSizes.orientation);
    376         logSeparator = separateLogs();
    377     }
    378 
    379     @Test
    380     public void testNonfullscreenAppOrientationRequests() {
    381         assumeTrue("Skipping test: no rotation support", supportsRotation());
    382 
    383         LogSeparator logSeparator = separateLogs();
    384         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
    385         final ReportedSizes initialReportedSizes =
    386                 getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
    387         assertEquals("portrait activity should be in portrait",
    388                 1 /* portrait */, initialReportedSizes.orientation);
    389         logSeparator = separateLogs();
    390 
    391         launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
    392         assertEquals("Legacy non-fullscreen activity requested landscape orientation",
    393                 0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    394 
    395         // TODO(b/36897968): uncomment once we can suppress unsupported configurations
    396         // final ReportedSizes updatedReportedSizes =
    397         //      getLastReportedSizesForActivity(PORTRAIT_ACTIVITY_NAME, logSeparator);
    398         // assertEquals("portrait activity should not have moved from portrait",
    399         //         1 /* portrait */, updatedReportedSizes.orientation);
    400     }
    401 
    402     /**
    403      * Test that device handles consequent requested orientations and will not report a config
    404      * change to an invisible activity.
    405      */
    406     @Test
    407     public void testAppOrientationRequestConfigChanges() {
    408         assumeTrue("Skipping test: no rotation support", supportsRotation());
    409 
    410         LogSeparator logSeparator = separateLogs();
    411         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    412         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
    413 
    414         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
    415                 1 /* start */, 1 /* resume */, 0 /* pause */, 0 /* stop */, 0 /* destroy */,
    416                 0 /* config */);
    417 
    418         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
    419         mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
    420 
    421         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
    422                 1 /* start */, 1 /* resume */, 1 /* pause */, 1 /* stop */, 0 /* destroy */,
    423                 0 /* config */);
    424         assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
    425                 1 /* start */, 1 /* resume */, 0 /* pause */, 0 /* stop */, 0 /* destroy */,
    426                 0 /* config */);
    427 
    428         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    429         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
    430 
    431         assertLifecycleCounts(PORTRAIT_ORIENTATION_ACTIVITY, logSeparator, 2 /* create */,
    432                 2 /* start */, 2 /* resume */, 1 /* pause */, 1 /* stop */, 0 /* destroy */,
    433                 0 /* config */);
    434         assertLifecycleCounts(LANDSCAPE_ORIENTATION_ACTIVITY, logSeparator, 1 /* create */,
    435                 1 /* start */, 1 /* resume */, 1 /* pause */, 1 /* stop */, 0 /* destroy */,
    436                 0 /* config */);
    437     }
    438 
    439     /**
    440      * Test that device orientation is restored when an activity that requests it is no longer
    441      * visible.
    442      */
    443     @Test
    444     public void testAppOrientationRequestConfigClears() {
    445         assumeTrue("Skipping test: no rotation support", supportsRotation());
    446 
    447         LogSeparator logSeparator = separateLogs();
    448         launchActivity(TEST_ACTIVITY);
    449         mAmWmState.assertVisibility(TEST_ACTIVITY, true /* visible */);
    450         final ReportedSizes initialReportedSizes =
    451                 getLastReportedSizesForActivity(TEST_ACTIVITY, logSeparator);
    452         final int  initialOrientation = initialReportedSizes.orientation;
    453 
    454 
    455         // Launch an activity that requests different orientation and check that it will be applied
    456         final boolean launchingPortrait;
    457         if (initialOrientation == 2 /* landscape */) {
    458             launchingPortrait = true;
    459         } else if (initialOrientation == 1 /* portrait */) {
    460             launchingPortrait = false;
    461         } else {
    462             fail("Unexpected orientation value: " + initialOrientation);
    463             return;
    464         }
    465         final ComponentName differentOrientationActivity = launchingPortrait
    466                 ? PORTRAIT_ORIENTATION_ACTIVITY : LANDSCAPE_ORIENTATION_ACTIVITY;
    467         logSeparator = separateLogs();
    468         launchActivity(differentOrientationActivity);
    469         mAmWmState.assertVisibility(differentOrientationActivity, true /* visible */);
    470         final ReportedSizes rotatedReportedSizes =
    471                 getLastReportedSizesForActivity(differentOrientationActivity, logSeparator);
    472         assertEquals("Applied orientation must correspond to activity request",
    473                 launchingPortrait ? 1 : 2, rotatedReportedSizes.orientation);
    474 
    475         // Launch another activity on top and check that its orientation is not affected by previous
    476         // activity.
    477         logSeparator = separateLogs();
    478         launchActivity(RESIZEABLE_ACTIVITY);
    479         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
    480         final ReportedSizes finalReportedSizes =
    481                 getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY, logSeparator);
    482         assertEquals("Applied orientation must not be influenced by previously visible activity",
    483                 initialOrientation, finalReportedSizes.orientation);
    484     }
    485 
    486     // TODO(b/70870253): This test seems malfunction.
    487     @Ignore("b/70870253")
    488     @Test
    489     public void testNonFullscreenActivityProhibited() {
    490         // We do not wait for the activity as it should not launch based on the restrictions around
    491         // specifying orientation. We instead start an activity known to launch immediately after
    492         // so that we can ensure processing the first activity occurred.
    493         launchActivityNoWait(TRANSLUCENT_LANDSCAPE_ACTIVITY);
    494         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    495 
    496         assertFalse("target SDK > 26 non-fullscreen activity should not reach onResume",
    497                 mAmWmState.getAmState().containsActivity(TRANSLUCENT_LANDSCAPE_ACTIVITY));
    498     }
    499 
    500     @Test
    501     public void testNonFullscreenActivityPermitted() throws Exception {
    502         try (final RotationSession rotationSession = new RotationSession()) {
    503             rotationSession.set(ROTATION_0);
    504 
    505             launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
    506             mAmWmState.assertResumedActivity(
    507                     "target SDK <= 26 non-fullscreen activity should be allowed to launch",
    508                     SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY);
    509             assertEquals("non-fullscreen activity requested landscape orientation",
    510                     0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    511         }
    512     }
    513 
    514     /**
    515      * Test that device handles moving between two tasks with different orientations.
    516      */
    517     @Test
    518     public void testTaskCloseRestoreFixedOrientation() {
    519         assumeTrue("Skipping test: no rotation support", supportsRotation());
    520 
    521         // Start landscape activity.
    522         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
    523         mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
    524         assertEquals("Fullscreen app requested landscape orientation",
    525                 0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    526 
    527         // Start another activity in a different task.
    528         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
    529 
    530         // Request portrait
    531         executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
    532         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
    533 
    534         // Finish activity
    535         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
    536 
    537         // Verify that activity brought to front is in originally requested orientation.
    538         mAmWmState.computeState(LANDSCAPE_ORIENTATION_ACTIVITY);
    539         assertEquals("Should return to app in landscape orientation",
    540                 0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    541     }
    542 
    543     /**
    544      * Test that device handles moving between two tasks with different orientations.
    545      */
    546     @Test
    547     public void testTaskCloseRestoreFreeOrientation() {
    548         assumeTrue("Skipping test: no rotation support", supportsRotation());
    549 
    550         // Start landscape activity.
    551         launchActivity(RESIZEABLE_ACTIVITY);
    552         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
    553         final int initialServerOrientation = mAmWmState.getWmState().getLastOrientation();
    554 
    555         // Verify fixed-landscape
    556         LogSeparator logSeparator = separateLogs();
    557         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
    558         executeShellCommand(REQUEST_LANDSCAPE_BROADCAST);
    559         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
    560         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
    561 
    562         // Verify that activity brought to front is in originally requested orientation.
    563         mAmWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
    564         ReportedSizes reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY,
    565                 logSeparator);
    566         assertNull("Should come back in original orientation", reportedSizes);
    567         assertEquals("Should come back in original server orientation",
    568                 initialServerOrientation, mAmWmState.getWmState().getLastOrientation());
    569         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
    570                 0 /* numConfigChange */, logSeparator);
    571 
    572         // Verify fixed-portrait
    573         logSeparator = separateLogs();
    574         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
    575         executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
    576         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
    577         executeShellCommand(FINISH_ACTIVITY_BROADCAST);
    578 
    579         // Verify that activity brought to front is in originally requested orientation.
    580         mAmWmState.waitForActivityState(RESIZEABLE_ACTIVITY, STATE_RESUMED);
    581         reportedSizes = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY, logSeparator);
    582         assertNull("Should come back in original orientation", reportedSizes);
    583         assertEquals("Should come back in original server orientation",
    584                 initialServerOrientation, mAmWmState.getWmState().getLastOrientation());
    585         assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
    586                 0 /* numConfigChange */, logSeparator);
    587     }
    588 
    589     /**
    590      * Test that activity orientation will change when device is rotated.
    591      * Also verify that occluded activity will not get config changes.
    592      */
    593     @Test
    594     public void testAppOrientationWhenRotating() throws Exception {
    595         assumeTrue("Skipping test: no rotation support", supportsRotation());
    596 
    597         // Start resizeable activity that handles configuration changes.
    598         LogSeparator logSeparator = separateLogs();
    599         launchActivity(TEST_ACTIVITY);
    600         launchActivity(RESIZEABLE_ACTIVITY);
    601         mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY, true /* visible */);
    602 
    603         final int displayId = mAmWmState.getAmState().getDisplayByActivity(RESIZEABLE_ACTIVITY);
    604 
    605         // Rotate the activity and check that it receives configuration changes with a different
    606         // orientation each time.
    607         try (final RotationSession rotationSession = new RotationSession()) {
    608             rotationSession.set(ROTATION_0);
    609             ReportedSizes reportedSizes =
    610                     getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY, logSeparator);
    611             int prevOrientation = reportedSizes.orientation;
    612 
    613             final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
    614             for (final int rotation : rotations) {
    615                 logSeparator = separateLogs();
    616                 rotationSession.set(rotation);
    617                 final int newDeviceRotation = getDeviceRotation(displayId);
    618                 if (rotation != newDeviceRotation) {
    619                     log("This device doesn't support locked user "
    620                             + "rotation mode. Not continuing the rotation checks.");
    621                     continue;
    622                 }
    623 
    624                 // Verify lifecycle count and orientation changes.
    625                 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
    626                         1 /* numConfigChange */, logSeparator);
    627                 reportedSizes =
    628                         getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY, logSeparator);
    629                 assertNotEquals(prevOrientation, reportedSizes.orientation);
    630                 assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */,
    631                         0 /* numConfigChange */, logSeparator);
    632 
    633                 prevOrientation = reportedSizes.orientation;
    634             }
    635         }
    636     }
    637 
    638     /**
    639      * Test that activity orientation will not change when trying to rotate fixed-orientation
    640      * activity.
    641      * Also verify that occluded activity will not get config changes.
    642      */
    643     @Test
    644     public void testFixedOrientationWhenRotating() throws Exception {
    645         assumeTrue("Skipping test: no rotation support", supportsRotation());
    646 
    647         // Start portrait-fixed activity
    648         LogSeparator logSeparator = separateLogs();
    649         launchActivity(RESIZEABLE_ACTIVITY);
    650         launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    651         mAmWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
    652 
    653         final int displayId = mAmWmState.getAmState()
    654                 .getDisplayByActivity(PORTRAIT_ORIENTATION_ACTIVITY);
    655 
    656         // Rotate the activity and check that the orientation doesn't change
    657         try (final RotationSession rotationSession = new RotationSession()) {
    658             rotationSession.set(ROTATION_0);
    659 
    660             final int[] rotations = { ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0 };
    661             for (final int rotation : rotations) {
    662                 logSeparator = separateLogs();
    663                 rotationSession.set(rotation);
    664                 final int newDeviceRotation = getDeviceRotation(displayId);
    665                 if (rotation != newDeviceRotation) {
    666                     log("This device doesn't support locked user "
    667                             + "rotation mode. Not continuing the rotation checks.");
    668                     continue;
    669                 }
    670 
    671                 // Verify lifecycle count and orientation changes.
    672                 assertRelaunchOrConfigChanged(PORTRAIT_ORIENTATION_ACTIVITY, 0 /* numRelaunch */,
    673                         0 /* numConfigChange */, logSeparator);
    674                 final ReportedSizes reportedSizes = getLastReportedSizesForActivity(
    675                         PORTRAIT_ORIENTATION_ACTIVITY, logSeparator);
    676                 assertNull("No new sizes must be reported", reportedSizes);
    677                 assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY, 0 /* numRelaunch */,
    678                         0 /* numConfigChange */, logSeparator);
    679             }
    680         }
    681     }
    682 
    683     /**
    684      * Test that device handles moving between two tasks with different orientations.
    685      */
    686     @Presubmit
    687     @Test
    688     @FlakyTest(bugId = 71792393)
    689     public void testTaskMoveToBackOrientation() {
    690         assumeTrue("Skipping test: no rotation support", supportsRotation());
    691 
    692         // Start landscape activity.
    693         launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
    694         mAmWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
    695         assertEquals("Fullscreen app requested landscape orientation",
    696                 0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    697 
    698         // Start another activity in a different task.
    699         launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
    700 
    701         // Request portrait
    702         executeShellCommand(REQUEST_PORTRAIT_BROADCAST);
    703         mAmWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
    704 
    705         // Finish activity
    706         executeShellCommand(MOVE_TASK_TO_BACK_BROADCAST);
    707 
    708         // Verify that activity brought to front is in originally requested orientation.
    709         mAmWmState.waitForValidState(LANDSCAPE_ORIENTATION_ACTIVITY);
    710         assertEquals("Should return to app in landscape orientation",
    711                 0 /* landscape */, mAmWmState.getWmState().getLastOrientation());
    712     }
    713 
    714     /**
    715      * Test that device doesn't change device orientation by app request while in multi-window.
    716      */
    717     @Presubmit
    718     @FlakyTest(bugId = 71918731)
    719     @Test
    720     public void testSplitscreenPortraitAppOrientationRequests() throws Exception {
    721         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    722 
    723         try (final RotationSession rotationSession = new RotationSession()) {
    724             requestOrientationInSplitScreen(rotationSession,
    725                     ROTATION_90 /* portrait */, LANDSCAPE_ORIENTATION_ACTIVITY);
    726         }
    727     }
    728 
    729     /**
    730      * Test that device doesn't change device orientation by app request while in multi-window.
    731      */
    732     @Presubmit
    733     @Test
    734     public void testSplitscreenLandscapeAppOrientationRequests() throws Exception {
    735         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    736 
    737         try (final RotationSession rotationSession = new RotationSession()) {
    738             requestOrientationInSplitScreen(rotationSession,
    739                     ROTATION_0 /* landscape */, PORTRAIT_ORIENTATION_ACTIVITY);
    740         }
    741     }
    742 
    743     /**
    744      * Rotate the device and launch specified activity in split-screen, checking if orientation
    745      * didn't change.
    746      */
    747     private void requestOrientationInSplitScreen(RotationSession rotationSession, int orientation,
    748             ComponentName activity) throws Exception {
    749         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    750 
    751         // Set initial orientation.
    752         rotationSession.set(orientation);
    753 
    754         // Launch activities that request orientations and check that device doesn't rotate.
    755         launchActivitiesInSplitScreen(
    756                 getLaunchActivityBuilder().setTargetActivity(LAUNCHING_ACTIVITY),
    757                 getLaunchActivityBuilder().setTargetActivity(activity).setMultipleTask(true));
    758 
    759         mAmWmState.assertVisibility(activity, true /* visible */);
    760         assertEquals("Split-screen apps shouldn't influence device orientation",
    761                 orientation, mAmWmState.getWmState().getRotation());
    762 
    763         getLaunchActivityBuilder().setMultipleTask(true).setTargetActivity(activity).execute();
    764         mAmWmState.computeState(activity);
    765         mAmWmState.assertVisibility(activity, true /* visible */);
    766         assertEquals("Split-screen apps shouldn't influence device orientation",
    767                 orientation, mAmWmState.getWmState().getRotation());
    768     }
    769 
    770     /**
    771      * Launches activity in docked stack, moves to fullscreen stack and back to docked stack.
    772      * Asserts that initial and final reported sizes in docked stack are the same.
    773      */
    774     private void moveActivitySplitFullSplit(ComponentName activityName) {
    775         assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
    776 
    777         // Launch to docked stack and record size.
    778         LogSeparator logSeparator = separateLogs();
    779         launchActivityInSplitScreenWithRecents(activityName);
    780         final ReportedSizes initialDockedSizes = getActivityDisplaySize(activityName, logSeparator);
    781         // Make sure docked stack is focused. This way when we dismiss it later fullscreen stack
    782         // will come up.
    783         launchActivity(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    784         mAmWmState.computeState(false /* compareTaskAndStackBounds */,
    785                 new WaitForValidActivityState.Builder(activityName).build());
    786 
    787         // Move to fullscreen stack.
    788         logSeparator = separateLogs();
    789         setActivityTaskWindowingMode(
    790                 activityName, WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
    791         final ReportedSizes fullscreenSizes = getActivityDisplaySize(activityName, logSeparator);
    792         assertSizesAreSane(fullscreenSizes, initialDockedSizes);
    793 
    794         // Move activity back to docked stack.
    795         logSeparator = separateLogs();
    796         setActivityTaskWindowingMode(activityName, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
    797         final ReportedSizes finalDockedSizes = getActivityDisplaySize(activityName, logSeparator);
    798 
    799         // After activity configuration was changed twice it must report same size as original one.
    800         assertSizesAreSame(initialDockedSizes, finalDockedSizes);
    801     }
    802 
    803     /**
    804      * Asserts that after rotation, the aspect ratios of display size, metrics, and configuration
    805      * have flipped.
    806      */
    807     private static void assertSizesRotate(ReportedSizes rotationA, ReportedSizes rotationB,
    808             boolean skipOrientationCheck) {
    809         assertEquals(rotationA.displayWidth, rotationA.metricsWidth);
    810         assertEquals(rotationA.displayHeight, rotationA.metricsHeight);
    811         assertEquals(rotationB.displayWidth, rotationB.metricsWidth);
    812         assertEquals(rotationB.displayHeight, rotationB.metricsHeight);
    813 
    814         if (skipOrientationCheck) {
    815             // All done if we are not doing orientation check.
    816             return;
    817         }
    818         final boolean beforePortrait = rotationA.displayWidth < rotationA.displayHeight;
    819         final boolean afterPortrait = rotationB.displayWidth < rotationB.displayHeight;
    820         assertFalse(beforePortrait == afterPortrait);
    821 
    822         final boolean beforeConfigPortrait = rotationA.widthDp < rotationA.heightDp;
    823         final boolean afterConfigPortrait = rotationB.widthDp < rotationB.heightDp;
    824         assertEquals(beforePortrait, beforeConfigPortrait);
    825         assertEquals(afterPortrait, afterConfigPortrait);
    826 
    827         assertEquals(rotationA.smallestWidthDp, rotationB.smallestWidthDp);
    828     }
    829 
    830     /**
    831      * Throws an AssertionError if fullscreenSizes has widths/heights (depending on aspect ratio)
    832      * that are smaller than the dockedSizes.
    833      */
    834     private static void assertSizesAreSane(ReportedSizes fullscreenSizes, ReportedSizes dockedSizes)
    835     {
    836         final boolean portrait = fullscreenSizes.displayWidth < fullscreenSizes.displayHeight;
    837         if (portrait) {
    838             assertThat(dockedSizes.displayHeight, lessThan(fullscreenSizes.displayHeight));
    839             assertThat(dockedSizes.heightDp, lessThan(fullscreenSizes.heightDp));
    840             assertThat(dockedSizes.metricsHeight, lessThan(fullscreenSizes.metricsHeight));
    841         } else {
    842             assertThat(dockedSizes.displayWidth, lessThan(fullscreenSizes.displayWidth));
    843             assertThat(dockedSizes.widthDp, lessThan(fullscreenSizes.widthDp));
    844             assertThat(dockedSizes.metricsWidth, lessThan(fullscreenSizes.metricsWidth));
    845         }
    846     }
    847 
    848     /**
    849      * Throws an AssertionError if sizes are different.
    850      */
    851     private static void assertSizesAreSame(ReportedSizes firstSize, ReportedSizes secondSize) {
    852         assertEquals(firstSize.widthDp, secondSize.widthDp);
    853         assertEquals(firstSize.heightDp, secondSize.heightDp);
    854         assertEquals(firstSize.displayWidth, secondSize.displayWidth);
    855         assertEquals(firstSize.displayHeight, secondSize.displayHeight);
    856         assertEquals(firstSize.metricsWidth, secondSize.metricsWidth);
    857         assertEquals(firstSize.metricsHeight, secondSize.metricsHeight);
    858         assertEquals(firstSize.smallestWidthDp, secondSize.smallestWidthDp);
    859     }
    860 
    861     private ReportedSizes getActivityDisplaySize(ComponentName activityName,
    862             LogSeparator logSeparator) {
    863         mAmWmState.computeState(false /* compareTaskAndStackBounds */,
    864                 new WaitForValidActivityState(activityName));
    865         final ReportedSizes details = getLastReportedSizesForActivity(activityName, logSeparator);
    866         assertNotNull(details);
    867         return details;
    868     }
    869 
    870     private Rect getDisplayRect(ComponentName activityName) {
    871         final String windowName = getWindowName(activityName);
    872 
    873         mAmWmState.computeState(activityName);
    874         mAmWmState.assertFocusedWindow("Test window must be the front window.", windowName);
    875 
    876         final List<WindowManagerState.WindowState> windowList =
    877                 mAmWmState.getWmState().getMatchingVisibleWindowState(windowName);
    878 
    879         assertEquals("Should have exactly one window state for the activity.", 1,
    880                 windowList.size());
    881 
    882         WindowManagerState.WindowState windowState = windowList.get(0);
    883         assertNotNull("Should have a valid window", windowState);
    884 
    885         WindowManagerState.Display display = mAmWmState.getWmState()
    886                 .getDisplay(windowState.getDisplayId());
    887         assertNotNull("Should be on a display", display);
    888 
    889         return display.getDisplayRect();
    890     }
    891 
    892     /**
    893      * Test launching an activity which requests specific UI mode during creation.
    894      */
    895     @Test
    896     public void testLaunchWithUiModeChange() {
    897         // Launch activity that changes UI mode and handles this configuration change.
    898         launchActivity(NIGHT_MODE_ACTIVITY);
    899         mAmWmState.waitForActivityState(NIGHT_MODE_ACTIVITY, STATE_RESUMED);
    900 
    901         // Check if activity is launched successfully.
    902         mAmWmState.assertVisibility(NIGHT_MODE_ACTIVITY, true /* visible */);
    903         mAmWmState.assertFocusedActivity("Launched activity should be focused",
    904                 NIGHT_MODE_ACTIVITY);
    905         mAmWmState.assertResumedActivity("Launched activity must be resumed", NIGHT_MODE_ACTIVITY);
    906     }
    907 }
    908