TaskOrganizer: Wait for apps to draw.

A Task may appear before the windows inside have finished drawing.
Rather than hand the burden to the TaskOrganizer of waiting for a
drawn signal, we simply defer emitting the leash until the draw
signal is received.

Bug: 152134500
Test: TaskOrganizerTests
Change-Id: I776b33fc637ac7f6d4ba2fe60b93cedf75ba19c0
This commit is contained in:
Robert Carr
2020-03-24 15:22:21 -07:00
parent 0810d57f95
commit de96c8acce
5 changed files with 154 additions and 49 deletions

View File

@@ -5201,7 +5201,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
finishLaunchTickingLocked();
if (task != null) {
task.hasBeenVisible = true;
task.setHasBeenVisible(true);
}
}

View File

@@ -258,7 +258,7 @@ class Task extends WindowContainer<WindowContainer> {
boolean autoRemoveRecents; // If true, we should automatically remove the task from
// recents when activity finishes
boolean askedCompatMode;// Have asked the user about compat mode for this task.
boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
private boolean mHasBeenVisible; // Set if any activities in the task have been visible
String stringName; // caching of toString() result.
boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
@@ -483,6 +483,10 @@ class Task extends WindowContainer<WindowContainer> {
*/
ITaskOrganizer mTaskOrganizer;
private int mLastTaskOrganizerWindowingMode = -1;
/**
* Prevent duplicate calls to onTaskAppeared.
*/
boolean mTaskAppearedSent;
/**
* Last Picture-in-Picture params applicable to the task. Updated when the app
@@ -1517,7 +1521,7 @@ class Task extends WindowContainer<WindowContainer> {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
// the user.
return autoRemoveRecents || (!hasChild() && !hasBeenVisible);
return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
}
/**
@@ -2030,7 +2034,7 @@ class Task extends WindowContainer<WindowContainer> {
}
private void saveLaunchingStateIfNeeded(DisplayContent display) {
if (!hasBeenVisible) {
if (!getHasBeenVisible()) {
// Not ever visible to user.
return;
}
@@ -3558,7 +3562,7 @@ class Task extends WindowContainer<WindowContainer> {
pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
}
pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId());
pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
pw.print(prefix + "mHasBeenVisible=" + getHasBeenVisible());
pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
pw.print(" isResizeable=" + isResizeable());
@@ -4087,14 +4091,42 @@ class Task extends WindowContainer<WindowContainer> {
super.reparentSurfaceControl(t, newParent);
}
void setHasBeenVisible(boolean hasBeenVisible) {
mHasBeenVisible = hasBeenVisible;
if (hasBeenVisible) {
sendTaskAppeared();
if (!isRootTask()) {
getRootTask().setHasBeenVisible(true);
}
}
}
boolean getHasBeenVisible() {
return mHasBeenVisible;
}
/** In the case that these three conditions are true, we want to send the Task to
* the organizer:
* 1. We have a SurfaceControl
* 2. An organizer has been set
* 3. We have finished drawing
* Any time any of these conditions are updated, the updating code should call
* sendTaskAppeared.
*/
private boolean taskAppearedReady() {
return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible();
}
private void sendTaskAppeared() {
if (mSurfaceControl != null && mTaskOrganizer != null) {
if (taskAppearedReady() && !mTaskAppearedSent) {
mTaskAppearedSent = true;
mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
}
}
private void sendTaskVanished() {
if (mTaskOrganizer != null) {
if (mTaskOrganizer != null && mTaskAppearedSent) {
mTaskAppearedSent = false;
mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
}
}
@@ -4113,6 +4145,7 @@ class Task extends WindowContainer<WindowContainer> {
void taskOrganizerUnregistered() {
mTaskOrganizer = null;
mTaskAppearedSent = false;
mLastTaskOrganizerWindowingMode = -1;
onTaskOrganizerChanged();
if (mCreatedByOrganizer) {

View File

@@ -120,7 +120,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
.build();
mTestTask.mUserId = TEST_USER_ID;
mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
mTestTask.hasBeenVisible = true;
mTestTask.setHasBeenVisible(true);
mTaskWithDifferentComponent = new TaskBuilder(mSupervisor)
.setComponent(ALTERNATIVE_COMPONENT).build();
@@ -346,7 +346,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
.build();
anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500);
anotherTaskOfTheSameUser.hasBeenVisible = true;
anotherTaskOfTheSameUser.setHasBeenVisible(true);
mTarget.saveTask(anotherTaskOfTheSameUser);
stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
@@ -358,7 +358,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase {
.build();
anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600);
anotherTaskOfDifferentUser.hasBeenVisible = true;
anotherTaskOfDifferentUser.setHasBeenVisible(true);
mTarget.saveTask(anotherTaskOfDifferentUser);
mTarget.onCleanupUser(TEST_USER_ID);

View File

@@ -96,10 +96,28 @@ public class TaskOrganizerTests extends WindowTestsBase {
return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
}
Task createTask(ActivityStack stack, boolean fakeDraw) {
final Task task = createTaskInStack(stack, 0);
if (fakeDraw) {
task.setHasBeenVisible(true);
}
return task;
}
Task createTask(ActivityStack stack) {
// Fake draw notifications for most of our tests.
return createTask(stack, true);
}
ActivityStack createStack() {
return createTaskStackOnDisplay(mDisplayContent);
}
@Test
public void testAppearVanish() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
@@ -109,10 +127,43 @@ public class TaskOrganizerTests extends WindowTestsBase {
verify(organizer).onTaskVanished(any());
}
@Test
public void testAppearWaitsForVisibility() throws RemoteException {
final ActivityStack stack = createStack();
final Task task = createTask(stack, false);
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
verify(organizer, never()).onTaskAppeared(any());
task.setHasBeenVisible(true);
assertTrue(stack.getHasBeenVisible());
verify(organizer).onTaskAppeared(any());
task.removeImmediately();
verify(organizer).onTaskVanished(any());
}
@Test
public void testNoVanishedIfNoAppear() throws RemoteException {
final ActivityStack stack = createStack();
final Task task = createTask(stack, false /* hasBeenVisible */);
final ITaskOrganizer organizer = registerMockOrganizer();
// In this test we skip making the Task visible, and verify
// that even though a TaskOrganizer is set remove doesn't emit
// a vanish callback, because we never emitted appear.
task.setTaskOrganizer(organizer);
verify(organizer, never()).onTaskAppeared(any());
task.removeImmediately();
verify(organizer, never()).onTaskVanished(any());
}
@Test
public void testSwapOrganizer() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -125,8 +176,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testSwapWindowingModes() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -139,8 +190,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testClearOrganizer() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setTaskOrganizer(organizer);
@@ -154,8 +205,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testUnregisterOrganizer() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -169,12 +220,12 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
final Task task2 = createTaskInStack(stack2, 0 /* userId */);
final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent);
final Task task3 = createTaskInStack(stack3, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ActivityStack stack2 = createStack();
final Task task2 = createTask(stack2);
final ActivityStack stack3 = createStack();
final Task task3 = createTask(stack3);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
// First organizer is registered, verify a task appears when changing windowing mode
@@ -202,9 +253,9 @@ public class TaskOrganizerTests extends WindowTestsBase {
public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final Task task2 = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final Task task2 = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
verify(organizer, times(1)).onTaskAppeared(any());
@@ -214,8 +265,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
final ActivityStack stack = createStack();
final Task task = createTask(stack);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -560,8 +611,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testTrivialBLASTCallback() throws RemoteException {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityStack stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
spyOn(task);
@@ -582,8 +633,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testOverlappingBLASTCallback() throws RemoteException {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityStack stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
spyOn(task);
@@ -611,8 +662,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithWindow() {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityStack stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
makeWindowVisible(w);
@@ -635,8 +686,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithInvisibleWindow() {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityStack stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
@@ -657,8 +708,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
@Test
public void testBLASTCallbackWithChildWindow() {
final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stackController1, 0 /* userId */);
final ActivityStack stackController1 = createStack();
final Task task = createTask(stackController1);
final ITaskOrganizer organizer = registerMockOrganizer();
final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
@@ -708,6 +759,8 @@ public class TaskOrganizerTests extends WindowTestsBase {
record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
spyOn(record);
doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
record.getRootTask().setHasBeenVisible(true);
return record;
}
@@ -755,4 +808,23 @@ public class TaskOrganizerTests extends WindowTestsBase {
assertEquals(3, ratio.getNumerator());
assertEquals(4, ratio.getDenominator());
}
@Test
public void testPreventDuplicateAppear() throws RemoteException {
final ActivityStack stack = createStack();
final Task task = createTask(stack);
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
// setHasBeenVisible was already called once by the set-up code.
task.setHasBeenVisible(true);
verify(organizer, times(1)).onTaskAppeared(any());
task.taskOrganizerUnregistered();
task.setTaskOrganizer(organizer);
verify(organizer, times(2)).onTaskAppeared(any());
task.removeImmediately();
verify(organizer).onTaskVanished(any());
}
}

View File

@@ -874,11 +874,11 @@ public class TaskRecordTests extends ActivityTestsBase {
spyOn(persister);
final Task task = getTestTask();
task.hasBeenVisible = false;
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.hasBeenVisible = true;
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
verify(persister).saveTask(task, task.getDisplayContent());
@@ -890,7 +890,7 @@ public class TaskRecordTests extends ActivityTestsBase {
spyOn(persister);
final Task task = getTestTask();
task.hasBeenVisible = false;
task.setHasBeenVisible(false);
task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
final DisplayContent oldDisplay = task.getDisplayContent();
@@ -900,7 +900,7 @@ public class TaskRecordTests extends ActivityTestsBase {
persister.getLaunchParams(task, null, params);
assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
task.hasBeenVisible = true;
task.setHasBeenVisible(true);
task.removeImmediately();
verify(persister).saveTask(task, oldDisplay);
@@ -915,10 +915,10 @@ public class TaskRecordTests extends ActivityTestsBase {
spyOn(persister);
final Task task = getTestTask();
task.hasBeenVisible = false;
task.setHasBeenVisible(false);
task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.hasBeenVisible = true;
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
verify(persister, never()).saveTask(same(task), any());
@@ -930,11 +930,11 @@ public class TaskRecordTests extends ActivityTestsBase {
spyOn(persister);
final Task task = getTestTask();
task.hasBeenVisible = false;
task.setHasBeenVisible(false);
task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
task.getStack().setWindowingMode(WINDOWING_MODE_PINNED);
task.hasBeenVisible = true;
task.setHasBeenVisible(true);
task.onConfigurationChanged(task.getParent().getConfiguration());
verify(persister, never()).saveTask(same(task), any());