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.app.AppOpsManager.MODE_ALLOWED;
20 import static android.app.AppOpsManager.MODE_ERRORED;
21 import static android.app.AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW;
22 import static android.server.wm.alertwindowapp.Components.ALERT_WINDOW_TEST_ACTIVITY;
23 import static android.server.wm.alertwindowappsdk25.Components.SDK25_ALERT_WINDOW_TEST_ACTIVITY;
24 
25 import static org.hamcrest.MatcherAssert.assertThat;
26 import static org.hamcrest.Matchers.empty;
27 import static org.hamcrest.Matchers.greaterThan;
28 import static org.hamcrest.Matchers.hasSize;
29 import static org.hamcrest.Matchers.lessThan;
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertNotNull;
32 import static org.junit.Assert.assertTrue;
33 
34 import android.content.ComponentName;
35 import android.platform.test.annotations.AppModeFull;
36 import android.platform.test.annotations.Presubmit;
37 
38 import com.android.compatibility.common.util.AppOpsUtils;
39 
40 import org.junit.After;
41 import org.junit.Before;
42 import org.junit.Test;
43 
44 import java.util.List;
45 
46 /**
47  * Build/Install/Run:
48  *     atest CtsWindowManagerDeviceTestCases:AlertWindowsTests
49  */
50 @Presubmit
51 @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS")
52 public class AlertWindowsTests extends ActivityManagerTestBase {
53 
54     // From WindowManager.java
55     private static final int TYPE_BASE_APPLICATION      = 1;
56     private static final int FIRST_SYSTEM_WINDOW        = 2000;
57 
58     private static final int TYPE_PHONE                 = FIRST_SYSTEM_WINDOW + 2;
59     private static final int TYPE_SYSTEM_ALERT          = FIRST_SYSTEM_WINDOW + 3;
60     private static final int TYPE_SYSTEM_OVERLAY        = FIRST_SYSTEM_WINDOW + 6;
61     private static final int TYPE_PRIORITY_PHONE        = FIRST_SYSTEM_WINDOW + 7;
62     private static final int TYPE_SYSTEM_ERROR          = FIRST_SYSTEM_WINDOW + 10;
63     private static final int TYPE_APPLICATION_OVERLAY   = FIRST_SYSTEM_WINDOW + 38;
64 
65     private static final int TYPE_STATUS_BAR            = FIRST_SYSTEM_WINDOW;
66     private static final int TYPE_INPUT_METHOD          = FIRST_SYSTEM_WINDOW + 11;
67     private static final int TYPE_NAVIGATION_BAR        = FIRST_SYSTEM_WINDOW + 19;
68 
69     private static final int[] ALERT_WINDOW_TYPES = {
70             TYPE_PHONE,
71             TYPE_PRIORITY_PHONE,
72             TYPE_SYSTEM_ALERT,
73             TYPE_SYSTEM_ERROR,
74             TYPE_SYSTEM_OVERLAY,
75             TYPE_APPLICATION_OVERLAY
76     };
77     private static final int[] SYSTEM_WINDOW_TYPES = {
78             TYPE_STATUS_BAR,
79             TYPE_INPUT_METHOD,
80             TYPE_NAVIGATION_BAR
81     };
82 
83     @Before
84     @Override
setUp()85     public void setUp() throws Exception {
86         super.setUp();
87         resetPermissionState(ALERT_WINDOW_TEST_ACTIVITY);
88         resetPermissionState(SDK25_ALERT_WINDOW_TEST_ACTIVITY);
89     }
90 
91     @After
tearDown()92     public void tearDown() throws Exception {
93         resetPermissionState(ALERT_WINDOW_TEST_ACTIVITY);
94         resetPermissionState(SDK25_ALERT_WINDOW_TEST_ACTIVITY);
95         stopTestPackage(ALERT_WINDOW_TEST_ACTIVITY.getPackageName());
96         stopTestPackage(SDK25_ALERT_WINDOW_TEST_ACTIVITY.getPackageName());
97     }
98 
99     @Test
testAlertWindowAllowed()100     public void testAlertWindowAllowed() throws Exception {
101         runAlertWindowTest(ALERT_WINDOW_TEST_ACTIVITY, true /* hasAlertWindowPermission */,
102                 true /* atLeastO */);
103     }
104 
105     @Test
testAlertWindowDisallowed()106     public void testAlertWindowDisallowed() throws Exception {
107         runAlertWindowTest(ALERT_WINDOW_TEST_ACTIVITY, false /* hasAlertWindowPermission */,
108                 true /* atLeastO */);
109     }
110 
111     @Test
testAlertWindowAllowedSdk25()112     public void testAlertWindowAllowedSdk25() throws Exception {
113         runAlertWindowTest(SDK25_ALERT_WINDOW_TEST_ACTIVITY, true /* hasAlertWindowPermission */,
114                 false /* atLeastO */);
115     }
116 
117     @Test
testAlertWindowDisallowedSdk25()118     public void testAlertWindowDisallowedSdk25() throws Exception {
119         runAlertWindowTest(SDK25_ALERT_WINDOW_TEST_ACTIVITY, false /* hasAlertWindowPermission */,
120                 false /* atLeastO */);
121     }
122 
runAlertWindowTest(final ComponentName activityName, final boolean hasAlertWindowPermission, final boolean atLeastO)123     private void runAlertWindowTest(final ComponentName activityName,
124             final boolean hasAlertWindowPermission, final boolean atLeastO) throws Exception {
125         setAlertWindowPermission(activityName, hasAlertWindowPermission);
126 
127         executeShellCommand(getAmStartCmd(activityName));
128         mWmState.computeState(new WaitForValidActivityState(activityName));
129         mWmState.assertVisibility(activityName, true);
130 
131         assertAlertWindows(activityName, hasAlertWindowPermission, atLeastO);
132     }
133 
allWindowsHidden(List<WindowManagerState.WindowState> windows)134     private boolean allWindowsHidden(List<WindowManagerState.WindowState> windows) {
135         for (WindowManagerState.WindowState ws : windows) {
136             if (ws.isSurfaceShown()) {
137                 return false;
138             }
139         }
140         return true;
141     }
142 
assertAlertWindows(final ComponentName activityName, final boolean hasAlertWindowPermission, final boolean atLeastO)143     private void assertAlertWindows(final ComponentName activityName,
144             final boolean hasAlertWindowPermission, final boolean atLeastO) throws Exception {
145         final String packageName = activityName.getPackageName();
146         final WindowManagerState wmState = mWmState;
147 
148         final List<WindowManagerState.WindowState> alertWindows =
149                 wmState.getWindowsByPackageName(packageName, ALERT_WINDOW_TYPES);
150 
151         if (!hasAlertWindowPermission) {
152             // When running in VR Mode, an App Op restriction is
153             // in place for SYSTEM_ALERT_WINDOW, which allows the window
154             // to be created, but will be hidden instead.
155             if (isUiModeLockedToVrHeadset()) {
156                 assertThat("Should not be empty alertWindows",
157                         alertWindows, hasSize(greaterThan(0)));
158                 assertTrue("All alert windows should be hidden",
159                         allWindowsHidden(alertWindows));
160             } else {
161                 assertThat("Should be empty alertWindows", alertWindows, empty());
162                 assertTrue(AppOpsUtils.rejectedOperationLogged(packageName,
163                         OPSTR_SYSTEM_ALERT_WINDOW));
164                 return;
165             }
166         }
167 
168         if (atLeastO) {
169             // Assert that only TYPE_APPLICATION_OVERLAY was created.
170             for (WindowManagerState.WindowState win : alertWindows) {
171                 assertEquals("Can't create win=" + win + " on SDK O or greater",
172                         win.getType(), TYPE_APPLICATION_OVERLAY);
173             }
174         }
175 
176         final WindowManagerState.WindowState mainAppWindow =
177                 wmState.getWindowByPackageName(packageName, TYPE_BASE_APPLICATION);
178 
179         assertNotNull(mainAppWindow);
180 
181         final WindowManagerState.WindowState lowestAlertWindow = alertWindows.get(0);
182         final WindowManagerState.WindowState highestAlertWindow =
183                 alertWindows.get(alertWindows.size() - 1);
184 
185         // Assert that the alert windows have higher z-order than the main app window
186         assertThat("lowestAlertWindow has higher z-order than mainAppWindow",
187                 wmState.getZOrder(lowestAlertWindow),
188                 greaterThan(wmState.getZOrder(mainAppWindow)));
189 
190         // Assert that legacy alert windows have a lower z-order than the new alert window layer.
191         final WindowManagerState.WindowState appOverlayWindow =
192                 wmState.getWindowByPackageName(packageName, TYPE_APPLICATION_OVERLAY);
193         if (appOverlayWindow != null && highestAlertWindow != appOverlayWindow) {
194             assertThat("highestAlertWindow has lower z-order than appOverlayWindow",
195                     wmState.getZOrder(highestAlertWindow),
196                     lessThan(wmState.getZOrder(appOverlayWindow)));
197         }
198 
199         // Assert that alert windows are below key system windows.
200         final List<WindowManagerState.WindowState> systemWindows =
201                 wmState.getWindowsByPackageName(packageName, SYSTEM_WINDOW_TYPES);
202         if (!systemWindows.isEmpty()) {
203             final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0);
204             assertThat("highestAlertWindow has lower z-order than lowestSystemWindow",
205                     wmState.getZOrder(highestAlertWindow),
206                     lessThan(wmState.getZOrder(lowestSystemWindow)));
207         }
208         assertTrue(AppOpsUtils.allowedOperationLogged(packageName, OPSTR_SYSTEM_ALERT_WINDOW));
209     }
210 
211     // Resets the permission states for a package to the system defaults.
212     // Also clears the app operation logs for this package, required to test that displaying
213     // the alert window gets logged.
resetPermissionState(ComponentName activityName)214     private void resetPermissionState(ComponentName activityName) throws Exception {
215         AppOpsUtils.reset(activityName.getPackageName());
216     }
217 
setAlertWindowPermission(final ComponentName activityName, final boolean allow)218     private void setAlertWindowPermission(final ComponentName activityName, final boolean allow)
219             throws Exception {
220         int mode = allow ? MODE_ALLOWED : MODE_ERRORED;
221         AppOpsUtils.setOpMode(activityName.getPackageName(), OPSTR_SYSTEM_ALERT_WINDOW, mode);
222     }
223 }
224