diff options
3 files changed, 50 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 359b353ba336..d9f12cfa3ff1 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -58,6 +58,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.server.FgThread; import java.util.ArrayList; +import java.util.function.Consumer; import java.util.function.LongConsumer; /** @@ -1314,18 +1315,18 @@ class TransitionController { return transit; } - /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ - boolean startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Runnable applySync) { + /** Starts the sync set if there is no pending or active syncs, otherwise enqueue the sync. */ + void startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Consumer<Boolean> applySync) { if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) { // Just add to queue since we already have a queue. - mQueuedTransitions.add(new QueuedTransition(syncGroup, (d) -> applySync.run())); + mQueuedTransitions.add(new QueuedTransition(syncGroup, + (deferred) -> applySync.accept(true /* deferred */))); ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Queueing legacy sync-set: %s", syncGroup.mSyncId); - return false; + return; } mSyncEngine.startSyncSet(syncGroup); - applySync.run(); - return true; + applySync.accept(false /* deferred */); } interface OnStartCollect { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 8e85e5bca240..a2f7ba499fdf 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -232,8 +232,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback); final int syncId = syncGroup.mSyncId; if (mTransitionController.isShellTransitionsEnabled()) { - mTransitionController.startLegacySyncOrQueue(syncGroup, () -> { - applyTransaction(t, syncId, null /*transition*/, caller); + mTransitionController.startLegacySyncOrQueue(syncGroup, (deferred) -> { + applyTransaction(t, syncId, null /* transition */, caller, deferred); setSyncReady(syncId); }); } else { @@ -304,7 +304,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub (deferred) -> { nextTransition.start(); nextTransition.mLogger.mStartWCT = wct; - applyTransaction(wct, -1 /*syncId*/, nextTransition, caller); + applyTransaction(wct, -1 /* syncId */, nextTransition, caller, + deferred); if (needsSetReady) { nextTransition.setAllReady(); } @@ -456,7 +457,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub transition.abort(); return; } - if (applyTransaction(wct, -1 /* syncId */, transition, caller) + if (applyTransaction(wct, -1 /* syncId */, transition, caller, deferred) == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) { transition.abort(); return; @@ -476,6 +477,23 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return applyTransaction(t, syncId, transition, caller, null /* finishTransition */); } + private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, + @Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) { + if (deferred) { + try { + return applyTransaction(t, syncId, transition, caller); + } catch (RuntimeException e) { + // If the transaction is deferred, the caller could be from TransitionController + // #tryStartCollectFromQueue that executes on system's worker thread rather than + // binder thread. And the operation in the WCT may be outdated that violates the + // current state. So catch the exception to avoid crashing the system. + Slog.e(TAG, "Failed to execute deferred applyTransaction", e); + } + return TRANSACT_EFFECTS_NONE; + } + return applyTransaction(t, syncId, transition, caller); + } + /** * @param syncId If non-null, this will be a sync-transaction. * @param transition A transition to collect changes into. @@ -838,13 +856,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub switch (type) { case HIERARCHY_OP_TYPE_REMOVE_TASK: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || wc.asTask() == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to remove invalid task: " + wc); + break; + } + final Task task = wc.asTask(); task.remove(true, "Applying remove task Hierarchy Op"); break; } case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to set launch root to a detached container: " + wc); + break; + } + final Task task = wc.asTask(); if (task == null) { throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); } else if (task.getTaskDisplayArea() == null) { @@ -858,7 +884,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: { final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer()); - final Task task = wc != null ? wc.asTask() : null; + if (wc == null || !wc.isAttached()) { + Slog.e(TAG, "Attempt to set launch adjacent to a detached container: " + wc); + break; + } + final Task task = wc.asTask(); final boolean clearRoot = hop.getToTop(); if (task == null) { throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc); diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 11267268a6fa..8c2deba5d8c1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -2191,8 +2191,11 @@ public class TransitionTests extends WindowTestsBase { BLASTSyncEngine.SyncGroup legacySync = mSyncEngine.prepareSyncSet( mock(BLASTSyncEngine.TransactionReadyListener.class), "test"); - final boolean[] applyLegacy = new boolean[]{false}; - controller.startLegacySyncOrQueue(legacySync, () -> applyLegacy[0] = true); + final boolean[] applyLegacy = new boolean[2]; + controller.startLegacySyncOrQueue(legacySync, (deferred) -> { + applyLegacy[0] = true; + applyLegacy[1] = deferred; + }); assertFalse(applyLegacy[0]); waitUntilHandlersIdle(); @@ -2208,6 +2211,7 @@ public class TransitionTests extends WindowTestsBase { assertTrue(transitA.isPlaying()); // legacy sync should start now assertTrue(applyLegacy[0]); + assertTrue(applyLegacy[1]); // transitB must wait assertTrue(transitB.isPending()); |