diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 869bcc0f9b8f..dd29995cb156 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4762,6 +4762,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) { Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons); } + + // ClientTransactions is queued during #deferWindowLayout() for performance. + // Notify to continue. + mLifecycleManager.onLayoutContinued(); } /** diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java index 6b6388b10ce7..2e476772f85b 100644 --- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java +++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java @@ -105,7 +105,7 @@ class ClientLifecycleManager { final ClientTransaction clientTransaction = getOrCreatePendingTransaction(client); clientTransaction.addTransactionItem(transactionItem); - onClientTransactionItemScheduledLocked(clientTransaction); + onClientTransactionItemScheduled(clientTransaction); } else { // TODO(b/260873529): cleanup after launch. final ClientTransaction clientTransaction = ClientTransaction.obtain(client); @@ -134,7 +134,7 @@ class ClientLifecycleManager { clientTransaction.addTransactionItem(transactionItem); clientTransaction.addTransactionItem(lifecycleItem); - onClientTransactionItemScheduledLocked(clientTransaction); + onClientTransactionItemScheduled(clientTransaction); } else { // TODO(b/260873529): cleanup after launch. final ClientTransaction clientTransaction = ClientTransaction.obtain(client); @@ -146,6 +146,9 @@ class ClientLifecycleManager { /** Executes all the pending transactions. */ void dispatchPendingTransactions() { + if (!Flags.bundleClientTransactionFlag()) { + return; + } final int size = mPendingTransactions.size(); for (int i = 0; i < size; i++) { final ClientTransaction transaction = mPendingTransactions.valueAt(i); @@ -158,6 +161,20 @@ class ClientLifecycleManager { mPendingTransactions.clear(); } + /** + * Called to when {@link WindowSurfacePlacer#continueLayout}. + * Dispatches all pending transactions unless there is an ongoing/scheduled layout, in which + * case the pending transactions will be dispatched in + * {@link RootWindowContainer#performSurfacePlacementNoTrace}. + */ + void onLayoutContinued() { + if (shouldDispatchPendingTransactionsImmediately()) { + // Dispatch the pending transactions immediately if there is no ongoing/scheduled layout + dispatchPendingTransactions(); + } + } + + /** Must only be called with WM lock. */ @NonNull private ClientTransaction getOrCreatePendingTransaction(@NonNull IApplicationThread client) { final IBinder clientBinder = client.asBinder(); @@ -173,20 +190,28 @@ class ClientLifecycleManager { } /** Must only be called with WM lock. */ - private void onClientTransactionItemScheduledLocked( + private void onClientTransactionItemScheduled( @NonNull ClientTransaction clientTransaction) throws RemoteException { - // TODO(b/260873529): make sure WindowSurfacePlacer#requestTraversal is called before - // ClientTransaction scheduled when needed. - - if (mWms != null && (mWms.mWindowPlacerLocked.isInLayout() - || mWms.mWindowPlacerLocked.isTraversalScheduled())) { - // The pending transactions will be dispatched when - // RootWindowContainer#performSurfacePlacementNoTrace. - return; + if (shouldDispatchPendingTransactionsImmediately()) { + // Dispatch the pending transaction immediately. + mPendingTransactions.remove(clientTransaction.getClient().asBinder()); + scheduleTransaction(clientTransaction); } + } - // Dispatch the pending transaction immediately. - mPendingTransactions.remove(clientTransaction.getClient().asBinder()); - scheduleTransaction(clientTransaction); + /** Must only be called with WM lock. */ + private boolean shouldDispatchPendingTransactionsImmediately() { + if (mWms == null) { + return true; + } + // Do not dispatch when + // 1. Layout deferred. + // 2. Layout requested. + // 3. Layout in process. + // The pending transactions will be dispatched during layout in + // RootWindowContainer#performSurfacePlacementNoTrace. + return !mWms.mWindowPlacerLocked.isLayoutDeferred() + && !mWms.mWindowPlacerLocked.isTraversalScheduled() + && !mWms.mWindowPlacerLocked.isInLayout(); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index d2c731c3f8ad..ed99108e0da3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -1087,4 +1087,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertTrue(homeActivity.getTask().isFocused()); assertFalse(pinnedTask.isFocused()); } + + @Test + public void testContinueWindowLayout_notifyClientLifecycleManager() { + clearInvocations(mClientLifecycleManager); + mAtm.deferWindowLayout(); + + verify(mClientLifecycleManager, never()).onLayoutContinued(); + + mAtm.continueWindowLayout(); + + verify(mClientLifecycleManager).onLayoutContinued(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java index 7fdc5fc2cff6..c757457a55ec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ClientLifecycleManagerTests.java @@ -16,6 +16,8 @@ package com.android.server.wm; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -52,14 +54,12 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WmTests:ClientLifecycleManagerTests */ -// Suppress GuardedBy warning on unit tests -@SuppressWarnings("GuardedBy") @SmallTest @Presubmit public class ClientLifecycleManagerTests { @Rule(order = 0) - public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @Rule(order = 1) public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); @@ -225,4 +225,30 @@ public class ClientLifecycleManagerTests { verify(mTransaction).schedule(); verify(mTransaction).recycle(); } + + @Test + public void testLayoutDeferred() throws RemoteException { + mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG); + spyOn(mWms.mWindowPlacerLocked); + doReturn(false).when(mWms.mWindowPlacerLocked).isInLayout(); + doReturn(false).when(mWms.mWindowPlacerLocked).isTraversalScheduled(); + doReturn(true).when(mWms.mWindowPlacerLocked).isLayoutDeferred(); + + // Queue transactions during layout deferred. + mLifecycleManager.scheduleTransactionItem(mNonBinderClient, mLifecycleItem); + + verify(mLifecycleManager, never()).scheduleTransaction(any()); + + // Continue queueing when there are multi-level defer. + mLifecycleManager.onLayoutContinued(); + + verify(mLifecycleManager, never()).scheduleTransaction(any()); + + // Immediately dispatch when layout continue without ongoing/scheduled layout. + doReturn(false).when(mWms.mWindowPlacerLocked).isLayoutDeferred(); + + mLifecycleManager.onLayoutContinued(); + + verify(mLifecycleManager).scheduleTransaction(any()); + } }