Loading data/etc/services.core.protolog.json +12 −0 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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", Loading services/core/java/com/android/server/wm/ActivityRecord.java +68 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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, Loading Loading @@ -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); Loading services/core/java/com/android/server/wm/WindowState.java +5 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading services/core/java/com/android/server/wm/WindowToken.java +4 −1 Original line number Diff line number Diff line Loading @@ -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()) { Loading services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +62 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
data/etc/services.core.protolog.json +12 −0 Original line number Diff line number Diff line Loading @@ -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", Loading Loading @@ -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", Loading
services/core/java/com/android/server/wm/ActivityRecord.java +68 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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() { Loading Loading @@ -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, Loading Loading @@ -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); Loading
services/core/java/com/android/server/wm/WindowState.java +5 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading
services/core/java/com/android/server/wm/WindowToken.java +4 −1 Original line number Diff line number Diff line Loading @@ -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()) { Loading
services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +62 −0 Original line number Diff line number Diff line Loading @@ -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 Loading