From 5495bffd235b09af345167c27b9bb1d6756f6a72 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Wed, 12 May 2021 21:16:13 +0800 Subject: [PATCH] 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 --- .../com/android/server/wm/ActivityRecord.java | 6 +++ .../android/server/wm/ActivityStarter.java | 2 + .../server/wm/ActivityTaskManagerService.java | 46 +++++++++++++++---- .../server/wm/ActivityTaskSupervisor.java | 3 ++ .../com/android/server/wm/DisplayContent.java | 16 +++++++ .../core/java/com/android/server/wm/Task.java | 4 ++ .../server/wm/WindowSurfacePlacer.java | 10 ++++ 7 files changed, 79 insertions(+), 8 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 065dc6e4f8edb..c39cf91d61601 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.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 66e55e04d198e..8262c59945c4a 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 @@ class ActivityStarter { 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 37a9b8012cb75..28c6349a0e96f 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 7fe0f5be287c6..b1404514f1f55 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 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { 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 d6bf46119cb13..343a4b541f2b1 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp 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 93fc4f2a31494..8bb635e032a7d 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.POSTFIX_VISIB 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 @@ class Task extends WindowContainer { 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 @@ class Task extends WindowContainer { 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 2ee5fb01efb3a..2d2b1f4ad3220 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 @@ class WindowSurfacePlacer { } } + /** + * 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; }