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