• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.res.Configuration.UI_MODE_TYPE_DESK;
20 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
21 import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL;
22 import static android.server.wm.app.Components.TEST_ACTIVITY;
23 import static android.server.wm.deskresources.Components.DESK_RESOURCES_ACTIVITY;
24 
25 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
26 
27 import static org.junit.Assume.assumeTrue;
28 
29 import android.content.ComponentName;
30 import android.content.Intent;
31 import android.content.res.Configuration;
32 import android.content.res.Resources;
33 import android.platform.test.annotations.Presubmit;
34 import android.view.Surface;
35 
36 import org.junit.Test;
37 
38 /**
39  * Build/Install/Run:
40  * atest CtsWindowManagerDeviceTestCases:DockConfigChangeTests
41  */
42 @Presubmit
43 public class DockConfigChangeTests extends ActivityManagerTestBase {
44 
45     @Test
testDeskMode_noConfigChange()46     public void testDeskMode_noConfigChange() {
47         // Test only applies to behavior when the config_skipActivityRelaunchWhenDocking flag is
48         // enabled.
49         assumeTrue(getConfigSkipRelaunchOnDock());
50 
51         RotationSession rotationSession = createManagedRotationSession();
52 
53         launchActivity(TEST_ACTIVITY);
54         waitAndAssertResumedActivity(TEST_ACTIVITY, "Activity must be resumed");
55 
56         // Set rotation to the same rotation as the device would be rotated to after docking. This
57         // prevents an extraneous config change from the device rotating when docked/undocked.
58         rotateToDockRotation(rotationSession);
59         waitAndAssertResumedActivity(TEST_ACTIVITY, "Activity must be resumed");
60         separateTestJournal();
61 
62         final DockTestSession dockTestSession = mObjectTracker.manage(new DockTestSession());
63 
64         // Dock the device.
65         dockTestSession.dock();
66         dockTestSession.waitForDeskUiMode(TEST_ACTIVITY);
67 
68         // The activity receives a configuration change instead of relaunching.
69         assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */, 1 /* numConfigChange */);
70 
71         // Undock the device.
72         separateTestJournal();
73         dockTestSession.undock();
74         dockTestSession.waitForNormalUiMode(TEST_ACTIVITY);
75 
76         // The activity receives another configuration change.
77         assertRelaunchOrConfigChanged(TEST_ACTIVITY, 0 /* numRelaunch */, 1 /* numConfigChange */);
78     }
79 
80     @Test
testDeskMode_hasDeskResources_relaunches()81     public void testDeskMode_hasDeskResources_relaunches() {
82         // Test only applies to behavior when the config_skipActivityRelaunchWhenDocking flag is
83         // enabled.
84         assumeTrue(getConfigSkipRelaunchOnDock());
85 
86         RotationSession rotationSession = createManagedRotationSession();
87 
88         launchActivity(DESK_RESOURCES_ACTIVITY);
89         waitAndAssertResumedActivity(DESK_RESOURCES_ACTIVITY, "Activity must be resumed");
90 
91         // Set rotation to the same rotation as the device would be rotated to after docking. This
92         // prevents an extraneous config change from the device rotating when docked/undocked.
93         rotateToDockRotation(rotationSession);
94         waitAndAssertResumedActivity(DESK_RESOURCES_ACTIVITY, "Activity must be resumed");
95         separateTestJournal();
96 
97         final DockTestSession dockTestSession = mObjectTracker.manage(new DockTestSession());
98 
99         // Dock the device.
100         dockTestSession.dock();
101         dockTestSession.waitForDeskUiMode(DESK_RESOURCES_ACTIVITY);
102 
103         // The activity is relaunched since the app has -desk resources.
104         assertRelaunchOrConfigChanged(DESK_RESOURCES_ACTIVITY, 1 /* numRelaunch */,
105                 0 /* numConfigChange */);
106 
107         // Undock the device.
108         separateTestJournal();
109         dockTestSession.undock();
110         dockTestSession.waitForNormalUiMode(DESK_RESOURCES_ACTIVITY);
111 
112         // The activity is relaunched again.
113         assertRelaunchOrConfigChanged(DESK_RESOURCES_ACTIVITY, 1 /* numRelaunch */,
114                 0 /* numConfigChange */);
115     }
116 
getConfigSkipRelaunchOnDock()117     boolean getConfigSkipRelaunchOnDock() {
118         return mContext.getResources().getBoolean(
119                 Resources.getSystem().getIdentifier("config_skipActivityRelaunchWhenDocking",
120                         "bool", "android"));
121     }
122 
123     /**
124      * Rotates the device to the same rotation as it would rotate to when docked.
125      *
126      * Dock rotation is read from config_deskDockRotation.
127      */
rotateToDockRotation(RotationSession rotationSession)128     void rotateToDockRotation(RotationSession rotationSession) {
129         int rotation = rotationDegreesToConst(mContext.getResources().getInteger(
130                 Resources.getSystem().getIdentifier("config_deskDockRotation",
131                         "integer", "android")));
132         if (rotation == -1) {
133             // -1 could come from the const itself, which means no rotation on dock, or from
134             // rotationDegreesToConst, which means we got an unexpected value from the resource.
135             return;
136         }
137         rotationSession.set(rotation);
138     }
139 
140     /**
141      * Converts from a rotation in degrees to a {@link Surface.Rotation} constant.
142      *
143      * Returns -1 if a value that doesn't match a {@link Surface.Rotation} constant is provided.
144      */
rotationDegreesToConst(int rotationDegrees)145     private int rotationDegreesToConst(int rotationDegrees) {
146         switch (rotationDegrees) {
147             case 0:
148                 return Surface.ROTATION_0;
149             case 90:
150                 return Surface.ROTATION_90;
151             case 180:
152                 return Surface.ROTATION_180;
153             case 270:
154                 return Surface.ROTATION_270;
155         }
156         return -1;
157     }
158 
159 
160     private class DockTestSession implements AutoCloseable {
dock()161         private void dock() {
162             runShellCommand("dumpsys DockObserver set state "
163                     + Intent.EXTRA_DOCK_STATE_HE_DESK);
164         }
165 
undock()166         private void undock() {
167             runShellCommand("dumpsys DockObserver set state "
168                     + Intent.EXTRA_DOCK_STATE_UNDOCKED);
169         }
170 
waitForDeskUiMode(ComponentName activity)171         private void waitForDeskUiMode(ComponentName activity) {
172             waitForUiMode(activity, UI_MODE_TYPE_DESK);
173         }
174 
waitForNormalUiMode(ComponentName activity)175         private void waitForNormalUiMode(ComponentName activity) {
176             waitForUiMode(activity, UI_MODE_TYPE_NORMAL);
177         }
178 
179         /**
180          * Waits until the activity has received the expected uiMode in a configuration change.
181          */
waitForUiMode(ComponentName activity, int expectedUiMode)182         private void waitForUiMode(ComponentName activity, int expectedUiMode) {
183             mWmState.waitFor(state -> {
184                 int actualUiMode = state.getActivity(activity).getUiMode();
185                 return (actualUiMode & UI_MODE_TYPE_MASK) == actualUiMode;
186             }, "Didn't enter expected uiMode in time: " + expectedUiMode);
187         }
188 
189         @Override
close()190         public void close() throws Exception {
191             runShellCommand("dumpsys DockObserver reset");
192         }
193     }
194 }
195