[2/n] Pin ActivityStack

Make TaskContainer#mContainers private. This is a preliminary
step of handling the pinning container in the following CLs.

Noted that the #getTaskFragmentContainers and #getSplitContainers
returns the real list to improve the efficiency, but we shouldn't
make modifications on the returned list directly.

Bug: 208573140
Test: atest SplitControllerTest TaskContainerTest
Change-Id: Ia5b298c402f287777c2cbdd4fb1bb23c8c5102b7
This commit is contained in:
Louis Chang
2023-04-19 04:17:07 +00:00
parent 82db3963d2
commit 44d6c6c242
5 changed files with 77 additions and 38 deletions

View File

@@ -308,7 +308,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
forAllTaskContainers(taskContainer -> {
synchronized (mLock) {
final List<TaskFragmentContainer> containers = taskContainer.mContainers;
final List<TaskFragmentContainer> containers =
taskContainer.getTaskFragmentContainers();
// Clean up the TaskFragmentContainers by the z-order from the lowest.
for (int i = 0; i < containers.size(); i++) {
final TaskFragmentContainer container = containers.get(i);
@@ -611,8 +612,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@NonNull TaskContainer taskContainer) {
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
final List<TaskFragmentContainer> containers =
new ArrayList<>(taskContainer.mContainers);
final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
@@ -1331,7 +1331,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Check pending appeared activity first because there can be a delay for the server
// update.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasPendingAppearedActivity(activityToken)) {
@@ -1342,7 +1343,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Check appeared activity if there is no such pending appeared activity.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (container.hasAppearedActivity(activityToken)) {
@@ -1472,7 +1474,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
void removeContainers(@NonNull TaskContainer taskContainer,
@NonNull List<TaskFragmentContainer> containers) {
// Remove all split containers that included this one
taskContainer.mContainers.removeAll(containers);
taskContainer.removeTaskFragmentContainers(containers);
// Marked as a pending removal which will be removed after it is actually removed on the
// server side (#onTaskFragmentVanished).
// In this way, we can keep track of the Task bounds until we no longer have any
@@ -1497,7 +1499,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
taskContainer.removeSplitContainers(containersToRemove);
// Cleanup any dependent references.
for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) {
final List<TaskFragmentContainer> taskFragmentContainers =
taskContainer.getTaskFragmentContainers();
for (TaskFragmentContainer containerToUpdate : taskFragmentContainers) {
containerToUpdate.removeContainersToFinishOnExit(containers);
}
}
@@ -1536,8 +1540,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskContainer == null) {
return null;
}
for (int i = taskContainer.mContainers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = taskContainer.mContainers.get(i);
final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers();
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
if (!container.isFinished() && (container.getRunningActivityCount() > 0
// We may be waiting for the top TaskFragment to become non-empty after
// creation. In that case, we don't want to treat the TaskFragment below it as
@@ -1933,7 +1938,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.getTaskFragmentContainers();
for (TaskFragmentContainer container : containers) {
if (container.getTaskFragmentToken().equals(fragmentToken)) {
return container;
@@ -2094,7 +2100,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.mContainers;
.getTaskFragmentContainers();
for (int j = containers.size() - 1; j >= 0; j--) {
final TaskFragmentContainer container = containers.get(j);
if (!container.hasActivity(activityToken)

View File

@@ -51,7 +51,7 @@ class TaskContainer {
/** Active TaskFragments in this Task. */
@NonNull
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
private final List<TaskFragmentContainer> mContainers = new ArrayList<>();
/** Active split pairs in this Task. */
@NonNull
@@ -207,9 +207,13 @@ class TaskContainer {
return false;
}
/**
* Returns a list of {@link SplitContainer}. Do not modify the containers directly on the
* returned list. Use {@link #addSplitContainer} or {@link #removeSplitContainers} instead.
*/
@NonNull
List<SplitContainer> getSplitContainers() {
return new ArrayList<>(mSplitContainers);
return mSplitContainers;
}
void addSplitContainer(@NonNull SplitContainer splitContainer) {
@@ -220,6 +224,36 @@ class TaskContainer {
mSplitContainers.removeAll(containers);
}
void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
mContainers.add(taskFragmentContainer);
}
void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) {
mContainers.add(index, taskFragmentContainer);
}
void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) {
mContainers.remove(taskFragmentContainer);
}
void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) {
mContainers.removeAll(taskFragmentContainer);
}
void clearTaskFragmentContainer() {
mContainers.clear();
}
/**
* Returns a list of {@link TaskFragmentContainer}. Do not modify the containers directly on
* the returned list. Use {@link #addTaskFragmentContainer},
* {@link #removeTaskFragmentContainer} or other related methods instead.
*/
@NonNull
List<TaskFragmentContainer> getTaskFragmentContainers() {
return mContainers;
}
/** Adds the descriptors of split states in this Task to {@code outSplitStates}. */
void getSplitStates(@NonNull List<SplitInfo> outSplitStates) {
for (SplitContainer container : mSplitContainers) {

View File

@@ -180,23 +180,25 @@ class TaskFragmentContainer {
throw new IllegalArgumentException(
"pairedPrimaryContainer must be in the same Task");
}
final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer);
taskContainer.mContainers.add(primaryIndex + 1, this);
final int primaryIndex = taskContainer.indexOf(pairedPrimaryContainer);
taskContainer.addTaskFragmentContainer(primaryIndex + 1, this);
} else if (pendingAppearedActivity != null) {
// The TaskFragment will be positioned right above the pending appeared Activity. If any
// existing TaskFragment is empty with pending Intent, it is likely that the Activity of
// the pending Intent hasn't been created yet, so the new Activity should be below the
// empty TaskFragment.
int i = taskContainer.mContainers.size() - 1;
final List<TaskFragmentContainer> containers =
taskContainer.getTaskFragmentContainers();
int i = containers.size() - 1;
for (; i >= 0; i--) {
final TaskFragmentContainer container = taskContainer.mContainers.get(i);
final TaskFragmentContainer container = containers.get(i);
if (!container.isEmpty() || container.getPendingAppearedIntent() == null) {
break;
}
}
taskContainer.mContainers.add(i + 1, this);
taskContainer.addTaskFragmentContainer(i + 1, this);
} else {
taskContainer.mContainers.add(this);
taskContainer.addTaskFragmentContainer(this);
}
if (pendingAppearedActivity != null) {
addPendingAppearedActivity(pendingAppearedActivity);

View File

@@ -183,23 +183,23 @@ public class SplitControllerTest {
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
taskContainer.mContainers.add(tf2);
taskContainer.addTaskFragmentContainer(tf2);
// tf3 is finished so is not active.
final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class);
doReturn(true).when(tf3).isFinished();
doReturn(false).when(tf3).isWaitingActivityAppear();
taskContainer.mContainers.add(tf3);
taskContainer.addTaskFragmentContainer(tf3);
mSplitController.mTaskContainers.put(TASK_ID, taskContainer);
assertWithMessage("Must return tf2 because tf3 is not active.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
taskContainer.mContainers.remove(tf3);
taskContainer.removeTaskFragmentContainer(tf3);
assertWithMessage("Must return tf2 because tf2 has running activity.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2);
taskContainer.mContainers.remove(tf2);
taskContainer.removeTaskFragmentContainer(tf2);
assertWithMessage("Must return tf because we are waiting for tf1 to appear.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1);
@@ -365,7 +365,8 @@ public class SplitControllerTest {
final Activity r1 = createMockActivity();
addSplitTaskFragments(r0, r1);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
final TaskFragmentContainer taskFragmentContainer = taskContainer.mContainers.get(0);
final TaskFragmentContainer taskFragmentContainer =
taskContainer.getTaskFragmentContainers().get(0);
spyOn(taskContainer);
// No update when the Task is invisible.
@@ -1092,7 +1093,7 @@ public class SplitControllerTest {
verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
assertTrue(taskContainer.mContainers.isEmpty());
assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
assertTrue(taskContainer.getSplitContainers().isEmpty());
}
@@ -1365,15 +1366,13 @@ public class SplitControllerTest {
TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
.mContainers;
assertEquals(containers.get(0), tf);
final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
assertEquals(taskContainer.getTaskFragmentContainers().get(0), tf);
mSplitController.finishActivityStacks(Collections.singleton(tf.getTaskFragmentToken()));
verify(mSplitPresenter).deleteTaskFragment(any(), eq(tf.getTaskFragmentToken()));
assertTrue(containers.isEmpty());
assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test
@@ -1383,10 +1382,8 @@ public class SplitControllerTest {
bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID)
.mContainers;
assertEquals(containers.size(), 2);
final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
assertEquals(taskContainer.getTaskFragmentContainers().size(), 2);
Set<IBinder> activityStackTokens = new ArraySet<>(new IBinder[]{
topTf.getTaskFragmentToken(), bottomTf.getTaskFragmentToken()});
@@ -1405,7 +1402,7 @@ public class SplitControllerTest {
+ "regardless of the order in ActivityStack set",
topTf.getTaskFragmentToken(), fragmentTokens.get(1));
assertTrue(containers.isEmpty());
assertTrue(taskContainer.getTaskFragmentContainers().isEmpty());
}
@Test

View File

@@ -127,7 +127,7 @@ public class TaskContainerTest {
assertFalse(taskContainer.isEmpty());
taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
taskContainer.mContainers.clear();
taskContainer.clearTaskFragmentContainer();
assertFalse(taskContainer.isEmpty());
}
@@ -152,13 +152,13 @@ public class TaskContainerTest {
assertNull(taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class);
taskContainer.mContainers.add(tf0);
taskContainer.addTaskFragmentContainer(tf0);
final Activity activity0 = mock(Activity.class);
doReturn(activity0).when(tf0).getTopNonFinishingActivity();
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf1 = mock(TaskFragmentContainer.class);
taskContainer.mContainers.add(tf1);
taskContainer.addTaskFragmentContainer(tf1);
assertEquals(activity0, taskContainer.getTopNonFinishingActivity());
final Activity activity1 = mock(Activity.class);