Reduce invocation of surface placement of switching activities
This skips to perform surface placement immediately when starting
activity and reporting activity paused. Because the window of
next resuming activity should also do it by relayout.
These skip cases are relatively safe because before the transition
is ready, the visual appearance should keep the same even if without
running surface placement.
One of obvious benefit is to reduce lock contention from the systemui
when handling starting window (add window/relayout/finish drawn).
Bug: 188398125
Bug: 183665220
Bug: 187104264
Test: CtsWindowManagerDeviceTestCases
Change-Id: Ice5a47a64178cbd00ce27625a60eded2b699e9cf
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 065dc6e..c39cf91 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -183,6 +183,7 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED;
import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -5361,6 +5362,11 @@
mAtmService.deferWindowLayout();
try {
task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
+ // If there is no possible transition to execute, then allow to skip layout
+ // because it may be done by next resumed activity.
+ if (!pausingActivity.mDisplayContent.areOpeningAppsReady()) {
+ mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_ALLOWED);
+ }
} finally {
mAtmService.continueWindowLayout();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 66e55e0..8262c59 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1549,6 +1549,8 @@
mService.getTransitionController().collect(r);
try {
mService.deferWindowLayout();
+ // Allow to skip layout because it may be done by the window of the starting activity.
+ mService.addWindowLayoutReasons(ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED);
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 37a9b80..28c6349 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -657,14 +657,27 @@
@IntDef({
LAYOUT_REASON_CONFIG_CHANGED,
LAYOUT_REASON_VISIBILITY_CHANGED,
+ SKIP_LAYOUT_REASON_ALLOWED,
+ SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT,
})
@interface LayoutReason {
}
+ static final int LAYOUT_REASON_MASK = 0x0000ffff;
static final int LAYOUT_REASON_CONFIG_CHANGED = 0x1;
static final int LAYOUT_REASON_VISIBILITY_CHANGED = 0x2;
+ static final int SKIP_LAYOUT_REASON_MASK = 0xfffe0000;
- /** The reasons to perform surface placement. */
+ /**
+ * Allow to call {@link WindowSurfacePlacer#endDeferAndSkipLayout} if there is a reason
+ * included in {@link #SKIP_LAYOUT_REASON_MASK} was added.
+ */
+ static final int SKIP_LAYOUT_REASON_ALLOWED = 1 << 16;
+
+ /** Used when the client is scheduled to call relayout. */
+ static final int SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT = 1 << 17;
+
+ /** The reasons to perform or skip surface placement. */
@LayoutReason
private int mLayoutReasons;
@@ -4187,18 +4200,35 @@
mWindowManager.mWindowPlacerLocked.deferLayout();
}
- /** @see WindowSurfacePlacer#continueLayout */
+ /**
+ * @see WindowSurfacePlacer#continueLayout
+ * @see WindowSurfacePlacer#endDeferAndSkipLayout
+ */
void continueWindowLayout() {
- mWindowManager.mWindowPlacerLocked.continueLayout(mLayoutReasons != 0);
- if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
- Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons);
+ if ((mLayoutReasons & SKIP_LAYOUT_REASON_ALLOWED) != 0) {
+ final int skipReasons = mLayoutReasons & SKIP_LAYOUT_REASON_MASK;
+ if (skipReasons != 0) {
+ if (DEBUG_ALL) {
+ Slog.i(TAG, "continueWindowLayout skip-reasons="
+ + Integer.toHexString(skipReasons));
+ }
+ mWindowManager.mWindowPlacerLocked.endDeferAndSkipLayout();
+ return;
+ }
+ }
+
+ final int reasons = mLayoutReasons & LAYOUT_REASON_MASK;
+ mWindowManager.mWindowPlacerLocked.continueLayout(reasons != 0);
+ if (DEBUG_ALL && reasons != 0 && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ Slog.i(TAG, "continueWindowLayout reasons=" + Integer.toHexString(reasons));
}
}
/**
- * If a reason is added between {@link #deferWindowLayout} and {@link #continueWindowLayout},
- * it will make sure {@link WindowSurfacePlacer#performSurfacePlacement} is called when the last
- * defer count is gone.
+ * If a reason within {@link #LAYOUT_REASON_MASK} is added between {@link #deferWindowLayout}
+ * and {@link #continueWindowLayout}, {@link WindowSurfacePlacer#performSurfacePlacement} will
+ * be called when the last defer count is gone. Note that the {@link #SKIP_LAYOUT_REASON_MASK}
+ * has higher priority to determine whether to perform layout.
*/
void addWindowLayoutReasons(@LayoutReason int reasons) {
mLayoutReasons |= reasons;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 7fe0f5b..b140451 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -938,6 +938,9 @@
if (r.app != null) {
r.app.updateServiceConnectionActivities();
}
+ // Expect a window of the starting activity will perform relayout.
+ mService.addWindowLayoutReasons(
+ ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d6bf461..343a4b5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3995,6 +3995,22 @@
return mLayoutNeeded;
}
+ /** Returns {@code true} if all opening apps may be ready to execute transition. */
+ boolean areOpeningAppsReady() {
+ final int size = mOpeningApps.size();
+ for (int i = size - 1; i >= 0; i--) {
+ final ActivityRecord r = mOpeningApps.valueAt(i);
+ if (!r.hasVisible) {
+ return false;
+ }
+ final WindowState w = r.findMainWindow();
+ if (w != null && !w.isDrawn()) {
+ return false;
+ }
+ }
+ return size > 0;
+ }
+
void dumpTokens(PrintWriter pw, boolean dumpAll) {
if (mTokenMap.isEmpty()) {
return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 93fc4f2..8bb635e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -106,6 +106,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG;
+import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT;
import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -6288,6 +6289,8 @@
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
}
+ // There may be a relayout from resuming next activity after the previous is paused.
+ mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
return true;
} else if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
@@ -6497,6 +6500,7 @@
ResumeActivityItem.obtain(next.app.getReportedProcState(),
dc.isNextTransitionForward()));
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
+ mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 2ee5fb0..2d2b1f4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -101,6 +101,16 @@
}
}
+ /**
+ * Resumes layout passes but skip to perform layout even if there was a request. This can only
+ * be called when there will be another layout request from client side, e.g. an activity is
+ * starting or resuming. And there should be no other significant changes need to apply.
+ */
+ void endDeferAndSkipLayout() {
+ mDeferDepth--;
+ mDeferredRequests = 0;
+ }
+
boolean isLayoutDeferred() {
return mDeferDepth > 0;
}