Merge "Fix system crash when ActivityEmbedding process died with pending events" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-10-25 09:17:33 +00:00
committed by Android (Google) Code Review
2 changed files with 73 additions and 11 deletions

View File

@@ -184,14 +184,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
void dispose() { void dispose() {
while (!mOrganizedTaskFragments.isEmpty()) { for (int i = mOrganizedTaskFragments.size() - 1; i >= 0; i--) {
final TaskFragment taskFragment = mOrganizedTaskFragments.get(0); // Cleanup the TaskFragmentOrganizer from all TaskFragments it organized before
// Cleanup before remove to prevent it from sending any additional event, such as // removing the windows to prevent it from adding any additional TaskFragment
// #onTaskFragmentVanished, to the removed organizer. // pending event.
final TaskFragment taskFragment = mOrganizedTaskFragments.get(i);
taskFragment.onTaskFragmentOrganizerRemoved(); taskFragment.onTaskFragmentOrganizerRemoved();
taskFragment.removeImmediately();
mOrganizedTaskFragments.remove(taskFragment);
} }
// Defer to avoid unnecessary layout when there are multiple TaskFragments removal.
mAtmService.deferWindowLayout();
try {
while (!mOrganizedTaskFragments.isEmpty()) {
final TaskFragment taskFragment = mOrganizedTaskFragments.remove(0);
taskFragment.removeImmediately();
}
} finally {
mAtmService.continueWindowLayout();
}
for (int i = mDeferredTransitions.size() - 1; i >= 0; i--) { for (int i = mDeferredTransitions.size() - 1; i >= 0; i--) {
// Cleanup any running transaction to unblock the current transition. // Cleanup any running transaction to unblock the current transition.
onTransactionFinished(mDeferredTransitions.keyAt(i)); onTransactionFinished(mDeferredTransitions.keyAt(i));
@@ -426,7 +437,6 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
@Override @Override
public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) { public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
validateAndGetState(organizer);
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid(); final long uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity(); final long origId = Binder.clearCallingIdentity();
@@ -697,11 +707,17 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) { private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer); final TaskFragmentOrganizerState state = mTaskFragmentOrganizerState.get(
organizer.asBinder());
if (state == null) {
Slog.w(TAG, "The organizer has already been removed.");
return;
}
// Remove any pending event of this organizer first because state.dispose() may trigger
// event dispatch as result of surface placement.
mPendingTaskFragmentEvents.remove(organizer.asBinder());
// remove all of the children of the organized TaskFragment // remove all of the children of the organized TaskFragment
state.dispose(); state.dispose();
// Remove any pending event of this organizer.
mPendingTaskFragmentEvents.remove(organizer.asBinder());
mTaskFragmentOrganizerState.remove(organizer.asBinder()); mTaskFragmentOrganizerState.remove(organizer.asBinder());
} }

View File

@@ -56,6 +56,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -91,6 +92,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor; import org.mockito.Captor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.List; import java.util.List;
@@ -761,6 +763,50 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
} }
@Test
public void testOrganizerRemovedWithPendingEvents() {
final TaskFragment tf0 = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setOrganizer(mOrganizer)
.setFragmentToken(mFragmentToken)
.build();
final TaskFragment tf1 = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setOrganizer(mOrganizer)
.setFragmentToken(new Binder())
.build();
assertTrue(tf0.isOrganizedTaskFragment());
assertTrue(tf1.isOrganizedTaskFragment());
assertTrue(tf0.isAttached());
assertTrue(tf0.isAttached());
// Mock the behavior that remove TaskFragment can trigger event dispatch.
final Answer<Void> removeImmediately = invocation -> {
invocation.callRealMethod();
mController.dispatchPendingEvents();
return null;
};
doAnswer(removeImmediately).when(tf0).removeImmediately();
doAnswer(removeImmediately).when(tf1).removeImmediately();
// Add pending events.
mController.onTaskFragmentAppeared(mIOrganizer, tf0);
mController.onTaskFragmentAppeared(mIOrganizer, tf1);
// Remove organizer.
mController.unregisterOrganizer(mIOrganizer);
mController.dispatchPendingEvents();
// Nothing should happen after the organizer is removed.
verify(mOrganizer, never()).onTransactionReady(any());
// TaskFragments should be removed.
assertFalse(tf0.isOrganizedTaskFragment());
assertFalse(tf1.isOrganizedTaskFragment());
assertFalse(tf0.isAttached());
assertFalse(tf0.isAttached());
}
@Test @Test
public void testTaskFragmentInPip_startActivityInTaskFragment() { public void testTaskFragmentInPip_startActivityInTaskFragment() {
setupTaskFragmentInPip(); setupTaskFragmentInPip();