WindowTestsBase.java revision dee1b3f80c363fa6d3c9e87acd729161bce56c23
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wm; 18 19import android.app.ActivityManager.TaskDescription; 20import android.content.res.Configuration; 21import android.graphics.Rect; 22import android.hardware.display.DisplayManagerGlobal; 23import android.os.Binder; 24import android.view.Display; 25import android.view.DisplayInfo; 26import android.view.IApplicationToken; 27import org.junit.Assert; 28import org.junit.After; 29import org.junit.Before; 30import org.mockito.MockitoAnnotations; 31 32import android.app.ActivityManager.TaskSnapshot; 33import android.content.Context; 34import android.os.IBinder; 35import android.support.test.InstrumentationRegistry; 36import android.view.IWindow; 37import android.view.WindowManager; 38 39import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; 40import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 41import static android.app.AppOpsManager.OP_NONE; 42import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 43import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 44import static android.content.res.Configuration.EMPTY; 45import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; 46import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; 47import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 48import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; 49import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 50import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; 51import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 52import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 53import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 54import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 55import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 56import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 57import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 58import static com.android.server.wm.WindowContainer.POSITION_TOP; 59import static org.mockito.Mockito.mock; 60 61import com.android.server.AttributeCache; 62 63import java.util.HashSet; 64import java.util.LinkedList; 65 66/** 67 * Common base class for window manager unit test classes. 68 */ 69class WindowTestsBase { 70 static WindowManagerService sWm = null; 71 static TestWindowManagerPolicy sPolicy = null; 72 private final static Session sMockSession = mock(Session.class); 73 private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1; 74 static int sNextStackId = FIRST_DYNAMIC_STACK_ID; 75 private static int sNextTaskId = 0; 76 77 private static boolean sOneTimeSetupDone = false; 78 static DisplayContent sDisplayContent; 79 static DisplayInfo sDisplayInfo = new DisplayInfo(); 80 static WindowLayersController sLayersController; 81 static WindowState sWallpaperWindow; 82 static WindowState sImeWindow; 83 static WindowState sImeDialogWindow; 84 static WindowState sStatusBarWindow; 85 static WindowState sDockedDividerWindow; 86 static WindowState sNavBarWindow; 87 static WindowState sAppWindow; 88 static WindowState sChildAppWindowAbove; 89 static WindowState sChildAppWindowBelow; 90 static HashSet<WindowState> sCommonWindows; 91 92 @Before 93 public void setUp() throws Exception { 94 if (sOneTimeSetupDone) { 95 return; 96 } 97 sOneTimeSetupDone = true; 98 MockitoAnnotations.initMocks(this); 99 final Context context = InstrumentationRegistry.getTargetContext(); 100 AttributeCache.init(context); 101 sWm = TestWindowManagerPolicy.getWindowManagerService(context); 102 sPolicy = (TestWindowManagerPolicy) sWm.mPolicy; 103 sLayersController = new WindowLayersController(sWm); 104 sDisplayContent = sWm.mRoot.getDisplayContent(context.getDisplay().getDisplayId()); 105 if (sDisplayContent != null) { 106 sDisplayContent.removeImmediately(); 107 } 108 // Make sure that display ids don't overlap, so there won't be several displays with same 109 // ids among RootWindowContainer children. 110 for (DisplayContent dc : sWm.mRoot.mChildren) { 111 if (dc.getDisplayId() >= sNextDisplayId) { 112 sNextDisplayId = dc.getDisplayId() + 1; 113 } 114 } 115 context.getDisplay().getDisplayInfo(sDisplayInfo); 116 sDisplayContent = createNewDisplay(); 117 sWm.mDisplayEnabled = true; 118 sWm.mDisplayReady = true; 119 120 // Set-up some common windows. 121 sCommonWindows = new HashSet(); 122 sWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow"); 123 sImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "sImeWindow"); 124 sWm.mInputMethodWindow = sImeWindow; 125 sImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, "sImeDialogWindow"); 126 sStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "sStatusBarWindow"); 127 sNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "sNavBarWindow"); 128 sDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, "sDockedDividerWindow"); 129 sAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "sAppWindow"); 130 sChildAppWindowAbove = createCommonWindow(sAppWindow, TYPE_APPLICATION_ATTACHED_DIALOG, 131 "sChildAppWindowAbove"); 132 sChildAppWindowBelow = createCommonWindow(sAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY, 133 "sChildAppWindowBelow"); 134 } 135 136 @After 137 public void tearDown() throws Exception { 138 final LinkedList<WindowState> nonCommonWindows = new LinkedList(); 139 sWm.mRoot.forAllWindows(w -> { 140 if (!sCommonWindows.contains(w)) { 141 nonCommonWindows.addLast(w); 142 } 143 }, true /* traverseTopToBottom */); 144 145 while (!nonCommonWindows.isEmpty()) { 146 nonCommonWindows.pollLast().removeImmediately(); 147 } 148 149 sWm.mInputMethodTarget = null; 150 sWm.mInputMethodTargetCandidate = null; 151 } 152 153 private static WindowState createCommonWindow(WindowState parent, int type, String name) { 154 final WindowState win = createWindow(parent, type, name); 155 sCommonWindows.add(win); 156 // Prevent common windows from been IMe targets 157 win.mAttrs.flags |= FLAG_NOT_FOCUSABLE; 158 return win; 159 } 160 161 /** 162 * Creates a window for a task on a the given {@param stackId}. 163 */ 164 private WindowState createStackWindow(int stackId, String name) { 165 final StackWindowController stackController = createStackControllerOnStackOnDisplay(stackId, 166 sDisplayContent); 167 final TestTaskWindowContainerController taskController = 168 new TestTaskWindowContainerController(stackController); 169 TestAppWindowToken appWinToken = new TestAppWindowToken(sDisplayContent); 170 appWinToken.mTask = taskController.mContainer; 171 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, name); 172 win.mAppToken = appWinToken; 173 return win; 174 } 175 176 /** Asserts that the first entry is greater than the second entry. */ 177 void assertGreaterThan(int first, int second) throws Exception { 178 Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second); 179 } 180 181 /** 182 * Waits until the main handler for WM has processed all messages. 183 */ 184 void waitUntilHandlerIdle() { 185 sWm.mH.runWithScissors(() -> { }, 0); 186 } 187 188 private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) { 189 if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { 190 return new TestWindowToken(type, dc); 191 } 192 193 final TaskStack stack = stackId == INVALID_STACK_ID 194 ? createTaskStackOnDisplay(dc) 195 : createStackControllerOnStackOnDisplay(stackId, dc).mContainer; 196 final Task task = createTaskInStack(stack, 0 /* userId */); 197 final TestAppWindowToken token = new TestAppWindowToken(dc); 198 task.addChild(token, 0); 199 return token; 200 } 201 202 static WindowState createWindow(WindowState parent, int type, String name) { 203 return (parent == null) 204 ? createWindow(parent, type, sDisplayContent, name) 205 : createWindow(parent, type, parent.mToken, name); 206 } 207 208 static WindowState createWindowOnStack(WindowState parent, int stackId, int type, 209 DisplayContent dc, String name) { 210 final WindowToken token = createWindowToken(dc, stackId, type); 211 return createWindow(parent, type, token, name); 212 } 213 214 WindowState createAppWindow(Task task, int type, String name) { 215 final AppWindowToken token = new TestAppWindowToken(sDisplayContent); 216 task.addChild(token, 0); 217 return createWindow(null, type, token, name); 218 } 219 220 static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { 221 final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type); 222 return createWindow(parent, type, token, name); 223 } 224 225 static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, 226 boolean ownerCanAddInternalSystemWindow) { 227 final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type); 228 return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow); 229 } 230 231 static WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { 232 return createWindow(parent, type, token, name, false /* ownerCanAddInternalSystemWindow */); 233 } 234 235 static WindowState createWindow(WindowState parent, int type, WindowToken token, String name, 236 boolean ownerCanAddInternalSystemWindow) { 237 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); 238 attrs.setTitle(name); 239 240 final WindowState w = new WindowState(sWm, sMockSession, new TestIWindow(), token, parent, 241 OP_NONE, 0, attrs, 0, 0, ownerCanAddInternalSystemWindow); 242 // TODO: Probably better to make this call in the WindowState ctor to avoid errors with 243 // adding it to the token... 244 token.addWindow(w); 245 sWm.mWindowMap.put(w.mClient.asBinder(), w); 246 return w; 247 } 248 249 /** Creates a {@link TaskStack} and adds it to the specified {@link DisplayContent}. */ 250 static TaskStack createTaskStackOnDisplay(DisplayContent dc) { 251 return createStackControllerOnDisplay(dc).mContainer; 252 } 253 254 static StackWindowController createStackControllerOnDisplay(DisplayContent dc) { 255 final int stackId = ++sNextStackId; 256 return createStackControllerOnStackOnDisplay(stackId, dc); 257 } 258 259 static StackWindowController createStackControllerOnStackOnDisplay(int stackId, 260 DisplayContent dc) { 261 return new StackWindowController(stackId, null, dc.getDisplayId(), 262 true /* onTop */, new Rect(), sWm); 263 } 264 265 /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ 266 static Task createTaskInStack(TaskStack stack, int userId) { 267 final Task newTask = new Task(sNextTaskId++, stack, userId, sWm, null, EMPTY, 0, false, 268 false, new TaskDescription(), null); 269 stack.addTask(newTask, POSITION_TOP); 270 return newTask; 271 } 272 273 /** Creates a {@link DisplayContent} and adds it to the system. */ 274 DisplayContent createNewDisplay() { 275 final int displayId = sNextDisplayId++; 276 final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, 277 sDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); 278 return new DisplayContent(display, sWm, sLayersController, new WallpaperController(sWm)); 279 } 280 281 /* Used so we can gain access to some protected members of the {@link WindowToken} class */ 282 static class TestWindowToken extends WindowToken { 283 int adj = 0; 284 285 TestWindowToken(int type, DisplayContent dc) { 286 this(type, dc, false /* persistOnEmpty */); 287 } 288 289 TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { 290 super(sWm, mock(IBinder.class), type, persistOnEmpty, dc, 291 false /* ownerCanManageAppTokens */); 292 } 293 294 int getWindowsCount() { 295 return mChildren.size(); 296 } 297 298 boolean hasWindow(WindowState w) { 299 return mChildren.contains(w); 300 } 301 302 @Override 303 int getAnimLayerAdjustment() { 304 return adj; 305 } 306 } 307 308 /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ 309 static class TestAppWindowToken extends AppWindowToken { 310 311 TestAppWindowToken(DisplayContent dc) { 312 super(sWm, null, false, dc, true /* fillsParent */); 313 } 314 315 TestAppWindowToken(WindowManagerService service, IApplicationToken token, 316 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, 317 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, 318 int rotationAnimationHint, int configChanges, boolean launchTaskBehind, 319 boolean alwaysFocusable, AppWindowContainerController controller) { 320 super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, 321 showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, 322 launchTaskBehind, alwaysFocusable, controller); 323 } 324 325 int getWindowsCount() { 326 return mChildren.size(); 327 } 328 329 boolean hasWindow(WindowState w) { 330 return mChildren.contains(w); 331 } 332 333 WindowState getFirstChild() { 334 return mChildren.getFirst(); 335 } 336 337 WindowState getLastChild() { 338 return mChildren.getLast(); 339 } 340 341 int positionInParent() { 342 return getParent().mChildren.indexOf(this); 343 } 344 } 345 346 /* Used so we can gain access to some protected members of the {@link Task} class */ 347 class TestTask extends Task { 348 349 boolean mShouldDeferRemoval = false; 350 boolean mOnDisplayChangedCalled = false; 351 private boolean mUseLocalIsAnimating = false; 352 private boolean mIsAnimating = false; 353 354 TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, 355 Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, 356 boolean homeTask, TaskWindowContainerController controller) { 357 super(taskId, stack, userId, service, bounds, overrideConfig, resizeMode, 358 supportsPictureInPicture, homeTask, new TaskDescription(), controller); 359 } 360 361 boolean shouldDeferRemoval() { 362 return mShouldDeferRemoval; 363 } 364 365 int positionInParent() { 366 return getParent().mChildren.indexOf(this); 367 } 368 369 @Override 370 void onDisplayChanged(DisplayContent dc) { 371 super.onDisplayChanged(dc); 372 mOnDisplayChangedCalled = true; 373 } 374 375 @Override 376 boolean isAnimating() { 377 return mUseLocalIsAnimating ? mIsAnimating : super.isAnimating(); 378 } 379 380 void setLocalIsAnimating(boolean isAnimating) { 381 mUseLocalIsAnimating = true; 382 mIsAnimating = isAnimating; 383 } 384 } 385 386 /** 387 * Used so we can gain access to some protected members of {@link TaskWindowContainerController} 388 * class. 389 */ 390 class TestTaskWindowContainerController extends TaskWindowContainerController { 391 392 TestTaskWindowContainerController() { 393 this(createStackControllerOnDisplay(sDisplayContent)); 394 } 395 396 TestTaskWindowContainerController(StackWindowController stackController) { 397 super(sNextTaskId++, new TaskWindowContainerListener() { 398 @Override 399 public void onSnapshotChanged(TaskSnapshot snapshot) { 400 401 } 402 403 @Override 404 public void requestResize(Rect bounds, int resizeMode) { 405 406 } 407 }, stackController, 0 /* userId */, null /* bounds */, 408 EMPTY /* overrideConfig*/, RESIZE_MODE_UNRESIZEABLE, 409 false /* supportsPictureInPicture */, false /* homeTask*/, true /* toTop*/, 410 true /* showForAllUsers */, new TaskDescription(), sWm); 411 } 412 413 @Override 414 TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, 415 Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture, 416 boolean homeTask, TaskDescription taskDescription) { 417 return new TestTask(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode, 418 supportsPictureInPicture, homeTask, this); 419 } 420 } 421 422 class TestAppWindowContainerController extends AppWindowContainerController { 423 424 final IApplicationToken mToken; 425 426 TestAppWindowContainerController(TestTaskWindowContainerController taskController) { 427 this(taskController, new TestIApplicationToken()); 428 } 429 430 TestAppWindowContainerController(TestTaskWindowContainerController taskController, 431 IApplicationToken token) { 432 super(taskController, token, null /* listener */, 0 /* index */, 433 SCREEN_ORIENTATION_UNSPECIFIED, true /* fullscreen */, 434 true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, 435 false /* launchTaskBehind */, false /* alwaysFocusable */, 436 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, 437 0 /* inputDispatchingTimeoutNanos */, sWm); 438 mToken = token; 439 } 440 441 @Override 442 AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, 443 boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, 444 boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, 445 int rotationAnimationHint, int configChanges, boolean launchTaskBehind, 446 boolean alwaysFocusable, AppWindowContainerController controller) { 447 return new TestAppWindowToken(service, token, voiceInteraction, dc, 448 inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, 449 orientation, 450 rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, 451 controller); 452 } 453 454 AppWindowToken getAppWindowToken() { 455 return (AppWindowToken) sDisplayContent.getWindowToken(mToken.asBinder()); 456 } 457 } 458 459 class TestIApplicationToken implements IApplicationToken { 460 461 private final Binder mBinder = new Binder(); 462 @Override 463 public IBinder asBinder() { 464 return mBinder; 465 } 466 } 467 468 /** Used to track resize reports. */ 469 class TestWindowState extends WindowState { 470 boolean resizeReported; 471 472 TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) { 473 super(sWm, sMockSession, new TestIWindow(), token, null, OP_NONE, 0, attrs, 0, 0, 474 false /* ownerCanAddInternalSystemWindow */); 475 sWm.mWindowMap.put(mClient.asBinder(), this); 476 } 477 478 @Override 479 void reportResized() { 480 super.reportResized(); 481 resizeReported = true; 482 } 483 484 @Override 485 public boolean isGoneForLayoutLw() { 486 return false; 487 } 488 489 @Override 490 void updateResizingWindowIfNeeded() { 491 // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive 492 // the system that it can actually update the window. 493 boolean hadSurface = mHasSurface; 494 mHasSurface = true; 495 496 super.updateResizingWindowIfNeeded(); 497 498 mHasSurface = hadSurface; 499 } 500 } 501} 502