| /* |
| * Copyright (C) 2018 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.WindowConfiguration.ACTIVITY_TYPE_HOME; |
| import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; |
| |
| import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; |
| 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.mock; |
| import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; |
| import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; |
| import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; |
| |
| import android.app.ActivityManagerInternal; |
| import android.app.ActivityOptions; |
| import android.app.IApplicationThread; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.IPackageManager; |
| import android.content.pm.PackageManagerInternal; |
| import android.content.res.Configuration; |
| import android.graphics.Rect; |
| import android.hardware.display.DisplayManager; |
| import android.hardware.display.DisplayManagerGlobal; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.PowerManager; |
| import android.os.Process; |
| import android.os.UserHandle; |
| import android.service.voice.IVoiceInteractionSession; |
| import android.testing.DexmakerShareClassLoaderRule; |
| import android.view.Display; |
| import android.view.DisplayInfo; |
| |
| import com.android.internal.app.IVoiceInteractor; |
| import com.android.server.AttributeCache; |
| import com.android.server.ServiceThread; |
| import com.android.server.am.ActivityManagerService; |
| import com.android.server.am.PendingIntentController; |
| import com.android.server.appop.AppOpsService; |
| import com.android.server.firewall.IntentFirewall; |
| import com.android.server.uri.UriGrantsManagerInternal; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Rule; |
| import org.mockito.invocation.InvocationOnMock; |
| |
| import java.io.File; |
| import java.util.List; |
| |
| /** |
| * A base class to handle common operations in activity related unit tests. |
| */ |
| class ActivityTestsBase { |
| private static int sNextDisplayId = DEFAULT_DISPLAY + 1; |
| |
| @Rule |
| public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = |
| new DexmakerShareClassLoaderRule(); |
| |
| final Context mContext = getInstrumentation().getTargetContext(); |
| final TestInjector mTestInjector = new TestInjector(); |
| |
| ActivityTaskManagerService mService; |
| RootActivityContainer mRootActivityContainer; |
| ActivityStackSupervisor mSupervisor; |
| |
| // Default package name |
| static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo"; |
| |
| // Default base activity name |
| private static final String DEFAULT_COMPONENT_CLASS_NAME = ".BarActivity"; |
| |
| @BeforeClass |
| public static void setUpOnceBase() { |
| AttributeCache.init(getInstrumentation().getTargetContext()); |
| } |
| |
| @Before |
| public void setUpBase() { |
| mTestInjector.setUp(); |
| |
| mService = new TestActivityTaskManagerService(mContext); |
| mSupervisor = mService.mStackSupervisor; |
| mRootActivityContainer = mService.mRootActivityContainer; |
| } |
| |
| @After |
| public void tearDownBase() { |
| mTestInjector.tearDown(); |
| if (mService != null) { |
| mService.setWindowManager(null); |
| mService = null; |
| } |
| } |
| |
| /** Creates a {@link TestActivityDisplay}. */ |
| TestActivityDisplay createNewActivityDisplay() { |
| return TestActivityDisplay.create(mSupervisor, sNextDisplayId++); |
| } |
| |
| TestActivityDisplay createNewActivityDisplay(DisplayInfo info) { |
| return TestActivityDisplay.create(mSupervisor, sNextDisplayId++, info); |
| } |
| |
| /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ |
| TestActivityDisplay addNewActivityDisplayAt(int position) { |
| final TestActivityDisplay display = createNewActivityDisplay(); |
| mRootActivityContainer.addChild(display, position); |
| return display; |
| } |
| |
| /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ |
| TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) { |
| final TestActivityDisplay display = createNewActivityDisplay(info); |
| mRootActivityContainer.addChild(display, position); |
| return display; |
| } |
| |
| /** |
| * Builder for creating new activities. |
| */ |
| protected static class ActivityBuilder { |
| // An id appended to the end of the component name to make it unique |
| private static int sCurrentActivityId = 0; |
| |
| private final ActivityTaskManagerService mService; |
| |
| private ComponentName mComponent; |
| private String mTargetActivity; |
| private TaskRecord mTaskRecord; |
| private int mUid; |
| private boolean mCreateTask; |
| private ActivityStack mStack; |
| private int mActivityFlags; |
| private int mLaunchMode; |
| |
| ActivityBuilder(ActivityTaskManagerService service) { |
| mService = service; |
| } |
| |
| ActivityBuilder setComponent(ComponentName component) { |
| mComponent = component; |
| return this; |
| } |
| |
| ActivityBuilder setTargetActivity(String targetActivity) { |
| mTargetActivity = targetActivity; |
| return this; |
| } |
| |
| static ComponentName getDefaultComponent() { |
| return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, |
| DEFAULT_COMPONENT_PACKAGE_NAME); |
| } |
| |
| ActivityBuilder setTask(TaskRecord task) { |
| mTaskRecord = task; |
| return this; |
| } |
| |
| ActivityBuilder setActivityFlags(int flags) { |
| mActivityFlags = flags; |
| return this; |
| } |
| |
| ActivityBuilder setLaunchMode(int launchMode) { |
| mLaunchMode = launchMode; |
| return this; |
| } |
| |
| ActivityBuilder setStack(ActivityStack stack) { |
| mStack = stack; |
| return this; |
| } |
| |
| ActivityBuilder setCreateTask(boolean createTask) { |
| mCreateTask = createTask; |
| return this; |
| } |
| |
| ActivityBuilder setUid(int uid) { |
| mUid = uid; |
| return this; |
| } |
| |
| ActivityRecord build() { |
| if (mComponent == null) { |
| final int id = sCurrentActivityId++; |
| mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, |
| DEFAULT_COMPONENT_CLASS_NAME + id); |
| } |
| |
| if (mCreateTask) { |
| mTaskRecord = new TaskBuilder(mService.mStackSupervisor) |
| .setComponent(mComponent) |
| .setStack(mStack).build(); |
| } |
| |
| Intent intent = new Intent(); |
| intent.setComponent(mComponent); |
| final ActivityInfo aInfo = new ActivityInfo(); |
| aInfo.applicationInfo = new ApplicationInfo(); |
| aInfo.applicationInfo.packageName = mComponent.getPackageName(); |
| aInfo.applicationInfo.uid = mUid; |
| aInfo.packageName = mComponent.getPackageName(); |
| if (mTargetActivity != null) { |
| aInfo.targetActivity = mTargetActivity; |
| } |
| aInfo.flags |= mActivityFlags; |
| aInfo.launchMode = mLaunchMode; |
| |
| final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, |
| 0 /* launchedFromPid */, 0, null, intent, null, |
| aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, |
| 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, |
| mService.mStackSupervisor, null /* options */, null /* sourceRecord */); |
| spyOn(activity); |
| activity.mAppWindowToken = mock(AppWindowToken.class); |
| doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility(); |
| doCallRealMethod().when(activity.mAppWindowToken) |
| .setOrientation(anyInt(), any(), any()); |
| doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt()); |
| doNothing().when(activity).removeWindowContainer(); |
| doReturn(mock(Configuration.class)).when(activity.mAppWindowToken) |
| .getRequestedOverrideConfiguration(); |
| |
| if (mTaskRecord != null) { |
| mTaskRecord.addActivityToTop(activity); |
| } |
| |
| final WindowProcessController wpc = new WindowProcessController(mService, |
| mService.mContext.getApplicationInfo(), "name", 12345, |
| UserHandle.getUserId(12345), mock(Object.class), |
| mock(WindowProcessListener.class)); |
| wpc.setThread(mock(IApplicationThread.class)); |
| activity.setProcess(wpc); |
| return activity; |
| } |
| } |
| |
| /** |
| * Builder for creating new tasks. |
| */ |
| protected static class TaskBuilder { |
| // Default package name |
| static final String DEFAULT_PACKAGE = "com.bar"; |
| |
| private final ActivityStackSupervisor mSupervisor; |
| |
| private ComponentName mComponent; |
| private String mPackage; |
| private int mFlags = 0; |
| // Task id 0 is reserved in ARC for the home app. |
| private int mTaskId = 1; |
| private int mUserId = 0; |
| private IVoiceInteractionSession mVoiceSession; |
| private boolean mCreateStack = true; |
| |
| private ActivityStack mStack; |
| |
| TaskBuilder(ActivityStackSupervisor supervisor) { |
| mSupervisor = supervisor; |
| } |
| |
| TaskBuilder setComponent(ComponentName component) { |
| mComponent = component; |
| return this; |
| } |
| |
| TaskBuilder setPackage(String packageName) { |
| mPackage = packageName; |
| return this; |
| } |
| |
| /** |
| * Set to {@code true} by default, set to {@code false} to prevent the task from |
| * automatically creating a parent stack. |
| */ |
| TaskBuilder setCreateStack(boolean createStack) { |
| mCreateStack = createStack; |
| return this; |
| } |
| |
| TaskBuilder setVoiceSession(IVoiceInteractionSession session) { |
| mVoiceSession = session; |
| 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 setStack(ActivityStack stack) { |
| mStack = stack; |
| return this; |
| } |
| |
| TaskRecord build() { |
| if (mStack == null && mCreateStack) { |
| mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack( |
| WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); |
| } |
| |
| final ActivityInfo aInfo = new ActivityInfo(); |
| aInfo.applicationInfo = new ApplicationInfo(); |
| aInfo.applicationInfo.packageName = mPackage; |
| |
| Intent intent = new Intent(); |
| if (mComponent == null) { |
| mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, |
| DEFAULT_COMPONENT_CLASS_NAME); |
| } |
| |
| intent.setComponent(mComponent); |
| intent.setFlags(mFlags); |
| |
| final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo, |
| intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/); |
| task.userId = mUserId; |
| |
| if (mStack != null) { |
| mStack.moveToFront("test"); |
| mStack.addTask(task, true, "creating test task"); |
| task.setStack(mStack); |
| task.setTask(); |
| mStack.getTaskStack().addChild(task.mTask, 0); |
| } |
| |
| task.touchActiveTime(); |
| |
| return task; |
| } |
| |
| private static class TestTaskRecord extends TaskRecord { |
| TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info, |
| Intent intent, IVoiceInteractionSession voiceSession, |
| IVoiceInteractor voiceInteractor) { |
| super(service, taskId, info, intent, voiceSession, voiceInteractor); |
| } |
| |
| @Override |
| void createTask(boolean onTop, boolean showForAllUsers) { |
| setTask(); |
| } |
| |
| void setTask() { |
| Task mockTask = mock(Task.class); |
| mockTask.mTaskRecord = this; |
| doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any()); |
| setTask(mock(Task.class)); |
| } |
| } |
| } |
| |
| protected class TestActivityTaskManagerService extends ActivityTaskManagerService { |
| private PackageManagerInternal mPmInternal; |
| |
| // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS. |
| // We keep the reference in order to prevent creating it twice. |
| ActivityStackSupervisor mTestStackSupervisor; |
| |
| ActivityDisplay mDefaultDisplay; |
| |
| TestActivityTaskManagerService(Context context) { |
| super(context); |
| spyOn(this); |
| |
| mUgmInternal = mock(UriGrantsManagerInternal.class); |
| |
| mSupportsMultiWindow = true; |
| mSupportsMultiDisplay = true; |
| mSupportsSplitScreenMultiWindow = true; |
| mSupportsFreeformWindowManagement = true; |
| mSupportsPictureInPicture = true; |
| |
| final TestActivityManagerService am = |
| new TestActivityManagerService(mTestInjector, this); |
| |
| spyOn(getLifecycleManager()); |
| spyOn(getLockTaskController()); |
| doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); |
| // allow background activity starts by default |
| doReturn(true).when(this).isBackgroundActivityStartsEnabled(); |
| doNothing().when(this).updateCpuStats(); |
| } |
| |
| void setup(IntentFirewall intentFirewall, PendingIntentController intentController, |
| ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) { |
| mAmInternal = amInternal; |
| initialize(intentFirewall, intentController, looper); |
| initRootActivityContainerMocks(wm); |
| setWindowManager(wm); |
| createDefaultDisplay(); |
| } |
| |
| void initRootActivityContainerMocks(WindowManagerService wm) { |
| spyOn(mRootActivityContainer); |
| mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class)); |
| mRootActivityContainer.mWindowManager = wm; |
| mRootActivityContainer.mDisplayManager = |
| (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); |
| doNothing().when(mRootActivityContainer).setWindowManager(any()); |
| // Invoked during {@link ActivityStack} creation. |
| doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay(); |
| // Always keep things awake. |
| doReturn(true).when(mRootActivityContainer).hasAwakeDisplay(); |
| // Called when moving activity to pinned stack. |
| doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(), |
| anyBoolean()); |
| } |
| |
| void createDefaultDisplay() { |
| // Create a default display and put a home stack on it so that we'll always have |
| // something focusable. |
| mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY); |
| spyOn(mDefaultDisplay); |
| mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP); |
| mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); |
| final TaskRecord task = new TaskBuilder(mStackSupervisor) |
| .setStack(mDefaultDisplay.getHomeStack()).build(); |
| new ActivityBuilder(this).setTask(task).build(); |
| |
| doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay(); |
| } |
| |
| @Override |
| int handleIncomingUser(int callingPid, int callingUid, int userId, String name) { |
| return userId; |
| } |
| |
| @Override |
| void updateCpuStats() { |
| } |
| |
| @Override |
| void updateBatteryStats(ActivityRecord component, boolean resumed) { |
| } |
| |
| @Override |
| void updateActivityUsageStats(ActivityRecord activity, int event) { |
| } |
| |
| @Override |
| protected ActivityStackSupervisor createStackSupervisor() { |
| if (mTestStackSupervisor == null) { |
| mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper()); |
| } |
| return mTestStackSupervisor; |
| } |
| |
| @Override |
| PackageManagerInternal getPackageManagerInternalLocked() { |
| if (mPmInternal == null) { |
| mPmInternal = mock(PackageManagerInternal.class); |
| doReturn(false) |
| .when(mPmInternal) |
| .isPermissionsReviewRequired(anyString(), anyInt()); |
| } |
| return mPmInternal; |
| } |
| } |
| |
| private static class TestInjector extends ActivityManagerService.Injector { |
| private ServiceThread mHandlerThread; |
| |
| @Override |
| public Context getContext() { |
| return getInstrumentation().getTargetContext(); |
| } |
| |
| @Override |
| public AppOpsService getAppOpsService(File file, Handler handler) { |
| return null; |
| } |
| |
| @Override |
| public Handler getUiHandler(ActivityManagerService service) { |
| return mHandlerThread.getThreadHandler(); |
| } |
| |
| @Override |
| public boolean isNetworkRestrictedForUid(int uid) { |
| return false; |
| } |
| |
| void setUp() { |
| mHandlerThread = new ServiceThread("ActivityTestsThread", |
| Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */); |
| mHandlerThread.start(); |
| } |
| |
| void tearDown() { |
| mHandlerThread.quitSafely(); |
| } |
| } |
| |
| // TODO: Replace this with a mock object since we are no longer in AMS package. |
| /** |
| * An {@link ActivityManagerService} subclass which provides a test |
| * {@link ActivityStackSupervisor}. |
| */ |
| class TestActivityManagerService extends ActivityManagerService { |
| |
| TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) { |
| super(testInjector, testInjector.mHandlerThread); |
| spyOn(this); |
| |
| mWindowManager = prepareMockWindowManager(); |
| mUgmInternal = mock(UriGrantsManagerInternal.class); |
| |
| atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager, |
| testInjector.mHandlerThread.getLooper()); |
| |
| mActivityTaskManager = atm; |
| mAtmInternal = atm.mInternal; |
| |
| doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); |
| PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class); |
| doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked(); |
| doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt()); |
| doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt()); |
| } |
| } |
| |
| /** |
| * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on |
| * setup not available in the test environment. Also specifies an injector for |
| */ |
| protected class TestActivityStackSupervisor extends ActivityStackSupervisor { |
| private KeyguardController mKeyguardController; |
| |
| TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) { |
| super(service, looper); |
| spyOn(this); |
| mWindowManager = prepareMockWindowManager(); |
| mKeyguardController = mock(KeyguardController.class); |
| |
| // Do not schedule idle timeouts |
| doNothing().when(this).scheduleIdleTimeoutLocked(any()); |
| // unit test version does not handle launch wake lock |
| doNothing().when(this).acquireLaunchWakelock(); |
| doReturn(mKeyguardController).when(this).getKeyguardController(); |
| |
| mLaunchingActivity = mock(PowerManager.WakeLock.class); |
| |
| initialize(); |
| } |
| |
| @Override |
| public KeyguardController getKeyguardController() { |
| return mKeyguardController; |
| } |
| |
| @Override |
| void setWindowManager(WindowManagerService wm) { |
| mWindowManager = wm; |
| } |
| } |
| |
| protected static class TestActivityDisplay extends ActivityDisplay { |
| private final ActivityStackSupervisor mSupervisor; |
| |
| static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) { |
| return create(supervisor, displayId, new DisplayInfo()); |
| } |
| |
| static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId, |
| DisplayInfo info) { |
| if (displayId == DEFAULT_DISPLAY) { |
| return new TestActivityDisplay(supervisor, |
| supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId)); |
| } |
| final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, |
| info, DEFAULT_DISPLAY_ADJUSTMENTS); |
| return new TestActivityDisplay(supervisor, display); |
| } |
| |
| TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) { |
| super(supervisor.mService.mRootActivityContainer, display); |
| // Normally this comes from display-properties as exposed by WM. Without that, just |
| // hard-code to FULLSCREEN for tests. |
| setWindowingMode(WINDOWING_MODE_FULLSCREEN); |
| mSupervisor = supervisor; |
| } |
| |
| @SuppressWarnings("TypeParameterUnusedInFormals") |
| @Override |
| ActivityStack createStackUnchecked(int windowingMode, int activityType, |
| int stackId, boolean onTop) { |
| return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this) |
| .setWindowingMode(windowingMode).setActivityType(activityType) |
| .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build(); |
| } |
| |
| @Override |
| protected DisplayContent createDisplayContent() { |
| return WindowTestUtils.createTestDisplayContent(); |
| } |
| |
| void removeAllTasks() { |
| for (int i = 0; i < getChildCount(); i++) { |
| final ActivityStack stack = getChildAt(i); |
| for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) { |
| stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING); |
| } |
| } |
| } |
| } |
| |
| private static WindowManagerService sMockWindowManagerService; |
| |
| private static WindowManagerService prepareMockWindowManager() { |
| if (sMockWindowManagerService != null) { |
| return sMockWindowManagerService; |
| } |
| |
| final WindowManagerService service = mock(WindowManagerService.class); |
| service.mRoot = mock(RootWindowContainer.class); |
| |
| doAnswer((InvocationOnMock invocationOnMock) -> { |
| final Runnable runnable = invocationOnMock.<Runnable>getArgument(0); |
| if (runnable != null) { |
| runnable.run(); |
| } |
| return null; |
| }).when(service).inSurfaceTransaction(any()); |
| |
| sMockWindowManagerService = service; |
| return service; |
| } |
| |
| /** |
| * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a |
| * method is called. Note that its functionality depends on the implementations of the |
| * construction arguments. |
| */ |
| protected static class TestActivityStack |
| extends ActivityStack { |
| private int mOnActivityRemovedFromStackCount = 0; |
| |
| static final int IS_TRANSLUCENT_UNSET = 0; |
| static final int IS_TRANSLUCENT_FALSE = 1; |
| static final int IS_TRANSLUCENT_TRUE = 2; |
| private int mIsTranslucent = IS_TRANSLUCENT_UNSET; |
| |
| static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0; |
| static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1; |
| static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2; |
| private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET; |
| |
| TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, |
| int windowingMode, int activityType, boolean onTop, boolean createActivity) { |
| super(display, stackId, supervisor, windowingMode, activityType, onTop); |
| if (createActivity) { |
| new ActivityBuilder(mService).setCreateTask(true).setStack(this).build(); |
| if (onTop) { |
| // We move the task to front again in order to regain focus after activity |
| // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack} |
| // could be other stacks (e.g. home stack). |
| moveToFront("createActivityStack"); |
| } else { |
| moveToBack("createActivityStack", null); |
| } |
| } |
| } |
| |
| @Override |
| void onActivityRemovedFromStack(ActivityRecord r) { |
| mOnActivityRemovedFromStackCount++; |
| super.onActivityRemovedFromStack(r); |
| } |
| |
| // Returns the number of times {@link #onActivityRemovedFromStack} has been called |
| int onActivityRemovedFromStackInvocationCount() { |
| return mOnActivityRemovedFromStackCount; |
| } |
| |
| @Override |
| protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) { |
| mTaskStack = WindowTestUtils.createMockTaskStack(); |
| |
| // Primary pinned stacks require a non-empty out bounds to be set or else all tasks |
| // will be moved to the full screen stack. |
| if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { |
| outBounds.set(0, 0, 100, 100); |
| } |
| |
| } |
| |
| @Override |
| TaskStack getTaskStack() { |
| return mTaskStack; |
| } |
| |
| void setIsTranslucent(boolean isTranslucent) { |
| mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE; |
| } |
| |
| @Override |
| boolean isStackTranslucent(ActivityRecord starting) { |
| switch (mIsTranslucent) { |
| case IS_TRANSLUCENT_TRUE: |
| return true; |
| case IS_TRANSLUCENT_FALSE: |
| return false; |
| case IS_TRANSLUCENT_UNSET: |
| default: |
| return super.isStackTranslucent(starting); |
| } |
| } |
| |
| void setSupportsSplitScreen(boolean supportsSplitScreen) { |
| mSupportsSplitScreen = supportsSplitScreen |
| ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE; |
| } |
| |
| @Override |
| public boolean supportsSplitScreenWindowingMode() { |
| switch (mSupportsSplitScreen) { |
| case SUPPORTS_SPLIT_SCREEN_TRUE: |
| return true; |
| case SUPPORTS_SPLIT_SCREEN_FALSE: |
| return false; |
| case SUPPORTS_SPLIT_SCREEN_UNSET: |
| default: |
| return super.supportsSplitScreenWindowingMode(); |
| } |
| } |
| |
| @Override |
| void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, |
| boolean newTask, boolean keepCurTransition, |
| ActivityOptions options) { |
| } |
| } |
| |
| static class StackBuilder { |
| private final RootActivityContainer mRootActivityContainer; |
| private ActivityDisplay mDisplay; |
| private int mStackId = -1; |
| private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; |
| private int mActivityType = ACTIVITY_TYPE_STANDARD; |
| private boolean mOnTop = true; |
| private boolean mCreateActivity = true; |
| |
| StackBuilder(RootActivityContainer root) { |
| mRootActivityContainer = root; |
| mDisplay = mRootActivityContainer.getDefaultDisplay(); |
| } |
| |
| StackBuilder setWindowingMode(int windowingMode) { |
| mWindowingMode = windowingMode; |
| return this; |
| } |
| |
| StackBuilder setActivityType(int activityType) { |
| mActivityType = activityType; |
| return this; |
| } |
| |
| StackBuilder setStackId(int stackId) { |
| mStackId = stackId; |
| return this; |
| } |
| |
| StackBuilder setDisplay(ActivityDisplay display) { |
| mDisplay = display; |
| return this; |
| } |
| |
| StackBuilder setOnTop(boolean onTop) { |
| mOnTop = onTop; |
| return this; |
| } |
| |
| StackBuilder setCreateActivity(boolean createActivity) { |
| mCreateActivity = createActivity; |
| return this; |
| } |
| |
| @SuppressWarnings("TypeParameterUnusedInFormals") |
| ActivityStack build() { |
| final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); |
| if (mWindowingMode == WINDOWING_MODE_PINNED) { |
| return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor, |
| mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) { |
| @Override |
| Rect getDefaultPictureInPictureBounds(float aspectRatio) { |
| return new Rect(50, 50, 100, 100); |
| } |
| |
| @Override |
| void createTaskStack(int displayId, boolean onTop, Rect outBounds) { |
| mTaskStack = mock(TaskStack.class); |
| } |
| }; |
| } else { |
| return new TestActivityStack(mDisplay, stackId, |
| mRootActivityContainer.mStackSupervisor, mWindowingMode, |
| mActivityType, mOnTop, mCreateActivity); |
| } |
| } |
| |
| } |
| } |