Make stacks without running activities being invisible

Stacks that don't have any running activities and not starting
one on it should be invisible, no matter they are the top
stack or not.

Use home stack as the focused stack if the display has no
focusable and visiable stack because we eventually need to
start home activity on it.

Bug: 117745438
Test: Manual test with the same reproduce steps
      atest com.android.server.am
Change-Id: Iceb9ad7b7fa388b5829bb0d18f3627905d5410d7
This commit is contained in:
Louis Chang
2018-10-17 15:14:45 +08:00
parent 0215e9930f
commit f2835df035
9 changed files with 165 additions and 114 deletions

View File

@@ -356,7 +356,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
return getOrCreateStack(windowingMode, activityType, onTop);
}
private int getNextStackId() {
@VisibleForTesting
int getNextStackId() {
return sNextFreeStackId++;
}

View File

@@ -48,41 +48,7 @@ import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.am.ActivityDisplay.POSITION_TOP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -102,8 +68,42 @@ import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.am.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_APP;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_STATES;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TASKS;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING;
import static com.android.server.am.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.am.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG;
import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.am.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static java.lang.Integer.MAX_VALUE;
import android.app.Activity;
@@ -1807,13 +1807,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
final ActivityRecord top = topRunningActivityLocked();
if (top == null && isInStackLocked(starting) == null && !isTopStackOnDisplay()) {
// Shouldn't be visible if you don't have any running activities, not starting one, and
// not the top stack on display.
return false;
}
final ActivityDisplay display = getDisplay();
boolean gotSplitScreenStack = false;
boolean gotOpaqueSplitScreenPrimary = false;
@@ -1822,9 +1815,16 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final boolean isAssistantType = isActivityTypeAssistant();
for (int i = display.getChildCount() - 1; i >= 0; --i) {
final ActivityStack other = display.getChildAt(i);
final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
if (other == this) {
// Should be visible if there is no other stack occluding it.
return true;
// Should be visible if there is no other stack occluding it, unless it doesn't
// have any running activities, not starting one and not home stack.
return hasRunningActivities || isInStackLocked(starting) != null
|| isActivityTypeHome();
}
if (!hasRunningActivities) {
continue;
}
final int otherWindowingMode = other.getWindowingMode();

View File

@@ -60,8 +60,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
public void testLastFocusedStackIsUpdatedWhenMovingStack() {
// Create a stack at bottom.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
final ActivityStack stack = display.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, !ON_TOP);
final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build();
final ActivityStack prevFocusedStack = display.getFocusedStack();
stack.moveToFront("moveStackToFront");
@@ -140,16 +139,14 @@ public class ActivityDisplayTests extends ActivityTestsBase {
*/
@Test
public void testTopRunningActivity() {
// Create stack to hold focus.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
final ActivityStack emptyStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
final KeyguardController keyguard = mSupervisor.getKeyguardController();
final ActivityStack stack = mSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
final ActivityStack stack = new StackBuilder(mSupervisor).build();
final ActivityRecord activity = stack.getTopActivity();
// Create empty stack on top.
final ActivityStack emptyStack =
new StackBuilder(mSupervisor).setCreateActivity(false).build();
// Make sure the top running activity is not affected when keyguard is not locked.
assertTopRunningActivity(activity, display);
@@ -159,8 +156,8 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
// Change focus to stack with activity.
stack.moveToFront("focusChangeToTestStack");
// Move stack with activity to top.
stack.moveToFront("testStackToFront");
assertEquals(stack, display.getFocusedStack());
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
@@ -175,11 +172,10 @@ public class ActivityDisplayTests extends ActivityTestsBase {
// Ensure the show when locked activity is returned.
assertTopRunningActivity(showWhenLockedActivity, display);
// Change focus back to empty stack.
emptyStack.moveToFront("focusChangeToEmptyStack");
assertEquals(emptyStack, display.getFocusedStack());
// If there is no running activity in focused stack, the running activity in next focusable
// stack should be returned.
// Move empty stack to front. The running activity in focusable stack which below the
// empty stack should be returned.
emptyStack.moveToFront("emptyStackToFront");
assertEquals(stack, display.getFocusedStack());
assertTopRunningActivity(showWhenLockedActivity, display);
}

View File

@@ -79,10 +79,9 @@ public class ActivityRecordTests extends ActivityTestsBase {
super.setUp();
setupActivityTaskManagerService();
mStack = mSupervisor.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
mActivity = new ActivityBuilder(mService).setTask(mTask).build();
mStack = new StackBuilder(mSupervisor).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
}
@Test

View File

@@ -359,15 +359,12 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
public void testFindTaskToMoveToFrontWhenRecentsOnTop() throws Exception {
// Create stack/task on default display.
final ActivityDisplay display = mSupervisor.getDefaultDisplay();
final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */);
final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build();
final TaskRecord targetTask = targetStack.getChildAt(0);
// Create Recents on top of the display.
final ActivityStack stack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_RECENTS, true /* onTop */);
final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
new ActivityBuilder(mService).setTask(task).build();
final ActivityStack stack =
new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build();
final String reason = "findTaskToMoveToFront";
mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,

View File

@@ -26,8 +26,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -361,14 +361,12 @@ public class ActivityStackTests extends ActivityTestsBase {
translucentStack.topRunningActivityLocked();
topRunningTranslucentActivity.finishing = true;
// Home shouldn't be visible since its activity is marked as finishing and it isn't the top
// of the stack list.
assertFalse(homeStack.shouldBeVisible(null /* starting */));
// Home stack should be visible even there are no running activities.
assertTrue(homeStack.shouldBeVisible(null /* starting */));
// Home should be visible if we are starting an activity within it.
assertTrue(homeStack.shouldBeVisible(topRunningHomeActivity /* starting */));
// The translucent stack should be visible since it is the top of the stack list even though
// it has its activity marked as finishing.
assertTrue(translucentStack.shouldBeVisible(null /* starting */));
// The translucent stack shouldn't be visible since its activity marked as finishing.
assertFalse(translucentStack.shouldBeVisible(null /* starting */));
}
@Test
@@ -672,9 +670,10 @@ public class ActivityStackTests extends ActivityTestsBase {
// There is still an activity1 in stack1 so the activity2 should be added to finishing list
// that will be destroyed until idle.
stack2.getTopActivity().visible = true;
final ActivityRecord activity2 = finishCurrentActivity(stack2);
assertEquals(FINISHING, activity2.getState());
assertTrue(mSupervisor.mFinishingActivities.contains(activity2));
assertEquals(STOPPING, activity2.getState());
assertTrue(mSupervisor.mStoppingActivities.contains(activity2));
// The display becomes empty. Since there is no next activity to be idle, the activity
// should be destroyed immediately with updating configuration to restore original state.

View File

@@ -39,14 +39,6 @@ import static org.mockito.Mockito.spy;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.content.pm.PackageManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
import org.mockito.invocation.InvocationOnMock;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
@@ -54,6 +46,7 @@ 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;
@@ -73,7 +66,10 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.ServiceThread;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.DisplayWindowController;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.RootWindowContainerController;
import com.android.server.wm.StackWindowController;
@@ -83,7 +79,9 @@ import com.android.server.wm.WindowTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import java.io.File;
import java.util.List;
@@ -608,23 +606,9 @@ public class ActivityTestsBase {
@Override
<T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
if (windowingMode == WINDOWING_MODE_PINNED) {
return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
@Override
PinnedStackWindowController createStackWindowController(int displayId,
boolean onTop, Rect outBounds) {
return mock(PinnedStackWindowController.class);
}
};
} else {
return (T) new TestActivityStack(
this, stackId, mSupervisor, windowingMode, activityType, onTop);
}
return new StackBuilder(mSupervisor).setDisplay(this)
.setWindowingMode(windowingMode).setActivityType(activityType)
.setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
}
@Override
@@ -677,8 +661,19 @@ public class ActivityTestsBase {
private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
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
@@ -750,4 +745,71 @@ public class ActivityTestsBase {
ActivityOptions options) {
}
}
protected static class StackBuilder {
private final ActivityStackSupervisor mSupervisor;
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(ActivityStackSupervisor supervisor) {
mSupervisor = supervisor;
mDisplay = mSupervisor.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;
}
<T extends ActivityStack> T build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
if (mWindowingMode == WINDOWING_MODE_PINNED) {
return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
@Override
PinnedStackWindowController createStackWindowController(int displayId,
boolean onTop, Rect outBounds) {
return mock(PinnedStackWindowController.class);
}
};
} else {
return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode,
mActivityType, mOnTop, mCreateActivity);
}
}
}
}

View File

@@ -883,7 +883,7 @@ public class RecentTasksTest extends ActivityTestsBase {
MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
super(display, LAST_STACK_ID++, supervisor, WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true);
ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
mDisplay = display;
}

View File

@@ -16,9 +16,7 @@
package com.android.server.am;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -30,7 +28,6 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.platform.test.annotations.Presubmit;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -72,8 +69,8 @@ public class RunningTasksTest extends ActivityTestsBase {
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
final ActivityStack stack = new TestActivityStack(display, stackIndex, mSupervisor,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true);
final ActivityStack stack =
new StackBuilder(mSupervisor).setCreateActivity(false).build();
display.addChild(stack, POSITION_BOTTOM);
}