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:
@@ -5201,7 +5201,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
finishLaunchTickingLocked();
|
||||
if (task != null) {
|
||||
task.hasBeenVisible = true;
|
||||
task.setHasBeenVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user