| /* |
| * Copyright (C) 2016 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 com.android.server.wm; |
| |
| import static android.app.AppOpsManager.OP_NONE; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.ROTATION_UNDEFINED; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; |
| import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; |
| import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; |
| import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; |
| import static android.os.Process.SYSTEM_UID; |
| import static android.view.View.VISIBLE; |
| import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; |
| import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; |
| import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
| import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; |
| import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; |
| import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; |
| import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; |
| import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; |
| import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; |
| import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; |
| import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; |
| import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; |
| |
| import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; |
| import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; |
| import static com.android.server.wm.WindowContainer.POSITION_TOP; |
| import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.Mockito.mock; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActivityOptions; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.graphics.Rect; |
| import android.hardware.HardwareBuffer; |
| import android.hardware.display.DisplayManager; |
| import android.os.Build; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.service.voice.IVoiceInteractionSession; |
| import android.util.SparseArray; |
| import android.view.Display; |
| import android.view.DisplayInfo; |
| import android.view.Gravity; |
| import android.view.IDisplayWindowInsetsController; |
| import android.view.IWindow; |
| import android.view.InsetsSourceControl; |
| import android.view.InsetsState; |
| import android.view.InsetsVisibilities; |
| import android.view.Surface; |
| import android.view.SurfaceControl; |
| import android.view.SurfaceControl.Transaction; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.view.WindowManager.DisplayImePolicy; |
| import android.window.ITransitionPlayer; |
| import android.window.StartingWindowInfo; |
| import android.window.StartingWindowRemovalInfo; |
| import android.window.TaskFragmentOrganizer; |
| import android.window.TransitionInfo; |
| import android.window.TransitionRequestInfo; |
| |
| import com.android.internal.policy.AttributeCache; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.runner.Description; |
| import org.mockito.Mockito; |
| |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.util.HashMap; |
| |
| /** Common base class for window manager unit test classes. */ |
| class WindowTestsBase extends SystemServiceTestsBase { |
| final Context mContext = getInstrumentation().getTargetContext(); |
| |
| // Default package name |
| static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo"; |
| |
| static final int DEFAULT_TASK_FRAGMENT_ORGANIZER_UID = 10000; |
| static final String DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME = "Test:TaskFragmentOrganizer"; |
| |
| // Default base activity name |
| private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity"; |
| |
| // An id appended to the end of the component name to make it unique |
| static int sCurrentActivityId = 0; |
| |
| ActivityTaskManagerService mAtm; |
| RootWindowContainer mRootWindowContainer; |
| ActivityTaskSupervisor mSupervisor; |
| WindowManagerService mWm; |
| private final IWindow mIWindow = new TestIWindow(); |
| private Session mMockSession; |
| |
| DisplayInfo mDisplayInfo = new DisplayInfo(); |
| DisplayContent mDefaultDisplay; |
| |
| static final int STATUS_BAR_HEIGHT = 10; |
| static final int NAV_BAR_HEIGHT = 15; |
| |
| /** |
| * It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with |
| * {@link UseTestDisplay}, it will be an additional display. |
| */ |
| DisplayContent mDisplayContent; |
| |
| // The following fields are only available depending on the usage of annotation UseTestDisplay. |
| WindowState mWallpaperWindow; |
| WindowState mImeWindow; |
| WindowState mImeDialogWindow; |
| WindowState mStatusBarWindow; |
| WindowState mNotificationShadeWindow; |
| WindowState mDockedDividerWindow; |
| WindowState mNavBarWindow; |
| WindowState mAppWindow; |
| WindowState mChildAppWindowAbove; |
| WindowState mChildAppWindowBelow; |
| |
| /** |
| * Spied {@link Transaction} class than can be used to verify calls. |
| */ |
| Transaction mTransaction; |
| |
| /** |
| * Whether device-specific global overrides have already been checked in |
| * {@link WindowTestsBase#setUpBase()}. |
| */ |
| private static boolean sGlobalOverridesChecked; |
| /** |
| * Whether device-specific overrides have already been checked in |
| * {@link WindowTestsBase#setUpBase()} when the default display is used. |
| */ |
| private static boolean sOverridesCheckedDefaultDisplay; |
| /** |
| * Whether device-specific overrides have already been checked in |
| * {@link WindowTestsBase#setUpBase()} when a {@link TestDisplayContent} is used. |
| */ |
| private static boolean sOverridesCheckedTestDisplay; |
| |
| @BeforeClass |
| public static void setUpOnceBase() { |
| AttributeCache.init(getInstrumentation().getTargetContext()); |
| } |
| |
| @Before |
| public void setUpBase() { |
| mAtm = mSystemServicesTestRule.getActivityTaskManagerService(); |
| mSupervisor = mAtm.mTaskSupervisor; |
| mRootWindowContainer = mAtm.mRootWindowContainer; |
| mWm = mSystemServicesTestRule.getWindowManagerService(); |
| SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock); |
| |
| mDefaultDisplay = mWm.mRoot.getDefaultDisplay(); |
| // Update the display policy to make the screen fully turned on so animation is allowed |
| final DisplayPolicy displayPolicy = mDefaultDisplay.getDisplayPolicy(); |
| displayPolicy.screenTurnedOn(null /* screenOnListener */); |
| displayPolicy.finishKeyguardDrawn(); |
| displayPolicy.finishWindowsDrawn(); |
| displayPolicy.finishScreenTurningOn(); |
| |
| mTransaction = mSystemServicesTestRule.mTransaction; |
| mMockSession = mock(Session.class); |
| |
| mContext.getSystemService(DisplayManager.class) |
| .getDisplay(Display.DEFAULT_DISPLAY).getDisplayInfo(mDisplayInfo); |
| |
| // Only create an additional test display for annotated test class/method because it may |
| // significantly increase the execution time. |
| final Description description = mSystemServicesTestRule.getDescription(); |
| UseTestDisplay testDisplayAnnotation = description.getAnnotation(UseTestDisplay.class); |
| if (testDisplayAnnotation == null) { |
| testDisplayAnnotation = description.getTestClass().getAnnotation(UseTestDisplay.class); |
| } |
| if (testDisplayAnnotation != null) { |
| createTestDisplay(testDisplayAnnotation); |
| } else { |
| mDisplayContent = mDefaultDisplay; |
| } |
| |
| // Ensure letterbox aspect ratio is not overridden on any device target. |
| // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set |
| // on some device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0); |
| // Ensure letterbox horizontal position multiplier is not overridden on any device target. |
| // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, |
| // may be set on some device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f); |
| // Ensure letterbox vertical position multiplier is not overridden on any device target. |
| // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, |
| // may be set on some device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); |
| // Ensure letterbox horizontal reachability treatment isn't overridden on any device target. |
| // {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled}, |
| // may be set on some device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(false); |
| // Ensure letterbox vertical reachability treatment isn't overridden on any device target. |
| // {@link com.android.internal.R.bool.config_letterboxIsVerticalReachabilityEnabled}, |
| // may be set on some device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(false); |
| // Ensure aspect ratio for unresizable apps isn't overridden on any device target. |
| // {@link com.android.internal.R.bool |
| // .config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}, may be set on some |
| // device form factors. |
| mAtm.mWindowManager.mLetterboxConfiguration |
| .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false); |
| |
| checkDeviceSpecificOverridesNotApplied(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| // Revert back to device overrides. |
| mAtm.mWindowManager.mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); |
| mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); |
| mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier(); |
| mAtm.mWindowManager.mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); |
| mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); |
| mAtm.mWindowManager.mLetterboxConfiguration |
| .resetIsSplitScreenAspectRatioForUnresizableAppsEnabled(); |
| } |
| |
| /** |
| * Check that device-specific overrides are not applied. Only need to check once during entire |
| * test run for each case: global overrides, default display, and test display. |
| */ |
| private void checkDeviceSpecificOverridesNotApplied() { |
| // Check global overrides |
| if (!sGlobalOverridesChecked) { |
| assertEquals(0, mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(), |
| 0 /* delta */); |
| sGlobalOverridesChecked = true; |
| } |
| // Check display-specific overrides |
| if (!sOverridesCheckedDefaultDisplay && mDisplayContent == mDefaultDisplay) { |
| assertFalse(mDisplayContent.getIgnoreOrientationRequest()); |
| sOverridesCheckedDefaultDisplay = true; |
| } else if (!sOverridesCheckedTestDisplay && mDisplayContent instanceof TestDisplayContent) { |
| assertFalse(mDisplayContent.getIgnoreOrientationRequest()); |
| sOverridesCheckedTestDisplay = true; |
| } |
| } |
| |
| private void createTestDisplay(UseTestDisplay annotation) { |
| beforeCreateTestDisplay(); |
| mDisplayContent = createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); |
| |
| final boolean addAll = annotation.addAllCommonWindows(); |
| final @CommonTypes int[] requestedWindows = annotation.addWindows(); |
| |
| if (addAll || ArrayUtils.contains(requestedWindows, W_WALLPAPER)) { |
| mWallpaperWindow = createCommonWindow(null, TYPE_WALLPAPER, "wallpaperWindow"); |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_INPUT_METHOD)) { |
| mImeWindow = createCommonWindow(null, TYPE_INPUT_METHOD, "mImeWindow"); |
| mDisplayContent.mInputMethodWindow = mImeWindow; |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_INPUT_METHOD_DIALOG)) { |
| mImeDialogWindow = createCommonWindow(null, TYPE_INPUT_METHOD_DIALOG, |
| "mImeDialogWindow"); |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) { |
| mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow"); |
| mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT; |
| mStatusBarWindow.mAttrs.gravity = Gravity.TOP; |
| mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode = |
| LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; |
| mStatusBarWindow.mAttrs.setFitInsetsTypes(0); |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) { |
| mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE, |
| "mNotificationShadeWindow"); |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) { |
| mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow"); |
| mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT; |
| mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; |
| mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4]; |
| mNavBarWindow.mAttrs.setFitInsetsTypes(0); |
| for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { |
| mNavBarWindow.mAttrs.paramsForRotation[rot] = |
| getNavBarLayoutParamsForRotation(rot); |
| } |
| } |
| if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) { |
| mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, |
| "mDockedDividerWindow"); |
| } |
| final boolean addAboveApp = ArrayUtils.contains(requestedWindows, W_ABOVE_ACTIVITY); |
| final boolean addBelowApp = ArrayUtils.contains(requestedWindows, W_BELOW_ACTIVITY); |
| if (addAll || addAboveApp || addBelowApp |
| || ArrayUtils.contains(requestedWindows, W_ACTIVITY)) { |
| mAppWindow = createCommonWindow(null, TYPE_BASE_APPLICATION, "mAppWindow"); |
| } |
| if (addAll || addAboveApp) { |
| mChildAppWindowAbove = createCommonWindow(mAppWindow, TYPE_APPLICATION_ATTACHED_DIALOG, |
| "mChildAppWindowAbove"); |
| } |
| if (addAll || addBelowApp) { |
| mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY, |
| "mChildAppWindowBelow"); |
| } |
| |
| mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false); |
| |
| // Adding a display will cause freezing the display. Make sure to wait until it's |
| // unfrozen to not run into race conditions with the tests. |
| waitUntilHandlersIdle(); |
| } |
| |
| private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) { |
| int width = WindowManager.LayoutParams.MATCH_PARENT; |
| int height = WindowManager.LayoutParams.MATCH_PARENT; |
| int gravity = Gravity.BOTTOM; |
| switch (rotation) { |
| case ROTATION_UNDEFINED: |
| case Surface.ROTATION_0: |
| case Surface.ROTATION_180: |
| height = NAV_BAR_HEIGHT; |
| break; |
| case Surface.ROTATION_90: |
| gravity = Gravity.RIGHT; |
| width = NAV_BAR_HEIGHT; |
| break; |
| case Surface.ROTATION_270: |
| gravity = Gravity.LEFT; |
| width = NAV_BAR_HEIGHT; |
| break; |
| } |
| WindowManager.LayoutParams lp = new WindowManager.LayoutParams( |
| WindowManager.LayoutParams.TYPE_NAVIGATION_BAR); |
| lp.width = width; |
| lp.height = height; |
| lp.gravity = gravity; |
| lp.setFitInsetsTypes(0); |
| return lp; |
| } |
| |
| void beforeCreateTestDisplay() { |
| // Called before display is created. |
| } |
| |
| private WindowState createCommonWindow(WindowState parent, int type, String name) { |
| final WindowState win = createWindow(parent, type, name); |
| // Prevent common windows from been IME targets. |
| win.mAttrs.flags |= FLAG_NOT_FOCUSABLE; |
| return win; |
| } |
| |
| private WindowToken createWindowToken( |
| DisplayContent dc, int windowingMode, int activityType, int type) { |
| if (type == TYPE_WALLPAPER) { |
| return createWallpaperToken(dc); |
| } |
| if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { |
| return createTestWindowToken(type, dc); |
| } |
| |
| return createActivityRecord(dc, windowingMode, activityType); |
| } |
| |
| private WindowToken createWallpaperToken(DisplayContent dc) { |
| return new WallpaperWindowToken(mWm, mock(IBinder.class), true /* explicit */, dc, |
| true /* ownerCanManageAppTokens */); |
| } |
| |
| WindowState createAppWindow(Task task, int type, String name) { |
| final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent()); |
| task.addChild(activity, 0); |
| return createWindow(null, type, activity, name); |
| } |
| |
| // TODO: Move these calls to a builder? |
| WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, |
| IWindow iwindow) { |
| final WindowToken token = createWindowToken( |
| dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); |
| return createWindow(parent, type, token, name, 0 /* ownerId */, |
| false /* ownerCanAddInternalSystemWindow */, iwindow); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, String name) { |
| return (parent == null) |
| ? createWindow(parent, type, mDisplayContent, name) |
| : createWindow(parent, type, parent.mToken, name); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, String name, int ownerId) { |
| return (parent == null) |
| ? createWindow(parent, type, mDisplayContent, name, ownerId) |
| : createWindow(parent, type, parent.mToken, name, ownerId); |
| } |
| |
| WindowState createWindow(WindowState parent, int windowingMode, int activityType, |
| int type, DisplayContent dc, String name) { |
| final WindowToken token = createWindowToken(dc, windowingMode, activityType, type); |
| return createWindow(parent, type, token, name); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { |
| return createWindow( |
| parent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type, dc, name); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, |
| int ownerId) { |
| final WindowToken token = createWindowToken( |
| dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); |
| return createWindow(parent, type, token, name, ownerId); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, |
| boolean ownerCanAddInternalSystemWindow) { |
| final WindowToken token = createWindowToken( |
| dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type); |
| return createWindow(parent, type, token, name, 0 /* ownerId */, |
| ownerCanAddInternalSystemWindow); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, WindowToken token, String name) { |
| return createWindow(parent, type, token, name, 0 /* ownerId */, |
| false /* ownerCanAddInternalSystemWindow */); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, WindowToken token, String name, |
| int ownerId) { |
| return createWindow(parent, type, token, name, ownerId, |
| false /* ownerCanAddInternalSystemWindow */); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, WindowToken token, String name, |
| int ownerId, boolean ownerCanAddInternalSystemWindow) { |
| return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow, |
| mIWindow); |
| } |
| |
| WindowState createWindow(WindowState parent, int type, WindowToken token, String name, |
| int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) { |
| return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId), |
| ownerCanAddInternalSystemWindow, mWm, mMockSession, iwindow, |
| mSystemServicesTestRule.getPowerManagerWrapper()); |
| } |
| |
| static WindowState createWindow(WindowState parent, int type, WindowToken token, |
| String name, int ownerId, int userId, boolean ownerCanAddInternalSystemWindow, |
| WindowManagerService service, Session session, IWindow iWindow, |
| WindowState.PowerManagerWrapper powerManagerWrapper) { |
| SystemServicesTestRule.checkHoldsLock(service.mGlobalLock); |
| |
| final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type); |
| attrs.setTitle(name); |
| attrs.packageName = "test"; |
| |
| final WindowState w = new WindowState(service, session, iWindow, token, parent, |
| OP_NONE, attrs, VISIBLE, ownerId, userId, |
| ownerCanAddInternalSystemWindow, |
| powerManagerWrapper); |
| // TODO: Probably better to make this call in the WindowState ctor to avoid errors with |
| // adding it to the token... |
| token.addWindow(w); |
| return w; |
| } |
| |
| static void makeWindowVisible(WindowState... windows) { |
| for (WindowState win : windows) { |
| win.mViewVisibility = View.VISIBLE; |
| win.mRelayoutCalled = true; |
| win.mHasSurface = true; |
| win.mHidden = false; |
| win.show(false /* doAnimation */, false /* requestAnim */); |
| } |
| } |
| |
| static void makeWindowVisibleAndDrawn(WindowState... windows) { |
| makeWindowVisible(windows); |
| for (WindowState win : windows) { |
| win.mWinAnimator.mDrawState = HAS_DRAWN; |
| } |
| } |
| |
| /** |
| * Gets the order of the given {@link Task} as its z-order in the hierarchy below this TDA. |
| * The Task can be a direct child of a child TaskDisplayArea. {@code -1} if not found. |
| */ |
| static int getTaskIndexOf(TaskDisplayArea taskDisplayArea, Task task) { |
| int index = 0; |
| final int childCount = taskDisplayArea.getChildCount(); |
| for (int i = 0; i < childCount; i++) { |
| final WindowContainer wc = taskDisplayArea.getChildAt(i); |
| if (wc.asTask() != null) { |
| if (wc.asTask() == task) { |
| return index; |
| } |
| index++; |
| } else { |
| final TaskDisplayArea tda = wc.asTaskDisplayArea(); |
| final int subIndex = getTaskIndexOf(tda, task); |
| if (subIndex > -1) { |
| return index + subIndex; |
| } else { |
| index += tda.getRootTaskCount(); |
| } |
| } |
| } |
| return -1; |
| } |
| |
| /** Creates a {@link TaskDisplayArea} right above the default one. */ |
| static TaskDisplayArea createTaskDisplayArea(DisplayContent displayContent, |
| WindowManagerService service, String name, int displayAreaFeature) { |
| final TaskDisplayArea newTaskDisplayArea = new TaskDisplayArea( |
| displayContent, service, name, displayAreaFeature); |
| final TaskDisplayArea defaultTaskDisplayArea = displayContent.getDefaultTaskDisplayArea(); |
| |
| // Insert the new TDA to the correct position. |
| defaultTaskDisplayArea.getParent().addChild(newTaskDisplayArea, |
| defaultTaskDisplayArea.getParent().mChildren.indexOf(defaultTaskDisplayArea) |
| + 1); |
| return newTaskDisplayArea; |
| } |
| |
| /** |
| * Creates a {@link Task} with a simple {@link ActivityRecord} and adds to the given |
| * {@link TaskDisplayArea}. |
| */ |
| Task createTaskWithActivity(TaskDisplayArea taskDisplayArea, |
| int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) { |
| return createTask(taskDisplayArea, windowingMode, activityType, |
| onTop, true /* createActivity */, twoLevelTask); |
| } |
| |
| /** Creates a {@link Task} and adds to the given {@link DisplayContent}. */ |
| Task createTask(DisplayContent dc) { |
| return createTask(dc.getDefaultTaskDisplayArea(), |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); |
| } |
| |
| Task createTask(DisplayContent dc, int windowingMode, int activityType) { |
| return createTask(dc.getDefaultTaskDisplayArea(), windowingMode, activityType); |
| } |
| |
| Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType) { |
| return createTask(taskDisplayArea, windowingMode, activityType, |
| true /* onTop */, false /* createActivity */, false /* twoLevelTask */); |
| } |
| |
| /** Creates a {@link Task} and adds to the given {@link TaskDisplayArea}. */ |
| Task createTask(TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, |
| boolean onTop, boolean createActivity, boolean twoLevelTask) { |
| final TaskBuilder builder = new TaskBuilder(mSupervisor) |
| .setTaskDisplayArea(taskDisplayArea) |
| .setWindowingMode(windowingMode) |
| .setActivityType(activityType) |
| .setOnTop(onTop) |
| .setCreateActivity(createActivity); |
| if (twoLevelTask) { |
| return builder |
| .setCreateParentTask(true) |
| .build() |
| .getRootTask(); |
| } else { |
| return builder.build(); |
| } |
| } |
| |
| /** Creates a {@link Task} and adds to the given root {@link Task}. */ |
| Task createTaskInRootTask(Task rootTask, int userId) { |
| final Task task = new TaskBuilder(rootTask.mTaskSupervisor) |
| .setUserId(userId) |
| .setParentTaskFragment(rootTask) |
| .build(); |
| return task; |
| } |
| |
| /** Creates an {@link ActivityRecord}. */ |
| static ActivityRecord createNonAttachedActivityRecord(DisplayContent dc) { |
| final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService) |
| .setOnTop(true) |
| .build(); |
| postCreateActivitySetup(activity, dc); |
| return activity; |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to a new created {@link Task}. |
| * [Task] - [ActivityRecord] |
| */ |
| ActivityRecord createActivityRecord(DisplayContent dc) { |
| return createActivityRecord(dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to a new created {@link Task}. |
| * [Task] - [ActivityRecord] |
| */ |
| ActivityRecord createActivityRecord(DisplayContent dc, int windowingMode, |
| int activityType) { |
| final Task task = createTask(dc, windowingMode, activityType); |
| return createActivityRecord(dc, task); |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to the specified {@link Task}. |
| * [Task] - [ActivityRecord] |
| */ |
| static ActivityRecord createActivityRecord(Task task) { |
| return createActivityRecord(task.getDisplayContent(), task); |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to the specified {@link Task}. |
| * [Task] - [ActivityRecord] |
| */ |
| static ActivityRecord createActivityRecord(DisplayContent dc, Task task) { |
| final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService) |
| .setTask(task) |
| .setOnTop(true) |
| .build(); |
| postCreateActivitySetup(activity, dc); |
| return activity; |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to a new created {@link Task}. |
| * Then adds the new created {@link Task} to a new created parent {@link Task} |
| * [Task1] - [Task2] - [ActivityRecord] |
| */ |
| ActivityRecord createActivityRecordWithParentTask(DisplayContent dc, int windowingMode, |
| int activityType) { |
| final Task task = createTask(dc, windowingMode, activityType); |
| return createActivityRecordWithParentTask(task); |
| } |
| |
| /** |
| * Creates an {@link ActivityRecord} and adds it to a new created {@link Task}. |
| * Then adds the new created {@link Task} to the specified parent {@link Task} |
| * [Task1] - [Task2] - [ActivityRecord] |
| */ |
| static ActivityRecord createActivityRecordWithParentTask(Task parentTask) { |
| final ActivityRecord activity = new ActivityBuilder(parentTask.mAtmService) |
| .setParentTask(parentTask) |
| .setCreateTask(true) |
| .setOnTop(true) |
| .build(); |
| postCreateActivitySetup(activity, parentTask.getDisplayContent()); |
| return activity; |
| } |
| |
| private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) { |
| activity.onDisplayChanged(dc); |
| activity.setOccludesParent(true); |
| activity.setVisible(true); |
| activity.mVisibleRequested = true; |
| } |
| |
| /** |
| * Creates a {@link TaskFragment} and attach it to the {@code parentTask}. |
| * |
| * @param parentTask the {@link Task} this TaskFragment is going to be attached |
| * @param createEmbeddedTask Sets to {@code true} to create an embedded Task for this |
| * TaskFragment. Otherwise, create a {@link ActivityRecord}. |
| * @return the created TaskFragment |
| */ |
| static TaskFragment createTaskFragmentWithParentTask(@NonNull Task parentTask, |
| boolean createEmbeddedTask) { |
| final TaskFragmentBuilder builder = new TaskFragmentBuilder(parentTask.mAtmService) |
| .setParentTask(parentTask); |
| if (createEmbeddedTask) { |
| builder.createEmbeddedTask(); |
| } else { |
| builder.createActivityCount(1); |
| } |
| return builder.build(); |
| } |
| |
| static TaskFragment createTaskFragmentWithEmbeddedActivity(@NonNull Task parentTask, |
| TaskFragmentOrganizer organizer) { |
| return new TaskFragmentBuilder(parentTask.mAtmService) |
| .setParentTask(parentTask) |
| .createActivityCount(1) |
| .setOrganizer(organizer) |
| .build(); |
| } |
| |
| /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ |
| DisplayContent createNewDisplay() { |
| return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL); |
| } |
| |
| /** Creates a {@link DisplayContent} and adds it to the system. */ |
| private DisplayContent createNewDisplayWithImeSupport(@DisplayImePolicy int imePolicy) { |
| return createNewDisplay(mDisplayInfo, imePolicy, /* overrideSettings */ null); |
| } |
| |
| /** Creates a {@link DisplayContent} that supports IME and adds it to the system. */ |
| DisplayContent createNewDisplay(DisplayInfo info) { |
| return createNewDisplay(info, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null); |
| } |
| |
| /** Creates a {@link DisplayContent} and adds it to the system. */ |
| private DisplayContent createNewDisplay(DisplayInfo info, @DisplayImePolicy int imePolicy, |
| @Nullable SettingsEntry overrideSettings) { |
| final DisplayContent display = |
| new TestDisplayContent.Builder(mAtm, info) |
| .setOverrideSettings(overrideSettings) |
| .build(); |
| final DisplayContent dc = display.mDisplayContent; |
| // this display can show IME. |
| dc.mWmService.mDisplayWindowSettings.setDisplayImePolicy(dc, imePolicy); |
| return dc; |
| } |
| |
| /** |
| * Creates a {@link DisplayContent} with given display state and adds it to the system. |
| * |
| * @param displayState For initializing the state of the display. See |
| * {@link Display#getState()}. |
| */ |
| DisplayContent createNewDisplay(int displayState) { |
| // Leverage main display info & initialize it with display state for given displayId. |
| DisplayInfo displayInfo = new DisplayInfo(); |
| displayInfo.copyFrom(mDisplayInfo); |
| displayInfo.state = displayState; |
| return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_LOCAL, /* overrideSettings */ null); |
| } |
| |
| /** Creates a {@link TestWindowState} */ |
| TestWindowState createWindowState(WindowManager.LayoutParams attrs, WindowToken token) { |
| SystemServicesTestRule.checkHoldsLock(mWm.mGlobalLock); |
| |
| return new TestWindowState(mWm, mMockSession, mIWindow, attrs, token); |
| } |
| |
| /** Creates a {@link DisplayContent} as parts of simulate display info for test. */ |
| DisplayContent createMockSimulatedDisplay() { |
| return createMockSimulatedDisplay(/* overrideSettings */ null); |
| } |
| |
| DisplayContent createMockSimulatedDisplay(@Nullable SettingsEntry overrideSettings) { |
| DisplayInfo displayInfo = new DisplayInfo(); |
| displayInfo.copyFrom(mDisplayInfo); |
| displayInfo.type = Display.TYPE_VIRTUAL; |
| displayInfo.ownerUid = SYSTEM_UID; |
| return createNewDisplay(displayInfo, DISPLAY_IME_POLICY_FALLBACK_DISPLAY, overrideSettings); |
| } |
| |
| IDisplayWindowInsetsController createDisplayWindowInsetsController() { |
| return new IDisplayWindowInsetsController.Stub() { |
| |
| @Override |
| public void insetsChanged(InsetsState insetsState) throws RemoteException { |
| } |
| |
| @Override |
| public void insetsControlChanged(InsetsState insetsState, |
| InsetsSourceControl[] insetsSourceControls) throws RemoteException { |
| } |
| |
| @Override |
| public void showInsets(int i, boolean b) throws RemoteException { |
| } |
| |
| @Override |
| public void hideInsets(int i, boolean b) throws RemoteException { |
| } |
| |
| @Override |
| public void topFocusedWindowChanged(ComponentName component, |
| InsetsVisibilities requestedVisibilities) { |
| } |
| }; |
| } |
| |
| BLASTSyncEngine createTestBLASTSyncEngine() { |
| return new BLASTSyncEngine(mWm) { |
| @Override |
| void scheduleTimeout(SyncGroup s, long timeoutMs) { |
| // Disable timeout. |
| } |
| }; |
| } |
| |
| /** Sets up a simple implementation of transition player for shell transitions. */ |
| TestTransitionPlayer registerTestTransitionPlayer() { |
| final TestTransitionPlayer testPlayer = new TestTransitionPlayer( |
| mAtm.getTransitionController(), mAtm.mWindowOrganizerController); |
| testPlayer.mController.registerTransitionPlayer(testPlayer, null /* playerProc */); |
| return testPlayer; |
| } |
| |
| /** |
| * Avoids rotating screen disturbed by some conditions. It is usually used for the default |
| * display that is not the instance of {@link TestDisplayContent} (it bypasses the conditions). |
| * |
| * @see DisplayRotation#updateRotationUnchecked |
| */ |
| void unblockDisplayRotation(DisplayContent dc) { |
| mWm.stopFreezingDisplayLocked(); |
| // The rotation animation won't actually play, it needs to be cleared manually. |
| dc.setRotationAnimation(null); |
| } |
| |
| // The window definition for UseTestDisplay#addWindows. The test can declare to add only |
| // necessary windows, that avoids adding unnecessary overhead of unused windows. |
| static final int W_NOTIFICATION_SHADE = TYPE_NOTIFICATION_SHADE; |
| static final int W_STATUS_BAR = TYPE_STATUS_BAR; |
| static final int W_NAVIGATION_BAR = TYPE_NAVIGATION_BAR; |
| static final int W_INPUT_METHOD_DIALOG = TYPE_INPUT_METHOD_DIALOG; |
| static final int W_INPUT_METHOD = TYPE_INPUT_METHOD; |
| static final int W_DOCK_DIVIDER = TYPE_DOCK_DIVIDER; |
| static final int W_ABOVE_ACTIVITY = TYPE_APPLICATION_ATTACHED_DIALOG; |
| static final int W_ACTIVITY = TYPE_BASE_APPLICATION; |
| static final int W_BELOW_ACTIVITY = TYPE_APPLICATION_MEDIA_OVERLAY; |
| static final int W_WALLPAPER = TYPE_WALLPAPER; |
| |
| /** The common window types supported by {@link UseTestDisplay}. */ |
| @Retention(RetentionPolicy.RUNTIME) |
| @IntDef(value = { |
| W_NOTIFICATION_SHADE, |
| W_STATUS_BAR, |
| W_NAVIGATION_BAR, |
| W_INPUT_METHOD_DIALOG, |
| W_INPUT_METHOD, |
| W_DOCK_DIVIDER, |
| W_ABOVE_ACTIVITY, |
| W_ACTIVITY, |
| W_BELOW_ACTIVITY, |
| W_WALLPAPER, |
| }) |
| @interface CommonTypes { |
| } |
| |
| /** |
| * The annotation for class and method (higher priority) to create a non-default display that |
| * will be assigned to {@link #mDisplayContent}. It is used if the test needs |
| * <ul> |
| * <li>Pure empty display.</li> |
| * <li>Configured common windows.</li> |
| * <li>Independent and customizable orientation.</li> |
| * <li>Cross display operation.</li> |
| * </ul> |
| * |
| * @see TestDisplayContent |
| * @see #createTestDisplay |
| **/ |
| @Target({ ElementType.METHOD, ElementType.TYPE }) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface UseTestDisplay { |
| boolean addAllCommonWindows() default false; |
| @CommonTypes int[] addWindows() default {}; |
| } |
| |
| /** Creates and adds a {@link TestDisplayContent} to supervisor at the given position. */ |
| TestDisplayContent addNewDisplayContentAt(int position) { |
| return new TestDisplayContent.Builder(mAtm, 1000, 1500).setPosition(position).build(); |
| } |
| |
| /** Sets the default minimum task size to 1 so that tests can use small task sizes */ |
| public void removeGlobalMinSizeRestriction() { |
| mAtm.mRootWindowContainer.forAllDisplays( |
| displayContent -> displayContent.mMinSizeOfResizeableTaskDp = 1); |
| } |
| |
| /** Mocks the behavior of taking a snapshot. */ |
| void mockSurfaceFreezerSnapshot(SurfaceFreezer surfaceFreezer) { |
| final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = |
| mock(SurfaceControl.ScreenshotHardwareBuffer.class); |
| final HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class); |
| spyOn(surfaceFreezer); |
| doReturn(screenshotBuffer).when(surfaceFreezer) |
| .createSnapshotBufferInner(any(), any()); |
| doReturn(null).when(surfaceFreezer) |
| .createFromHardwareBufferInner(any()); |
| doReturn(hardwareBuffer).when(screenshotBuffer).getHardwareBuffer(); |
| doReturn(100).when(hardwareBuffer).getWidth(); |
| doReturn(100).when(hardwareBuffer).getHeight(); |
| } |
| |
| static ComponentName getUniqueComponentName() { |
| return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, |
| DEFAULT_COMPONENT_CLASS_NAME + sCurrentActivityId++); |
| } |
| |
| /** |
| * Builder for creating new activities. |
| */ |
| protected static class ActivityBuilder { |
| static final int DEFAULT_FAKE_UID = 12345; |
| static final String DEFAULT_PROCESS_NAME = "procName"; |
| static int sProcNameSeq; |
| |
| private final ActivityTaskManagerService mService; |
| |
| private ComponentName mComponent; |
| private String mTargetActivity; |
| private Task mTask; |
| private String mProcessName = DEFAULT_PROCESS_NAME; |
| private String mAffinity; |
| private int mUid = DEFAULT_FAKE_UID; |
| private boolean mCreateTask = false; |
| private Task mParentTask; |
| private int mActivityFlags; |
| private int mLaunchMode; |
| private int mResizeMode = RESIZE_MODE_RESIZEABLE; |
| private float mMaxAspectRatio; |
| private float mMinAspectRatio; |
| private boolean mSupportsSizeChanges; |
| private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; |
| private boolean mLaunchTaskBehind = false; |
| private int mConfigChanges; |
| private int mLaunchedFromPid; |
| private int mLaunchedFromUid; |
| private String mLaunchedFromPackage; |
| private WindowProcessController mWpc; |
| private Bundle mIntentExtras; |
| private boolean mOnTop = false; |
| private ActivityInfo.WindowLayout mWindowLayout; |
| private boolean mVisible = true; |
| private ActivityOptions mLaunchIntoPipOpts; |
| |
| ActivityBuilder(ActivityTaskManagerService service) { |
| mService = service; |
| } |
| |
| ActivityBuilder setComponent(ComponentName component) { |
| mComponent = component; |
| return this; |
| } |
| |
| ActivityBuilder setTargetActivity(String targetActivity) { |
| mTargetActivity = targetActivity; |
| return this; |
| } |
| |
| ActivityBuilder setIntentExtras(Bundle extras) { |
| mIntentExtras = extras; |
| return this; |
| } |
| |
| static ComponentName getDefaultComponent() { |
| return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, |
| DEFAULT_COMPONENT_PACKAGE_NAME); |
| } |
| |
| ActivityBuilder setTask(Task task) { |
| mTask = task; |
| return this; |
| } |
| |
| ActivityBuilder setActivityFlags(int flags) { |
| mActivityFlags = flags; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchMode(int launchMode) { |
| mLaunchMode = launchMode; |
| return this; |
| } |
| |
| ActivityBuilder setParentTask(Task parentTask) { |
| mParentTask = parentTask; |
| return this; |
| } |
| |
| ActivityBuilder setCreateTask(boolean createTask) { |
| mCreateTask = createTask; |
| return this; |
| } |
| |
| ActivityBuilder setProcessName(String name) { |
| mProcessName = name; |
| return this; |
| } |
| |
| ActivityBuilder setUid(int uid) { |
| mUid = uid; |
| return this; |
| } |
| |
| ActivityBuilder setResizeMode(int resizeMode) { |
| mResizeMode = resizeMode; |
| return this; |
| } |
| |
| ActivityBuilder setMaxAspectRatio(float maxAspectRatio) { |
| mMaxAspectRatio = maxAspectRatio; |
| return this; |
| } |
| |
| ActivityBuilder setMinAspectRatio(float minAspectRatio) { |
| mMinAspectRatio = minAspectRatio; |
| return this; |
| } |
| |
| ActivityBuilder setSupportsSizeChanges(boolean supportsSizeChanges) { |
| mSupportsSizeChanges = supportsSizeChanges; |
| return this; |
| } |
| |
| ActivityBuilder setScreenOrientation(int screenOrientation) { |
| mScreenOrientation = screenOrientation; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) { |
| mLaunchTaskBehind = launchTaskBehind; |
| return this; |
| } |
| |
| ActivityBuilder setConfigChanges(int configChanges) { |
| mConfigChanges = configChanges; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchedFromPid(int pid) { |
| mLaunchedFromPid = pid; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchedFromUid(int uid) { |
| mLaunchedFromUid = uid; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchedFromPackage(String packageName) { |
| mLaunchedFromPackage = packageName; |
| return this; |
| } |
| |
| ActivityBuilder setUseProcess(WindowProcessController wpc) { |
| mWpc = wpc; |
| return this; |
| } |
| |
| ActivityBuilder setAffinity(String affinity) { |
| mAffinity = affinity; |
| return this; |
| } |
| |
| ActivityBuilder setOnTop(boolean onTop) { |
| mOnTop = onTop; |
| return this; |
| } |
| |
| ActivityBuilder setWindowLayout(ActivityInfo.WindowLayout windowLayout) { |
| mWindowLayout = windowLayout; |
| return this; |
| } |
| |
| ActivityBuilder setVisible(boolean visible) { |
| mVisible = visible; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchIntoPipActivityOptions(ActivityOptions opts) { |
| mLaunchIntoPipOpts = opts; |
| return this; |
| } |
| |
| ActivityRecord build() { |
| SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock); |
| try { |
| mService.deferWindowLayout(); |
| return buildInner(); |
| } finally { |
| mService.continueWindowLayout(); |
| } |
| } |
| |
| ActivityRecord buildInner() { |
| if (mComponent == null) { |
| mComponent = getUniqueComponentName(); |
| } |
| |
| Intent intent = new Intent(); |
| intent.setComponent(mComponent); |
| if (mIntentExtras != null) { |
| intent.putExtras(mIntentExtras); |
| } |
| final ActivityInfo aInfo = new ActivityInfo(); |
| aInfo.applicationInfo = new ApplicationInfo(); |
| aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; |
| aInfo.applicationInfo.packageName = mComponent.getPackageName(); |
| aInfo.applicationInfo.uid = mUid; |
| if (DEFAULT_PROCESS_NAME.equals(mProcessName)) { |
| mProcessName += ++sProcNameSeq; |
| } |
| aInfo.processName = mProcessName; |
| aInfo.packageName = mComponent.getPackageName(); |
| aInfo.name = mComponent.getClassName(); |
| if (mTargetActivity != null) { |
| aInfo.targetActivity = mTargetActivity; |
| } |
| aInfo.flags |= mActivityFlags; |
| aInfo.launchMode = mLaunchMode; |
| aInfo.resizeMode = mResizeMode; |
| aInfo.setMaxAspectRatio(mMaxAspectRatio); |
| aInfo.setMinAspectRatio(mMinAspectRatio); |
| aInfo.supportsSizeChanges = mSupportsSizeChanges; |
| aInfo.screenOrientation = mScreenOrientation; |
| aInfo.configChanges |= mConfigChanges; |
| aInfo.taskAffinity = mAffinity; |
| aInfo.windowLayout = mWindowLayout; |
| |
| if (mCreateTask) { |
| mTask = new TaskBuilder(mService.mTaskSupervisor) |
| .setComponent(mComponent) |
| // Apply the root activity info and intent |
| .setActivityInfo(aInfo) |
| .setIntent(intent) |
| .setParentTaskFragment(mParentTask).build(); |
| } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask( |
| mParentTask.getWindowingMode(), mParentTask.getActivityType())) { |
| // The parent task can be the task root. |
| mTask = mParentTask; |
| } |
| |
| ActivityOptions options = null; |
| if (mLaunchIntoPipOpts != null) { |
| options = mLaunchIntoPipOpts; |
| } else if (mLaunchTaskBehind) { |
| options = ActivityOptions.makeTaskLaunchBehind(); |
| } |
| final ActivityRecord activity = new ActivityRecord.Builder(mService) |
| .setLaunchedFromPid(mLaunchedFromPid) |
| .setLaunchedFromUid(mLaunchedFromUid) |
| .setLaunchedFromPackage(mLaunchedFromPackage) |
| .setIntent(intent) |
| .setActivityInfo(aInfo) |
| .setActivityOptions(options) |
| .build(); |
| |
| spyOn(activity); |
| if (mTask != null) { |
| mTask.addChild(activity); |
| if (mOnTop) { |
| // Move the task to front after activity is added. |
| // Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be other |
| // root tasks (e.g. home root task). |
| mTask.moveToFront("createActivity"); |
| } |
| if (mVisible) { |
| activity.mVisibleRequested = true; |
| activity.setVisible(true); |
| } |
| } |
| |
| final WindowProcessController wpc; |
| if (mWpc != null) { |
| wpc = mWpc; |
| } else { |
| final WindowProcessController p = mService.getProcessController(mProcessName, mUid); |
| wpc = p != null ? p : SystemServicesTestRule.addProcess( |
| mService, aInfo.applicationInfo, mProcessName, 0 /* pid */); |
| } |
| activity.setProcess(wpc); |
| |
| // Resume top activities to make sure all other signals in the system are connected. |
| mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); |
| return activity; |
| } |
| } |
| |
| static class TaskFragmentBuilder { |
| private final ActivityTaskManagerService mAtm; |
| private Task mParentTask; |
| private boolean mCreateParentTask; |
| private boolean mCreateEmbeddedTask; |
| private int mCreateActivityCount = 0; |
| @Nullable |
| private TaskFragmentOrganizer mOrganizer; |
| private IBinder mFragmentToken; |
| private Rect mBounds; |
| |
| TaskFragmentBuilder(ActivityTaskManagerService service) { |
| mAtm = service; |
| } |
| |
| TaskFragmentBuilder setCreateParentTask() { |
| mCreateParentTask = true; |
| return this; |
| } |
| |
| TaskFragmentBuilder setParentTask(Task task) { |
| mParentTask = task; |
| return this; |
| } |
| |
| /** Creates a child embedded Task and its Activity */ |
| TaskFragmentBuilder createEmbeddedTask() { |
| mCreateEmbeddedTask = true; |
| return this; |
| } |
| |
| TaskFragmentBuilder createActivityCount(int count) { |
| mCreateActivityCount = count; |
| return this; |
| } |
| |
| TaskFragmentBuilder setOrganizer(@Nullable TaskFragmentOrganizer organizer) { |
| mOrganizer = organizer; |
| return this; |
| } |
| |
| TaskFragmentBuilder setFragmentToken(@Nullable IBinder fragmentToken) { |
| mFragmentToken = fragmentToken; |
| return this; |
| } |
| |
| TaskFragmentBuilder setBounds(@Nullable Rect bounds) { |
| mBounds = bounds; |
| return this; |
| } |
| |
| TaskFragment build() { |
| SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock); |
| |
| final TaskFragment taskFragment = new TaskFragment(mAtm, mFragmentToken, |
| mOrganizer != null); |
| if (mParentTask == null && mCreateParentTask) { |
| mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build(); |
| } |
| if (mParentTask != null) { |
| mParentTask.addChild(taskFragment, POSITION_TOP); |
| } |
| if (mCreateEmbeddedTask) { |
| new TaskBuilder(mAtm.mTaskSupervisor) |
| .setParentTaskFragment(taskFragment) |
| .setCreateActivity(true) |
| .build(); |
| } |
| while (mCreateActivityCount > 0) { |
| final ActivityRecord activity = new ActivityBuilder(mAtm).build(); |
| taskFragment.addChild(activity); |
| mCreateActivityCount--; |
| } |
| if (mOrganizer != null) { |
| taskFragment.setTaskFragmentOrganizer( |
| mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID, |
| DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); |
| } |
| if (mBounds != null && !mBounds.isEmpty()) { |
| taskFragment.setBounds(mBounds); |
| } |
| spyOn(taskFragment); |
| return taskFragment; |
| } |
| } |
| |
| /** |
| * Builder for creating new tasks. |
| */ |
| protected static class TaskBuilder { |
| private final ActivityTaskSupervisor mSupervisor; |
| |
| private TaskDisplayArea mTaskDisplayArea; |
| private ComponentName mComponent; |
| private String mPackage; |
| private int mFlags = 0; |
| private int mTaskId = -1; |
| private int mUserId = 0; |
| private int mWindowingMode = WINDOWING_MODE_UNDEFINED; |
| private int mActivityType = ACTIVITY_TYPE_STANDARD; |
| private ActivityInfo mActivityInfo; |
| private Intent mIntent; |
| private boolean mOnTop = true; |
| private IVoiceInteractionSession mVoiceSession; |
| |
| private boolean mCreateParentTask = false; |
| private TaskFragment mParentTaskFragment; |
| |
| private boolean mCreateActivity = false; |
| private boolean mCreatedByOrganizer = false; |
| |
| TaskBuilder(ActivityTaskSupervisor supervisor) { |
| mSupervisor = supervisor; |
| mTaskDisplayArea = mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea(); |
| } |
| |
| /** |
| * Set the parent {@link DisplayContent} and use the default task display area. Overrides |
| * the task display area, if was set before. |
| */ |
| TaskBuilder setDisplay(DisplayContent display) { |
| mTaskDisplayArea = display.getDefaultTaskDisplayArea(); |
| return this; |
| } |
| |
| /** Set the parent {@link TaskDisplayArea}. Overrides the display, if was set before. */ |
| TaskBuilder setTaskDisplayArea(TaskDisplayArea taskDisplayArea) { |
| mTaskDisplayArea = taskDisplayArea; |
| return this; |
| } |
| |
| TaskBuilder setComponent(ComponentName component) { |
| mComponent = component; |
| return this; |
| } |
| |
| TaskBuilder setPackage(String packageName) { |
| mPackage = packageName; |
| return this; |
| } |
| |
| TaskBuilder setFlags(int flags) { |
| mFlags = flags; |
| return this; |
| } |
| |
| TaskBuilder setTaskId(int taskId) { |
| mTaskId = taskId; |
| return this; |
| } |
| |
| TaskBuilder setUserId(int userId) { |
| mUserId = userId; |
| return this; |
| } |
| |
| TaskBuilder setWindowingMode(int windowingMode) { |
| mWindowingMode = windowingMode; |
| return this; |
| } |
| |
| TaskBuilder setActivityType(int activityType) { |
| mActivityType = activityType; |
| return this; |
| } |
| |
| TaskBuilder setActivityInfo(ActivityInfo info) { |
| mActivityInfo = info; |
| return this; |
| } |
| |
| TaskBuilder setIntent(Intent intent) { |
| mIntent = intent; |
| return this; |
| } |
| |
| TaskBuilder setOnTop(boolean onTop) { |
| mOnTop = onTop; |
| return this; |
| } |
| |
| TaskBuilder setVoiceSession(IVoiceInteractionSession session) { |
| mVoiceSession = session; |
| return this; |
| } |
| |
| TaskBuilder setCreateParentTask(boolean createParentTask) { |
| mCreateParentTask = createParentTask; |
| return this; |
| } |
| |
| TaskBuilder setParentTaskFragment(TaskFragment parentTaskFragment) { |
| mParentTaskFragment = parentTaskFragment; |
| return this; |
| } |
| |
| TaskBuilder setCreateActivity(boolean createActivity) { |
| mCreateActivity = createActivity; |
| return this; |
| } |
| |
| TaskBuilder setCreatedByOrganizer(boolean createdByOrganizer) { |
| mCreatedByOrganizer = createdByOrganizer; |
| return this; |
| } |
| |
| Task build() { |
| SystemServicesTestRule.checkHoldsLock(mSupervisor.mService.mGlobalLock); |
| |
| // Create parent task. |
| if (mParentTaskFragment == null && mCreateParentTask) { |
| mParentTaskFragment = mTaskDisplayArea.createRootTask( |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| } |
| if (mParentTaskFragment != null |
| && !Mockito.mockingDetails(mParentTaskFragment).isSpy()) { |
| spyOn(mParentTaskFragment); |
| } |
| |
| // Create task. |
| if (mActivityInfo == null) { |
| mActivityInfo = new ActivityInfo(); |
| mActivityInfo.applicationInfo = new ApplicationInfo(); |
| mActivityInfo.applicationInfo.packageName = mPackage; |
| } |
| |
| if (mIntent == null) { |
| mIntent = new Intent(); |
| if (mComponent == null) { |
| mComponent = getUniqueComponentName(); |
| } |
| mIntent.setComponent(mComponent); |
| mIntent.setFlags(mFlags); |
| } |
| |
| final Task.Builder builder = new Task.Builder(mSupervisor.mService) |
| .setTaskId(mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId()) |
| .setWindowingMode(mWindowingMode) |
| .setActivityInfo(mActivityInfo) |
| .setIntent(mIntent) |
| .setOnTop(mOnTop) |
| .setVoiceSession(mVoiceSession) |
| .setCreatedByOrganizer(mCreatedByOrganizer); |
| final Task task; |
| if (mParentTaskFragment == null) { |
| task = builder.setActivityType(mActivityType) |
| .setParent(mTaskDisplayArea) |
| .build(); |
| } else { |
| task = builder.setParent(mParentTaskFragment).build(); |
| if (mParentTaskFragment.asTask() != null) { |
| mParentTaskFragment.asTask().moveToFront("build-task"); |
| } |
| } |
| spyOn(task); |
| task.mUserId = mUserId; |
| final Task rootTask = task.getRootTask(); |
| if (task != rootTask && !Mockito.mockingDetails(rootTask).isSpy()) { |
| spyOn(rootTask); |
| } |
| doNothing().when(rootTask).startActivityLocked( |
| any(), any(), anyBoolean(), anyBoolean(), any(), any()); |
| |
| // Create child activity. |
| if (mCreateActivity) { |
| new ActivityBuilder(mSupervisor.mService) |
| .setTask(task) |
| .setComponent(mComponent) |
| .build(); |
| if (mOnTop) { |
| // We move the task to front again in order to regain focus after activity |
| // is added. Or {@link TaskDisplayArea#mPreferredTopFocusableRootTask} could be |
| // other root tasks (e.g. home root task). |
| task.moveToFront("createActivityTask"); |
| } else { |
| task.moveToBack("createActivityTask", null); |
| } |
| } |
| |
| return task; |
| } |
| } |
| |
| static class TestStartingWindowOrganizer extends WindowOrganizerTests.StubOrganizer { |
| private final ActivityTaskManagerService mAtm; |
| private final WindowManagerService mWMService; |
| private final WindowState.PowerManagerWrapper mPowerManagerWrapper; |
| |
| private Runnable mRunnableWhenAddingSplashScreen; |
| private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>(); |
| private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>(); |
| |
| TestStartingWindowOrganizer(ActivityTaskManagerService service, |
| WindowState.PowerManagerWrapper powerManagerWrapper) { |
| mAtm = service; |
| mWMService = mAtm.mWindowManager; |
| mPowerManagerWrapper = powerManagerWrapper; |
| mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run); |
| mAtm.mTaskOrganizerController.registerTaskOrganizer(this); |
| } |
| |
| void setRunnableWhenAddingSplashScreen(Runnable r) { |
| mRunnableWhenAddingSplashScreen = r; |
| } |
| |
| @Override |
| public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { |
| synchronized (mWMService.mGlobalLock) { |
| final ActivityRecord activity = mWMService.mRoot.getActivityRecord( |
| appToken); |
| IWindow iWindow = mock(IWindow.class); |
| doReturn(mock(IBinder.class)).when(iWindow).asBinder(); |
| final WindowState window = WindowTestsBase.createWindow(null, |
| TYPE_APPLICATION_STARTING, activity, |
| "Starting window", 0 /* ownerId */, 0 /* userId*/, |
| false /* internalWindows */, mWMService, mock(Session.class), |
| iWindow, |
| mPowerManagerWrapper); |
| activity.mStartingWindow = window; |
| mAppWindowMap.put(appToken, window); |
| mTaskAppMap.put(info.taskInfo.taskId, appToken); |
| } |
| if (mRunnableWhenAddingSplashScreen != null) { |
| mRunnableWhenAddingSplashScreen.run(); |
| mRunnableWhenAddingSplashScreen = null; |
| } |
| } |
| @Override |
| public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { |
| synchronized (mWMService.mGlobalLock) { |
| final IBinder appToken = mTaskAppMap.get(removalInfo.taskId); |
| if (appToken != null) { |
| mTaskAppMap.remove(removalInfo.taskId); |
| final ActivityRecord activity = mWMService.mRoot.getActivityRecord( |
| appToken); |
| WindowState win = mAppWindowMap.remove(appToken); |
| activity.removeChild(win); |
| activity.mStartingWindow = null; |
| } |
| } |
| } |
| } |
| |
| static class TestSplitOrganizer extends WindowOrganizerTests.StubOrganizer { |
| final ActivityTaskManagerService mService; |
| final TaskDisplayArea mDefaultTDA; |
| Task mPrimary; |
| Task mSecondary; |
| int mDisplayId; |
| |
| TestSplitOrganizer(ActivityTaskManagerService service, DisplayContent display) { |
| mService = service; |
| mDefaultTDA = display.getDefaultTaskDisplayArea(); |
| mDisplayId = display.mDisplayId; |
| mService.mTaskOrganizerController.registerTaskOrganizer(this); |
| mPrimary = mService.mTaskOrganizerController.createRootTask( |
| display, WINDOWING_MODE_MULTI_WINDOW, null); |
| mSecondary = mService.mTaskOrganizerController.createRootTask( |
| display, WINDOWING_MODE_MULTI_WINDOW, null); |
| |
| mPrimary.setAdjacentTaskFragment(mSecondary); |
| display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary); |
| |
| final Rect primaryBounds = new Rect(); |
| final Rect secondaryBounds = new Rect(); |
| if (display.getConfiguration().orientation == ORIENTATION_LANDSCAPE) { |
| display.getBounds().splitVertically(primaryBounds, secondaryBounds); |
| } else { |
| display.getBounds().splitHorizontally(primaryBounds, secondaryBounds); |
| } |
| mPrimary.setBounds(primaryBounds); |
| mSecondary.setBounds(secondaryBounds); |
| |
| spyOn(mPrimary); |
| spyOn(mSecondary); |
| } |
| |
| TestSplitOrganizer(ActivityTaskManagerService service) { |
| this(service, service.mTaskSupervisor.mRootWindowContainer.getDefaultDisplay()); |
| } |
| |
| public Task createTaskToPrimary(boolean onTop) { |
| final Task primaryTask = mDefaultTDA.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, onTop); |
| putTaskToPrimary(primaryTask, onTop); |
| return primaryTask; |
| } |
| |
| public Task createTaskToSecondary(boolean onTop) { |
| final Task secondaryTask = mDefaultTDA.createRootTask( |
| WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, onTop); |
| putTaskToSecondary(secondaryTask, onTop); |
| return secondaryTask; |
| } |
| |
| public void putTaskToPrimary(Task task, boolean onTop) { |
| task.reparent(mPrimary, onTop ? POSITION_TOP : POSITION_BOTTOM); |
| } |
| |
| public void putTaskToSecondary(Task task, boolean onTop) { |
| task.reparent(mSecondary, onTop ? POSITION_TOP : POSITION_BOTTOM); |
| } |
| } |
| |
| static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { |
| return createTestWindowToken(type, dc, false /* persistOnEmpty */); |
| } |
| |
| static TestWindowToken createTestWindowToken(int type, DisplayContent dc, |
| boolean persistOnEmpty) { |
| SystemServicesTestRule.checkHoldsLock(dc.mWmService.mGlobalLock); |
| |
| return new TestWindowToken(type, dc, persistOnEmpty); |
| } |
| |
| /** Used so we can gain access to some protected members of the {@link WindowToken} class */ |
| static class TestWindowToken extends WindowToken { |
| |
| private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { |
| super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc, |
| false /* ownerCanManageAppTokens */); |
| } |
| |
| int getWindowsCount() { |
| return mChildren.size(); |
| } |
| |
| boolean hasWindow(WindowState w) { |
| return mChildren.contains(w); |
| } |
| } |
| |
| /** Used to track resize reports. */ |
| static class TestWindowState extends WindowState { |
| boolean mResizeReported; |
| |
| TestWindowState(WindowManagerService service, Session session, IWindow window, |
| WindowManager.LayoutParams attrs, WindowToken token) { |
| super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0, |
| false /* ownerCanAddInternalSystemWindow */); |
| } |
| |
| @Override |
| void reportResized() { |
| super.reportResized(); |
| mResizeReported = true; |
| } |
| |
| @Override |
| public boolean isGoneForLayout() { |
| return false; |
| } |
| |
| @Override |
| void updateResizingWindowIfNeeded() { |
| // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive |
| // the system that it can actually update the window. |
| boolean hadSurface = mHasSurface; |
| mHasSurface = true; |
| |
| super.updateResizingWindowIfNeeded(); |
| |
| mHasSurface = hadSurface; |
| } |
| } |
| |
| static class TestTransitionPlayer extends ITransitionPlayer.Stub { |
| final TransitionController mController; |
| final WindowOrganizerController mOrganizer; |
| Transition mLastTransit = null; |
| TransitionRequestInfo mLastRequest = null; |
| TransitionInfo mLastReady = null; |
| |
| TestTransitionPlayer(@NonNull TransitionController controller, |
| @NonNull WindowOrganizerController organizer) { |
| mController = controller; |
| mOrganizer = organizer; |
| } |
| |
| void clear() { |
| mLastTransit = null; |
| mLastReady = null; |
| mLastRequest = null; |
| } |
| |
| @Override |
| public void onTransitionReady(IBinder transitToken, TransitionInfo transitionInfo, |
| SurfaceControl.Transaction transaction, SurfaceControl.Transaction finishT) |
| throws RemoteException { |
| mLastTransit = Transition.fromBinder(transitToken); |
| mLastReady = transitionInfo; |
| } |
| |
| @Override |
| public void requestStartTransition(IBinder transitToken, |
| TransitionRequestInfo request) throws RemoteException { |
| mLastTransit = Transition.fromBinder(transitToken); |
| mLastRequest = request; |
| } |
| |
| void startTransition() { |
| mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null); |
| } |
| |
| void onTransactionReady(SurfaceControl.Transaction t) { |
| mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t); |
| } |
| |
| void start() { |
| startTransition(); |
| onTransactionReady(mock(SurfaceControl.Transaction.class)); |
| } |
| |
| public void finish() { |
| mController.finishTransition(mLastTransit); |
| } |
| } |
| } |