diff options
5 files changed, 151 insertions, 1 deletions
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index c77004d4eb17..da91a964565f 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1867,6 +1867,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java" }, + "-483957611": { + "message": "Resuming configuration dispatch for %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-481924678": { "message": "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w.isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d", "level": "DEBUG", @@ -4021,6 +4027,12 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, + "1473051122": { + "message": "Pausing configuration dispatch for %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1494644409": { "message": " Rejecting as detached: %s", "level": "VERBOSE", diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 036f7b6841c2..3d492bbd4d37 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -141,6 +141,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_ASPECT_RATIO; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_FIXED_ORIENTATION; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; @@ -991,6 +992,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private CustomAppTransition mCustomOpenTransition; private CustomAppTransition mCustomCloseTransition; + /** Non-zero to pause dispatching configuration changes to the client. */ + int mPauseConfigurationDispatchCount = 0; + private final Runnable mPauseTimeoutRunnable = new Runnable() { @Override public void run() { @@ -9276,6 +9280,59 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + @Override + void dispatchConfigurationToChild(WindowState child, Configuration config) { + if (isConfigurationDispatchPaused()) { + return; + } + super.dispatchConfigurationToChild(child, config); + } + + /** + * Pauses dispatch of configuration changes to the client. This includes any + * configuration-triggered lifecycle changes, WindowState configs, and surface changes. If + * a lifecycle change comes from another source (eg. stop), it will still run but will use the + * paused configuration. + * + * The main way this works is by blocking calls to {@link #updateReportedConfigurationAndSend}. + * That method is responsible for evaluating whether the activity needs to be relaunched and + * sending configurations. + */ + void pauseConfigurationDispatch() { + ++mPauseConfigurationDispatchCount; + if (mPauseConfigurationDispatchCount == 1) { + ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Pausing configuration dispatch for " + + " %s", this); + } + } + + /** @return `true` if configuration actually changed. */ + boolean resumeConfigurationDispatch() { + --mPauseConfigurationDispatchCount; + if (mPauseConfigurationDispatchCount > 0) { + return false; + } + ProtoLog.v(WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Resuming configuration dispatch for %s", this); + if (mPauseConfigurationDispatchCount < 0) { + Slog.wtf(TAG, "Trying to resume non-paused configuration dispatch"); + mPauseConfigurationDispatchCount = 0; + return false; + } + if (mLastReportedDisplayId == getDisplayId() + && getConfiguration().equals(mLastReportedConfiguration.getMergedConfiguration())) { + return false; + } + for (int i = getChildCount() - 1; i >= 0; --i) { + dispatchConfigurationToChild(getChildAt(i), getConfiguration()); + } + updateReportedConfigurationAndSend(); + return true; + } + + boolean isConfigurationDispatchPaused() { + return mPauseConfigurationDispatchCount > 0; + } + private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { return applyAspectRatio(outBounds, containingAppBounds, containingBounds, @@ -9525,6 +9582,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } + if (isConfigurationDispatchPaused()) { + return true; + } + + return updateReportedConfigurationAndSend(); + } + + boolean updateReportedConfigurationAndSend() { + if (isConfigurationDispatchPaused()) { + Slog.wtf(TAG, "trying to update reported(client) config while dispatch is paused"); + } ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct " + "configuration: %s", this); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 56f2bc3d3e3b..7ad87ed8f094 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5187,6 +5187,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mSurfaceControl == null) { return; } + if (mActivityRecord != null && mActivityRecord.isConfigurationDispatchPaused()) { + // Don't update surface-position while dispatch paused. This is calculated from + // the server-side activity configuration so return early. + return; + } if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout()) && !mSurfacePlacementNeeded) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 5048cef3da1b..13e1ba785b87 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -639,9 +639,12 @@ class WindowToken extends WindowContainer<WindowState> { @Override void updateSurfacePosition(SurfaceControl.Transaction t) { + final ActivityRecord r = asActivityRecord(); + if (r != null && r.isConfigurationDispatchPaused()) { + return; + } super.updateSurfacePosition(t); if (!mTransitionController.isShellTransitionsEnabled() && isFixedRotationTransforming()) { - final ActivityRecord r = asActivityRecord(); final Task rootTask = r != null ? r.getRootTask() : null; // Don't transform the activity in PiP because the PiP task organizer will handle it. if (rootTask == null || !rootTask.inPinnedWindowingMode()) { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 2a89b02482b3..31d6fa3e91f8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3722,6 +3722,68 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(ar.moveFocusableActivityToTop("test")); } + @Test + public void testPauseConfigDispatch() throws RemoteException { + final Task task = new TaskBuilder(mSupervisor) + .setDisplay(mDisplayContent).setCreateActivity(true).build(); + final ActivityRecord activity = task.getTopNonFinishingActivity(); + final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams( + TYPE_BASE_APPLICATION); + attrs.setTitle("AppWindow"); + final TestWindowState appWindow = createWindowState(attrs, activity); + activity.addWindow(appWindow); + + clearInvocations(mClientLifecycleManager); + clearInvocations(activity); + + Configuration ro = activity.getRequestedOverrideConfiguration(); + ro.windowConfiguration.setBounds(new Rect(20, 0, 120, 200)); + activity.onRequestedOverrideConfigurationChanged(ro); + activity.ensureActivityConfiguration(); + mWm.mRoot.performSurfacePlacement(); + + // policy will center the bounds, so just check for matching size here. + assertEquals(100, activity.getWindowConfiguration().getBounds().width()); + assertEquals(100, appWindow.getWindowConfiguration().getBounds().width()); + // No scheduled transactions since it asked for a restart. + verify(mClientLifecycleManager, times(1)).scheduleTransaction(any()); + verify(activity, times(1)).setLastReportedConfiguration(any(), any()); + assertTrue(appWindow.mResizeReported); + + // act like everything drew and went idle + appWindow.mResizeReported = false; + makeLastConfigReportedToClient(appWindow, true); + + // Now pause dispatch and try to resize + activity.pauseConfigurationDispatch(); + + ro.windowConfiguration.setBounds(new Rect(20, 0, 150, 200)); + activity.onRequestedOverrideConfigurationChanged(ro); + activity.ensureActivityConfiguration(); + mWm.mRoot.performSurfacePlacement(); + + // Activity should get new config (core-side) + assertEquals(130, activity.getWindowConfiguration().getBounds().width()); + // But windows should not get new config. + assertEquals(100, appWindow.getWindowConfiguration().getBounds().width()); + // The client shouldn't receive any changes + verify(mClientLifecycleManager, times(1)).scheduleTransaction(any()); + // and lastReported shouldn't be set. + verify(activity, times(1)).setLastReportedConfiguration(any(), any()); + // There should be no resize reported to client. + assertFalse(appWindow.mResizeReported); + + // Now resume dispatch + activity.resumeConfigurationDispatch(); + mWm.mRoot.performSurfacePlacement(); + + // Windows and client should now receive updates + verify(activity, times(2)).setLastReportedConfiguration(any(), any()); + verify(mClientLifecycleManager, times(2)).scheduleTransaction(any()); + assertEquals(130, appWindow.getWindowConfiguration().getBounds().width()); + assertTrue(appWindow.mResizeReported); + } + private ICompatCameraControlCallback getCompatCameraControlCallback() { return new ICompatCameraControlCallback.Stub() { @Override |