/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package android.server.wm; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_ERRORED; import static android.app.AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW; import static android.server.wm.alertwindowapp.Components.ALERT_WINDOW_TEST_ACTIVITY; import static android.server.wm.alertwindowappsdk25.Components.SDK25_ALERT_WINDOW_TEST_ACTIVITY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.content.ComponentName; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.Presubmit; import com.android.compatibility.common.util.AppOpsUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.util.List; /** * Build/Install/Run: * atest CtsWindowManagerDeviceTestCases:AlertWindowsTests */ @Presubmit @AppModeFull(reason = "Requires android.permission.MANAGE_ACTIVITY_TASKS") public class AlertWindowsTests extends ActivityManagerTestBase { // From WindowManager.java private static final int TYPE_BASE_APPLICATION = 1; private static final int FIRST_SYSTEM_WINDOW = 2000; private static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW + 2; private static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3; private static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6; private static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7; private static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10; private static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38; private static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW; private static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11; private static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19; private static final int[] ALERT_WINDOW_TYPES = { TYPE_PHONE, TYPE_PRIORITY_PHONE, TYPE_SYSTEM_ALERT, TYPE_SYSTEM_ERROR, TYPE_SYSTEM_OVERLAY, TYPE_APPLICATION_OVERLAY }; private static final int[] SYSTEM_WINDOW_TYPES = { TYPE_STATUS_BAR, TYPE_INPUT_METHOD, TYPE_NAVIGATION_BAR }; @Before @Override public void setUp() throws Exception { super.setUp(); resetPermissionState(ALERT_WINDOW_TEST_ACTIVITY); resetPermissionState(SDK25_ALERT_WINDOW_TEST_ACTIVITY); } @After public void tearDown() throws Exception { resetPermissionState(ALERT_WINDOW_TEST_ACTIVITY); resetPermissionState(SDK25_ALERT_WINDOW_TEST_ACTIVITY); stopTestPackage(ALERT_WINDOW_TEST_ACTIVITY.getPackageName()); stopTestPackage(SDK25_ALERT_WINDOW_TEST_ACTIVITY.getPackageName()); } @Test public void testAlertWindowAllowed() throws Exception { runAlertWindowTest(ALERT_WINDOW_TEST_ACTIVITY, true /* hasAlertWindowPermission */, true /* atLeastO */); } @Test public void testAlertWindowDisallowed() throws Exception { runAlertWindowTest(ALERT_WINDOW_TEST_ACTIVITY, false /* hasAlertWindowPermission */, true /* atLeastO */); } @Test public void testAlertWindowAllowedSdk25() throws Exception { runAlertWindowTest(SDK25_ALERT_WINDOW_TEST_ACTIVITY, true /* hasAlertWindowPermission */, false /* atLeastO */); } @Test public void testAlertWindowDisallowedSdk25() throws Exception { runAlertWindowTest(SDK25_ALERT_WINDOW_TEST_ACTIVITY, false /* hasAlertWindowPermission */, false /* atLeastO */); } private void runAlertWindowTest(final ComponentName activityName, final boolean hasAlertWindowPermission, final boolean atLeastO) throws Exception { setAlertWindowPermission(activityName, hasAlertWindowPermission); executeShellCommand(getAmStartCmd(activityName)); mWmState.computeState(new WaitForValidActivityState(activityName)); mWmState.assertVisibility(activityName, true); assertAlertWindows(activityName, hasAlertWindowPermission, atLeastO); } private boolean allWindowsHidden(List windows) { for (WindowManagerState.WindowState ws : windows) { if (ws.isSurfaceShown()) { return false; } } return true; } private void assertAlertWindows(final ComponentName activityName, final boolean hasAlertWindowPermission, final boolean atLeastO) throws Exception { final String packageName = activityName.getPackageName(); final WindowManagerState wmState = mWmState; final List alertWindows = wmState.getWindowsByPackageName(packageName, ALERT_WINDOW_TYPES); if (!hasAlertWindowPermission) { // When running in VR Mode, an App Op restriction is // in place for SYSTEM_ALERT_WINDOW, which allows the window // to be created, but will be hidden instead. if (isUiModeLockedToVrHeadset()) { assertThat("Should not be empty alertWindows", alertWindows, hasSize(greaterThan(0))); assertTrue("All alert windows should be hidden", allWindowsHidden(alertWindows)); } else { assertThat("Should be empty alertWindows", alertWindows, empty()); assertTrue(AppOpsUtils.rejectedOperationLogged(packageName, OPSTR_SYSTEM_ALERT_WINDOW)); return; } } if (atLeastO) { // Assert that only TYPE_APPLICATION_OVERLAY was created. for (WindowManagerState.WindowState win : alertWindows) { assertEquals("Can't create win=" + win + " on SDK O or greater", win.getType(), TYPE_APPLICATION_OVERLAY); } } final WindowManagerState.WindowState mainAppWindow = wmState.getWindowByPackageName(packageName, TYPE_BASE_APPLICATION); assertNotNull(mainAppWindow); final WindowManagerState.WindowState lowestAlertWindow = alertWindows.get(0); final WindowManagerState.WindowState highestAlertWindow = alertWindows.get(alertWindows.size() - 1); // Assert that the alert windows have higher z-order than the main app window assertThat("lowestAlertWindow has higher z-order than mainAppWindow", wmState.getZOrder(lowestAlertWindow), greaterThan(wmState.getZOrder(mainAppWindow))); // Assert that legacy alert windows have a lower z-order than the new alert window layer. final WindowManagerState.WindowState appOverlayWindow = wmState.getWindowByPackageName(packageName, TYPE_APPLICATION_OVERLAY); if (appOverlayWindow != null && highestAlertWindow != appOverlayWindow) { assertThat("highestAlertWindow has lower z-order than appOverlayWindow", wmState.getZOrder(highestAlertWindow), lessThan(wmState.getZOrder(appOverlayWindow))); } // Assert that alert windows are below key system windows. final List systemWindows = wmState.getWindowsByPackageName(packageName, SYSTEM_WINDOW_TYPES); if (!systemWindows.isEmpty()) { final WindowManagerState.WindowState lowestSystemWindow = alertWindows.get(0); assertThat("highestAlertWindow has lower z-order than lowestSystemWindow", wmState.getZOrder(highestAlertWindow), lessThan(wmState.getZOrder(lowestSystemWindow))); } assertTrue(AppOpsUtils.allowedOperationLogged(packageName, OPSTR_SYSTEM_ALERT_WINDOW)); } // Resets the permission states for a package to the system defaults. // Also clears the app operation logs for this package, required to test that displaying // the alert window gets logged. private void resetPermissionState(ComponentName activityName) throws Exception { AppOpsUtils.reset(activityName.getPackageName()); } private void setAlertWindowPermission(final ComponentName activityName, final boolean allow) throws Exception { int mode = allow ? MODE_ALLOWED : MODE_ERRORED; AppOpsUtils.setOpMode(activityName.getPackageName(), OPSTR_SYSTEM_ALERT_WINDOW, mode); } }