Merge "Fix system crash when ActivityEmbedding process died with pending events" into tm-qpr-dev
This commit is contained in:
@@ -184,19 +184,30 @@ 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));
|
||||||
}
|
}
|
||||||
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
|
mOrganizer.asBinder().unlinkToDeath(this, 0 /* flags */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user