/* * Copyright (C) 2017 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.am; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; import org.mockito.invocation.InvocationOnMock; import android.app.ActivityManager; 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.res.Configuration; import android.graphics.Rect; import android.os.HandlerThread; import android.os.Looper; import android.support.test.InstrumentationRegistry; import com.android.server.AttributeCache; import com.android.server.wm.AppWindowContainerController; import com.android.server.wm.StackWindowController; import com.android.server.wm.TaskWindowContainerController; import com.android.server.wm.WindowManagerService; import com.android.server.wm.WindowTestUtils; import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; /** * A base class to handle common operations in activity related unit tests. */ public class ActivityTestsBase { private final Context mContext = InstrumentationRegistry.getContext(); private HandlerThread mHandlerThread; // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must // be called at before any tests. private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHandlerThread = new HandlerThread("ActivityTestsBaseThread"); mHandlerThread.start(); } @After public void tearDown() { mHandlerThread.quitSafely(); } protected ActivityManagerService createActivityManagerService() { final ActivityManagerService service = new TestActivityManagerService(mContext); service.mWindowManager = WindowTestUtils.getMockWindowManagerService(); return service; } protected static ActivityStack createActivityStack(ActivityManagerService service, int stackId, int displayId, boolean onTop) { if (service.mStackSupervisor instanceof TestActivityStackSupervisor) { return ((TestActivityStackSupervisor) service.mStackSupervisor) .createTestStack(service, stackId, onTop); } return null; } protected static ActivityRecord createActivity(ActivityManagerService service, ComponentName component, TaskRecord task) { Intent intent = new Intent(); intent.setComponent(component); final ActivityInfo aInfo = new ActivityInfo(); aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = component.getPackageName(); AttributeCache.init(service.mContext); final ActivityRecord activity = new ActivityRecord(service, null /* caller */, 0 /* launchedFromPid */, 0, null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, service.mStackSupervisor, null /* container */, null /* options */, null /* sourceRecord */); activity.mWindowContainerController = mock(AppWindowContainerController.class); if (task != null) { task.addActivityToTop(activity); } return activity; } protected static TaskRecord createTask(ActivityManagerService service, ComponentName component, int stackId) { final ActivityInfo aInfo = new ActivityInfo(); aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = component.getPackageName(); Intent intent = new Intent(); intent.setComponent(component); final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/, null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo()); final ActivityStack stack = service.mStackSupervisor.getStack(stackId, true /*createStaticStackIfNeeded*/, true /*onTop*/); stack.addTask(task, true, "creating test task"); task.setStack(stack); task.setWindowContainerController(mock(TaskWindowContainerController.class)); return task; } /** * An {@link ActivityManagerService} subclass which provides a test * {@link ActivityStackSupervisor}. */ protected static class TestActivityManagerService extends ActivityManagerService { public TestActivityManagerService(Context context) { super(context); mSupportsMultiWindow = true; mSupportsMultiDisplay = true; mWindowManager = WindowTestUtils.getWindowManagerService(context); } @Override protected ActivityStackSupervisor createStackSupervisor() { return new TestActivityStackSupervisor(this, mHandlerThread.getLooper()); } } /** * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on * setup not available in the test environment. Also specifies an injector for */ protected static class TestActivityStackSupervisor extends ActivityStackSupervisor { public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) { super(service, looper); mWindowManager = prepareMockWindowManager(); } // No home stack is set. @Override void moveHomeStackToFront(String reason) { } // Invoked during {@link ActivityStack} creation. @Override void updateUIDsPresentOnDisplay() { } // Just return the current front task. @Override ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) { return mFocusedStack; } // Called when moving activity to pinned stack. @Override void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, boolean preserveWindows) { } public T createTestStack(ActivityManagerService service, int stackId, boolean onTop) { final ActivityDisplay display = new ActivityDisplay(); final TestActivityContainer container = new TestActivityContainer(service, stackId, display, onTop); mActivityContainers.put(stackId, container); return (T) container.getStack(); } @Override protected T getStack(int stackId, boolean createStaticStackIfNeeded, boolean createOnTop) { final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop); if (stack != null || !createStaticStackIfNeeded) { return stack; } return createTestStack(mService, stackId, createOnTop); } private class TestActivityContainer extends ActivityContainer { private final ActivityManagerService mService; private boolean mOnTop; private int mStackId; private ActivityStack mStack; TestActivityContainer(ActivityManagerService service, int stackId, ActivityDisplay activityDisplay, boolean onTop) { super(stackId, activityDisplay, onTop); mService = service; } @Override protected void createStack(int stackId, boolean onTop) { // normally stack creation is done here. However we need to do it on demand since // we cannot set {@link mService} by the time the super constructor calling this // method is invoked. mOnTop = onTop; mStackId = stackId; } public ActivityStack getStack() { if (mStack == null) { final RecentTasks recents = new RecentTasks(mService, mService.mStackSupervisor); mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID ? new PinnedActivityStack(this, recents, mOnTop) : new TestActivityStack(this, recents, mOnTop); } return mStack; } } } private static WindowManagerService prepareMockWindowManager() { final WindowManagerService service = mock(WindowManagerService.class); doAnswer((InvocationOnMock invocationOnMock) -> { final Runnable runnable = invocationOnMock.getArgument(0); if (runnable != null) { runnable.run(); } return null; }).when(service).inSurfaceTransaction(any()); return service; } protected interface ActivityStackReporter { int onActivityRemovedFromStackInvocationCount(); } /** * Override of {@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 implements ActivityStackReporter { private int mOnActivityRemovedFromStackCount = 0; private T mContainerController; TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer, RecentTasks recentTasks, boolean onTop) { super(activityContainer, recentTasks, onTop); } @Override void onActivityRemovedFromStack(ActivityRecord r) { mOnActivityRemovedFromStackCount++; super.onActivityRemovedFromStack(r); } // Returns the number of times {@link #onActivityRemovedFromStack} has been called @Override public int onActivityRemovedFromStackInvocationCount() { return mOnActivityRemovedFromStackCount; } @Override protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); return mContainerController; } @Override T getWindowContainerController() { return mContainerController; } } protected static class ActivityStackBuilder { private boolean mOnTop = true; private int mStackId = 0; private int mDisplayId = 1; private final ActivityManagerService mService; public ActivityStackBuilder(ActivityManagerService ams) { mService = ams; } public ActivityStackBuilder setOnTop(boolean onTop) { mOnTop = onTop; return this; } public ActivityStackBuilder setStackId(int id) { mStackId = id; return this; } public ActivityStackBuilder setDisplayId(int id) { mDisplayId = id; return this; } public ActivityStack build() { return createActivityStack(mService, mStackId, mDisplayId, mOnTop); } } }