1 /* 2 * Copyright (C) 2020 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.server.wm.app.Components.HIDE_OVERLAY_WINDOWS_ACTIVITY; 20 import static android.server.wm.app.Components.HideOverlayWindowsActivity.ACTION; 21 import static android.server.wm.app.Components.HideOverlayWindowsActivity.MOTION_EVENT_EXTRA; 22 import static android.server.wm.app.Components.HideOverlayWindowsActivity.PONG; 23 import static android.server.wm.app.Components.HideOverlayWindowsActivity.REPORT_TOUCH; 24 import static android.view.Gravity.LEFT; 25 import static android.view.Gravity.TOP; 26 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import android.Manifest; 31 import android.app.Activity; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.graphics.Color; 38 import android.graphics.Point; 39 import android.os.Bundle; 40 import android.os.ConditionVariable; 41 import android.platform.test.annotations.Presubmit; 42 import android.server.wm.app.Components; 43 import android.view.Display; 44 import android.view.MotionEvent; 45 import android.view.WindowManager; 46 import android.widget.TextView; 47 48 import androidx.annotation.Nullable; 49 50 import com.android.compatibility.common.util.SystemUtil; 51 52 import org.junit.After; 53 import org.junit.Before; 54 import org.junit.Test; 55 56 /** 57 * Build/Install/Run: 58 * atest CtsWindowManagerDeviceTestCases:HideOverlayWindowsTest 59 */ 60 @Presubmit 61 public class HideOverlayWindowsTest extends ActivityManagerTestBase { 62 63 private final static String WINDOW_NAME_EXTRA = "window_name"; 64 private final static String SYSTEM_APPLICATION_OVERLAY_EXTRA = "system_application_overlay"; 65 private PongReceiver mPongReceiver; 66 private TouchReceiver mTouchReceiver; 67 68 @Before 69 @Override setUp()70 public void setUp() throws Exception { 71 super.setUp(); 72 mPongReceiver = new PongReceiver(); 73 mContext.registerReceiver(mPongReceiver, new IntentFilter(PONG), Context.RECEIVER_EXPORTED); 74 mTouchReceiver = new TouchReceiver(); 75 mContext.registerReceiver(mTouchReceiver, new IntentFilter(REPORT_TOUCH), 76 Context.RECEIVER_EXPORTED); 77 } 78 79 @After tearDown()80 public void tearDown() throws Exception { 81 mContext.unregisterReceiver(mPongReceiver); 82 mContext.unregisterReceiver(mTouchReceiver); 83 } 84 85 @Test testApplicationOverlayHiddenWhenRequested()86 public void testApplicationOverlayHiddenWhenRequested() { 87 String windowName = "SYSTEM_ALERT_WINDOW"; 88 ComponentName componentName = new ComponentName( 89 mContext, SystemWindowActivity.class); 90 91 SystemUtil.runWithShellPermissionIdentity(() -> { 92 launchActivity(componentName, 93 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName)); 94 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 95 }, Manifest.permission.SYSTEM_ALERT_WINDOW); 96 97 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 98 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 99 100 setHideOverlayWindowsAndWaitForPong(true); 101 mWmState.waitAndAssertWindowSurfaceShown(windowName, false); 102 103 setHideOverlayWindowsAndWaitForPong(false); 104 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 105 } 106 107 @Test testSystemApplicationOverlayFlagNoEffectWithoutPermission()108 public void testSystemApplicationOverlayFlagNoEffectWithoutPermission() { 109 String windowName = "SYSTEM_ALERT_WINDOW"; 110 ComponentName componentName = new ComponentName( 111 mContext, SystemWindowActivity.class); 112 113 SystemUtil.runWithShellPermissionIdentity(() -> { 114 launchActivity(componentName, 115 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName), 116 CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true)); 117 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 118 }, Manifest.permission.SYSTEM_ALERT_WINDOW); 119 120 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 121 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 122 123 setHideOverlayWindowsAndWaitForPong(true); 124 mWmState.waitAndAssertWindowSurfaceShown(windowName, false); 125 126 setHideOverlayWindowsAndWaitForPong(false); 127 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 128 } 129 130 @Test testInternalSystemApplicationOverlaysNotHidden()131 public void testInternalSystemApplicationOverlaysNotHidden() { 132 String windowName = "INTERNAL_SYSTEM_WINDOW"; 133 ComponentName componentName = new ComponentName( 134 mContext, InternalSystemWindowActivity.class); 135 136 SystemUtil.runWithShellPermissionIdentity(() -> { 137 launchActivity(componentName, 138 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName)); 139 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 140 }, Manifest.permission.INTERNAL_SYSTEM_WINDOW); 141 142 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 143 setHideOverlayWindowsAndWaitForPong(true); 144 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 145 } 146 147 @Test testSystemApplicationOverlaysNotHidden()148 public void testSystemApplicationOverlaysNotHidden() { 149 String windowName = "SYSTEM_APPLICATION_OVERLAY"; 150 ComponentName componentName = new ComponentName( 151 mContext, SystemApplicationOverlayActivity.class); 152 SystemUtil.runWithShellPermissionIdentity(() -> { 153 launchActivity(componentName, 154 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName), 155 CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true)); 156 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 157 }, Manifest.permission.SYSTEM_APPLICATION_OVERLAY); 158 159 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 160 setHideOverlayWindowsAndWaitForPong(true); 161 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 162 } 163 164 @Test testSystemApplicationOverlayHiddenWithoutFlag()165 public void testSystemApplicationOverlayHiddenWithoutFlag() { 166 String windowName = "SYSTEM_APPLICATION_OVERLAY"; 167 ComponentName componentName = new ComponentName( 168 mContext, SystemApplicationOverlayActivity.class); 169 SystemUtil.runWithShellPermissionIdentity(() -> { 170 launchActivity(componentName, 171 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName)); 172 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 173 }, Manifest.permission.SYSTEM_APPLICATION_OVERLAY); 174 175 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 176 setHideOverlayWindowsAndWaitForPong(true); 177 mWmState.waitAndAssertWindowSurfaceShown(windowName, false); 178 } 179 180 @Test testSystemApplicationOverlayAllowsTouchWithoutObscured()181 public void testSystemApplicationOverlayAllowsTouchWithoutObscured() { 182 String windowName = "SYSTEM_APPLICATION_OVERLAY"; 183 ComponentName componentName = new ComponentName( 184 mContext, SystemApplicationOverlayActivity.class); 185 SystemUtil.runWithShellPermissionIdentity(() -> { 186 launchActivity(componentName, 187 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName), 188 CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true)); 189 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 190 }, Manifest.permission.SYSTEM_APPLICATION_OVERLAY); 191 192 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 193 setHideOverlayWindowsAndWaitForPong(true); 194 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 195 196 MotionEvent motionEvent = touchCenterOfDisplayAndWaitForMotionEvent(); 197 198 assertThat( 199 motionEvent.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED).isEqualTo( 200 0); 201 } 202 203 @Test testApplicationOverlay_touchIsObscuredWithoutCorrectPermission()204 public void testApplicationOverlay_touchIsObscuredWithoutCorrectPermission() { 205 String windowName = "SYSTEM_APPLICATION_OVERLAY"; 206 ComponentName componentName = new ComponentName( 207 mContext, SystemWindowActivity.class); 208 SystemUtil.runWithShellPermissionIdentity(() -> { 209 launchActivity(componentName, 210 CliIntentExtra.extraString(WINDOW_NAME_EXTRA, windowName), 211 CliIntentExtra.extraBool(SYSTEM_APPLICATION_OVERLAY_EXTRA, true)); 212 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 213 }, Manifest.permission.SYSTEM_ALERT_WINDOW); 214 215 launchActivity(HIDE_OVERLAY_WINDOWS_ACTIVITY); 216 setHideOverlayWindowsAndWaitForPong(false); 217 mWmState.waitAndAssertWindowSurfaceShown(windowName, true); 218 219 MotionEvent motionEvent = touchCenterOfDisplayAndWaitForMotionEvent(); 220 assertThat( 221 motionEvent.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED).isEqualTo( 222 MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED); 223 } 224 touchCenterOfDisplayAndWaitForMotionEvent()225 private MotionEvent touchCenterOfDisplayAndWaitForMotionEvent() { 226 mTouchHelper.tapOnDisplayCenter(Display.DEFAULT_DISPLAY); 227 return mTouchReceiver.getMotionEvent(); 228 } 229 setHideOverlayWindowsAndWaitForPong(boolean hide)230 void setHideOverlayWindowsAndWaitForPong(boolean hide) { 231 Intent intent = new Intent(ACTION); 232 intent.putExtra(Components.HideOverlayWindowsActivity.SHOULD_HIDE, hide); 233 mContext.sendBroadcast(intent); 234 mPongReceiver.waitForPong(); 235 } 236 237 public static class BaseSystemWindowActivity extends Activity { 238 239 TextView mTextView; 240 241 @Override onCreate(@ullable Bundle savedInstanceState)242 protected void onCreate(@Nullable Bundle savedInstanceState) { 243 super.onCreate(savedInstanceState); 244 String windowName = getIntent().getStringExtra(WINDOW_NAME_EXTRA); 245 246 final Point size = new Point(); 247 getDisplay().getRealSize(size); 248 249 WindowManager.LayoutParams params = 250 new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY, 251 WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); 252 params.width = size.x / 3; 253 params.height = size.y / 3; 254 params.gravity = TOP | LEFT; 255 params.setTitle(windowName); 256 params.setSystemApplicationOverlay( 257 getIntent().getBooleanExtra(SYSTEM_APPLICATION_OVERLAY_EXTRA, false)); 258 259 mTextView = new TextView(this); 260 mTextView.setText(windowName + " type=" + TYPE_APPLICATION_OVERLAY); 261 mTextView.setBackgroundColor(Color.GREEN); 262 263 getWindowManager().addView(mTextView, params); 264 } 265 266 @Override onDestroy()267 protected void onDestroy() { 268 super.onDestroy(); 269 getWindowManager().removeView(mTextView); 270 } 271 } 272 273 // These activities are running the same code, but in different processes to ensure that they 274 // each create their own WindowSession, using the correct permissions. If they are run in the 275 // same process WindowSession is cached and might end up not matching the permissions set up 276 // with adoptShellPermissions 277 public static class InternalSystemWindowActivity extends BaseSystemWindowActivity {} 278 public static class SystemApplicationOverlayActivity extends BaseSystemWindowActivity {} 279 public static class SystemWindowActivity extends BaseSystemWindowActivity {} 280 281 private static class PongReceiver extends BroadcastReceiver { 282 283 volatile ConditionVariable mConditionVariable = new ConditionVariable(); 284 285 @Override onReceive(Context context, Intent intent)286 public void onReceive(Context context, Intent intent) { 287 mConditionVariable.open(); 288 } 289 waitForPong()290 public void waitForPong() { 291 assertThat(mConditionVariable.block(10000L)).isTrue(); 292 mConditionVariable = new ConditionVariable(); 293 } 294 } 295 296 private static class TouchReceiver extends BroadcastReceiver { 297 298 volatile ConditionVariable mConditionVariable = new ConditionVariable(); 299 MotionEvent mMotionEvent; 300 301 @Override onReceive(Context context, Intent intent)302 public void onReceive(Context context, Intent intent) { 303 mMotionEvent = intent.getParcelableExtra(MOTION_EVENT_EXTRA, MotionEvent.class); 304 mConditionVariable.open(); 305 } 306 getMotionEvent()307 public MotionEvent getMotionEvent() { 308 assertThat(mConditionVariable.block(10000L)).isTrue(); 309 mConditionVariable = new ConditionVariable(); 310 return mMotionEvent; 311 } 312 } 313 314 } 315