From 770597c76174184a05410e007ac992d905e27538 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Mon, 18 May 2020 19:58:23 +0800 Subject: [PATCH] Fix recursive calls on DC#ensureActivitiesVisible Previously CL[1] used display#ensureActivitiesVisible instead of stack#ensureActivitiesVisible, and it caused recursive calls on ensureActivitiesVisible in a very hidden way. A possible scenario is when an activity paused and AM tried to resume another activity which did not support multi-resume. [1]: Id8253eb97581a9dd52ab748117d9cd4cc63a206d fixes: 156189923 Test: atest DisplayContentTests#testEnsureActivitiesVisibleNotRecursive Change-Id: Iba2a052e4a1d91edfc6fa80954a730ad5ca3ff0d --- .../com/android/server/wm/DisplayContent.java | 21 ++++++++++++++++--- .../server/wm/DisplayContentTests.java | 19 ++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d93e9764146fd..eb85db61754f1 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -625,6 +625,12 @@ class DisplayContent extends WindowContainer mUpdateWindowsForAnimator = w -> { WindowStateAnimator winAnimator = w.mWinAnimator; final ActivityRecord activity = w.mActivityRecord; @@ -5442,9 +5448,18 @@ class DisplayContent extends WindowContainer= 0; --i) { - getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges, - preserveWindows, notifyClients); + if (mInEnsureActivitiesVisible) { + // Don't do recursive work. + return; + } + mInEnsureActivitiesVisible = true; + try { + for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { + getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges, + preserveWindows, notifyClients); + } + } finally { + mInEnsureActivitiesVisible = false; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index ac95a817bec91..4e82ceb882a83 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -72,6 +72,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import android.annotation.SuppressLint; import android.app.ActivityTaskManager; @@ -1142,7 +1143,7 @@ public class DisplayContentTests extends WindowTestsBase { Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt()); final boolean[] continued = new boolean[1]; // TODO(display-merge): Remove cast - Mockito.doAnswer( + doAnswer( invocation -> { continued[0] = true; return true; @@ -1248,6 +1249,22 @@ public class DisplayContentTests extends WindowTestsBase { assertEquals(window, result); } + @Test + public void testEnsureActivitiesVisibleNotRecursive() { + final TaskDisplayArea mockTda = mock(TaskDisplayArea.class); + doReturn(mockTda).when(mDisplayContent).getTaskDisplayAreaAt(anyInt()); + final boolean[] called = { false }; + doAnswer(invocation -> { + // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice. + assertFalse(called[0]); + called[0] = true; + mDisplayContent.ensureActivitiesVisible(null, 0, false, false); + return null; + }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean()); + + mDisplayContent.ensureActivitiesVisible(null, 0, false, false); + } + private boolean isOptionsPanelAtRight(int displayId) { return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; }