Merge "Try to fix IndexOutBoundsException in Task#removeChild" into rvc-dev

This commit is contained in:
Charles Chen
2020-07-01 06:00:27 +00:00
committed by Android (Google) Code Review
3 changed files with 53 additions and 17 deletions

View File

@@ -1504,12 +1504,21 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
} }
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) { void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
task.removeTaskActivitiesLocked(reason); if (task.mInRemoveTask) {
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents); // Prevent recursion.
mService.getLockTaskController().clearLockedTask(task); return;
mService.getTaskChangeNotificationController().notifyTaskStackChanged(); }
if (task.isPersistable) { task.mInRemoveTask = true;
mService.notifyTaskPersisterLocked(null, true); try {
task.performClearTask(reason);
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
mService.getLockTaskController().clearLockedTask(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
} finally {
task.mInRemoveTask = false;
} }
} }

View File

@@ -435,6 +435,13 @@ class Task extends WindowContainer<WindowContainer> {
static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1; static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
private int mForceHiddenFlags = 0; private int mForceHiddenFlags = 0;
// TODO(b/160201781): Revisit double invocation issue in Task#removeChild.
/**
* Skip {@link ActivityStackSupervisor#removeTask(Task, boolean, boolean, String)} execution if
* {@code true} to prevent double traversal of {@link #mChildren} in a loop.
*/
boolean mInRemoveTask;
// When non-null, this is a transaction that will get applied on the next frame returned after // When non-null, this is a transaction that will get applied on the next frame returned after
// a relayout is requested from the client. While this is only valid on a leaf task; since the // a relayout is requested from the client. While this is only valid on a leaf task; since the
// transaction can effect an ancestor task, this also needs to keep track of the ancestor task // transaction can effect an ancestor task, this also needs to keep track of the ancestor task
@@ -1496,11 +1503,8 @@ class Task extends WindowContainer<WindowContainer> {
return autoRemoveRecents || (!hasChild() && !getHasBeenVisible()); return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
} }
/** /** Completely remove all activities associated with an existing task. */
* Completely remove all activities associated with an existing void performClearTask(String reason) {
* task starting at a specified index.
*/
private void performClearTaskAtIndexLocked(String reason) {
// Broken down into to cases to avoid object create due to capturing mStack. // Broken down into to cases to avoid object create due to capturing mStack.
if (getStack() == null) { if (getStack() == null) {
forAllActivities((r) -> { forAllActivities((r) -> {
@@ -1524,7 +1528,7 @@ class Task extends WindowContainer<WindowContainer> {
*/ */
void performClearTaskLocked() { void performClearTaskLocked() {
mReuseTask = true; mReuseTask = true;
performClearTaskAtIndexLocked("clear-task-all"); performClearTask("clear-task-all");
mReuseTask = false; mReuseTask = false;
} }
@@ -1585,11 +1589,6 @@ class Task extends WindowContainer<WindowContainer> {
return false; return false;
} }
void removeTaskActivitiesLocked(String reason) {
// Just remove the entire task.
performClearTaskAtIndexLocked(reason);
}
String lockTaskAuthToString() { String lockTaskAuthToString() {
switch (mLockTaskAuth) { switch (mLockTaskAuth) {
case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK"; case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";

View File

@@ -28,6 +28,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import android.graphics.Point; import android.graphics.Point;
@@ -158,4 +160,30 @@ public class TaskTests extends WindowTestsBase {
assertEquals(activity1, task1.isInTask(activity1)); assertEquals(activity1, task1.isInTask(activity1));
assertNull(task1.isInTask(activity2)); assertNull(task1.isInTask(activity2));
} }
@Test
public void testRemoveChildForOverlayTask() {
final Task task = createTaskStackOnDisplay(mDisplayContent);
final int taskId = task.mTaskId;
final ActivityRecord activity1 =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
final ActivityRecord activity2 =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
final ActivityRecord activity3 =
WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
activity1.setTaskOverlay(true);
activity2.setTaskOverlay(true);
activity3.setTaskOverlay(true);
assertEquals(3, task.getChildCount());
assertTrue(task.onlyHasTaskOverlayActivities(true));
task.removeChild(activity1);
verify(task.mStackSupervisor).removeTask(any(), anyBoolean(), anyBoolean(), anyString());
assertEquals(2, task.getChildCount());
task.forAllActivities((r) -> {
assertTrue(r.finishing);
});
}
} }