Merge "Use blast sync for 180 degrees rotation" into udc-dev
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 5c9c813..7a11120 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -93,6 +93,9 @@
     /** Whether the start transaction of the transition is committed (by shell). */
     private boolean mIsStartTransactionCommitted;
 
+    /** Whether all windows should wait for the start transaction. */
+    private boolean mAlwaysWaitForStartTransaction;
+
     /** Whether the target windows have been requested to sync their draw transactions. */
     private boolean mIsSyncDrawRequested;
 
@@ -144,6 +147,15 @@
         if (mTransitionOp == OP_LEGACY) {
             mIsStartTransactionCommitted = true;
         } else if (displayContent.mTransitionController.isCollecting(displayContent)) {
+            final Transition transition =
+                    mDisplayContent.mTransitionController.getCollectingTransition();
+            if (transition != null) {
+                final BLASTSyncEngine.SyncGroup syncGroup =
+                        mDisplayContent.mWmService.mSyncEngine.getSyncSet(transition.getSyncId());
+                if (syncGroup != null && syncGroup.mSyncMethod == BLASTSyncEngine.METHOD_BLAST) {
+                    mAlwaysWaitForStartTransaction = true;
+                }
+            }
             keepAppearanceInPreviousRotation();
         }
     }
@@ -202,7 +214,7 @@
         // target windows. But the windows still need to use sync transaction to keep the appearance
         // in previous rotation, so request a no-op sync to keep the state.
         for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
-            if (mTargetWindowTokens.valueAt(i).canDrawBeforeStartTransaction()) {
+            if (canDrawBeforeStartTransaction(mTargetWindowTokens.valueAt(i))) {
                 // Expect a screenshot layer will cover the non seamless windows.
                 continue;
             }
@@ -521,7 +533,7 @@
         if (op == null) return false;
         if (DEBUG) Slog.d(TAG, "handleFinishDrawing " + w);
         if (postDrawTransaction == null || !mIsSyncDrawRequested
-                || op.canDrawBeforeStartTransaction()) {
+                || canDrawBeforeStartTransaction(op)) {
             mDisplayContent.finishAsyncRotation(w.mToken);
             return false;
         }
@@ -563,6 +575,14 @@
         return super.getFadeOutAnimation();
     }
 
+    /**
+     * Returns {@code true} if the corresponding window can draw its latest content before the
+     * start transaction of rotation transition is applied.
+     */
+    private boolean canDrawBeforeStartTransaction(Operation op) {
+        return !mAlwaysWaitForStartTransaction && op.mAction != Operation.ACTION_SEAMLESS;
+    }
+
     /** The operation to control the rotation appearance associated with window token. */
     private static class Operation {
         @Retention(RetentionPolicy.SOURCE)
@@ -588,14 +608,5 @@
         Operation(@Action int action) {
             mAction = action;
         }
-
-        /**
-         * Returns {@code true} if the corresponding window can draw its latest content before the
-         * start transaction of rotation transition is applied.
-         */
-        boolean canDrawBeforeStartTransaction() {
-            return TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST
-                    && mAction != ACTION_SEAMLESS;
-        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index cd26e2e..48cf567 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -95,7 +95,7 @@
      */
     class SyncGroup {
         final int mSyncId;
-        final int mSyncMethod;
+        int mSyncMethod = METHOD_BLAST;
         final TransactionReadyListener mListener;
         final Runnable mOnTimeout;
         boolean mReady = false;
@@ -103,9 +103,8 @@
         private SurfaceControl.Transaction mOrphanTransaction = null;
         private String mTraceName;
 
-        private SyncGroup(TransactionReadyListener listener, int id, String name, int method) {
+        private SyncGroup(TransactionReadyListener listener, int id, String name) {
             mSyncId = id;
-            mSyncMethod = method;
             mListener = listener;
             mOnTimeout = () -> {
                 Slog.w(TAG, "Sync group " + mSyncId + " timeout");
@@ -288,13 +287,12 @@
      * Prepares a {@link SyncGroup} that is not active yet. Caller must call {@link #startSyncSet}
      * before calling {@link #addToSyncSet(int, WindowContainer)} on any {@link WindowContainer}.
      */
-    SyncGroup prepareSyncSet(TransactionReadyListener listener, String name, int method) {
-        return new SyncGroup(listener, mNextSyncId++, name, method);
+    SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) {
+        return new SyncGroup(listener, mNextSyncId++, name);
     }
 
-    int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name,
-            int method) {
-        final SyncGroup s = prepareSyncSet(listener, name, method);
+    int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name) {
+        final SyncGroup s = prepareSyncSet(listener, name);
         startSyncSet(s, timeoutMs);
         return s.mSyncId;
     }
@@ -334,6 +332,15 @@
         getSyncGroup(id).addToSync(wc);
     }
 
+    void setSyncMethod(int id, int method) {
+        final SyncGroup syncGroup = getSyncGroup(id);
+        if (!syncGroup.mRootMembers.isEmpty()) {
+            throw new IllegalStateException(
+                    "Not allow to change sync method after adding group member, id=" + id);
+        }
+        syncGroup.mSyncMethod = method;
+    }
+
     void setReady(int id, boolean ready) {
         getSyncGroup(id).setReady(ready);
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index f2e4937..606f011 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -369,7 +369,6 @@
         return mState;
     }
 
-    @VisibleForTesting
     int getSyncId() {
         return mSyncId;
     }
@@ -409,18 +408,14 @@
         return mState == STATE_FINISHED;
     }
 
-    @VisibleForTesting
-    void startCollecting(long timeoutMs) {
-        startCollecting(timeoutMs, TransitionController.SYNC_METHOD);
-    }
-
     /** Starts collecting phase. Once this starts, all relevant surface operations are sync. */
-    void startCollecting(long timeoutMs, int method) {
+    void startCollecting(long timeoutMs) {
         if (mState != STATE_PENDING) {
             throw new IllegalStateException("Attempting to re-use a transition");
         }
         mState = STATE_COLLECTING;
-        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG, method);
+        mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG);
+        mSyncEngine.setSyncMethod(mSyncId, TransitionController.SYNC_METHOD);
 
         mLogger.mSyncId = mSyncId;
         mLogger.mCollectTimeNs = SystemClock.elapsedRealtimeNanos();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 6c951bf..bacc6e6 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -51,7 +51,6 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.FgThread;
@@ -200,12 +199,6 @@
 
     /** Starts Collecting */
     void moveToCollecting(@NonNull Transition transition) {
-        moveToCollecting(transition, SYNC_METHOD);
-    }
-
-    /** Starts Collecting */
-    @VisibleForTesting
-    void moveToCollecting(@NonNull Transition transition, int method) {
         if (mCollectingTransition != null) {
             throw new IllegalStateException("Simultaneous transition collection not supported.");
         }
@@ -219,7 +212,7 @@
         // Distinguish change type because the response time is usually expected to be not too long.
         final long timeoutMs =
                 transition.mType == TRANSIT_CHANGE ? CHANGE_TIMEOUT_MS : DEFAULT_TIMEOUT_MS;
-        mCollectingTransition.startCollecting(timeoutMs, method);
+        mCollectingTransition.startCollecting(timeoutMs);
         ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Start collecting in Transition: %s",
                 mCollectingTransition);
         dispatchLegacyAppTransitionPending();
@@ -492,6 +485,15 @@
         } else {
             newTransition = requestStartTransition(createTransition(type, flags),
                     trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
+            if (newTransition != null && displayChange != null && (displayChange.getStartRotation()
+                    + displayChange.getEndRotation()) % 2 == 0) {
+                // 180 degrees rotation change may not change screen size. So the clients may draw
+                // some frames before and after the display projection transaction is applied by the
+                // remote player. That may cause some buffers to show in different rotation. So use
+                // sync method to pause clients drawing until the projection transaction is applied.
+                mAtm.mWindowManager.mSyncEngine.setSyncMethod(newTransition.getSyncId(),
+                        BLASTSyncEngine.METHOD_BLAST);
+            }
         }
         if (trigger != null) {
             if (isExistenceType(type)) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 495d7ce4..c3c87af 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1648,7 +1648,7 @@
     private BLASTSyncEngine.SyncGroup prepareSyncWithOrganizer(
             IWindowContainerTransactionCallback callback) {
         final BLASTSyncEngine.SyncGroup s = mService.mWindowManager.mSyncEngine
-                .prepareSyncSet(this, "", BLASTSyncEngine.METHOD_BLAST);
+                .prepareSyncSet(this, "Organizer");
         mTransactionCallbacksByPendingSyncId.put(s.mSyncId, callback);
         return s;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
index aff9c1a..8e91ca2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SyncEngineTests.java
@@ -22,7 +22,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.BLASTSyncEngine.METHOD_BLAST;
 import static com.android.server.wm.BLASTSyncEngine.METHOD_NONE;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -363,7 +362,8 @@
         BLASTSyncEngine.TransactionReadyListener listener = mock(
                 BLASTSyncEngine.TransactionReadyListener.class);
 
-        int id = startSyncSet(bse, listener, METHOD_NONE);
+        final int id = startSyncSet(bse, listener);
+        bse.setSyncMethod(id, METHOD_NONE);
         bse.addToSyncSet(id, mAppWindow.mToken);
         mAppWindow.prepareSync();
         assertFalse(mAppWindow.shouldSyncWithBuffers());
@@ -373,12 +373,7 @@
 
     static int startSyncSet(BLASTSyncEngine engine,
             BLASTSyncEngine.TransactionReadyListener listener) {
-        return startSyncSet(engine, listener, METHOD_BLAST);
-    }
-
-    static int startSyncSet(BLASTSyncEngine engine,
-            BLASTSyncEngine.TransactionReadyListener listener, int method) {
-        return engine.startSyncSet(listener, BLAST_TIMEOUT_DURATION, "", method);
+        return engine.startSyncSet(listener, BLAST_TIMEOUT_DURATION, "Test");
     }
 
     static class TestWindowContainer extends WindowContainer {
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 048e2cc..debfc84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1704,7 +1704,8 @@
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         final Transition transition = new Transition(TRANSIT_OPEN, 0 /* flags */,
                 app.mTransitionController, mWm.mSyncEngine);
-        app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
+        app.mTransitionController.moveToCollecting(transition);
+        mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
         final ArrayList<WindowContainer> freezeCalls = new ArrayList<>();
         transition.setContainerFreezer(new Transition.IContainerFreezer() {
             @Override
@@ -1755,7 +1756,8 @@
                 mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
                 app.mTransitionController, mWm.mSyncEngine);
-        app.mTransitionController.moveToCollecting(transition, BLASTSyncEngine.METHOD_NONE);
+        app.mTransitionController.moveToCollecting(transition);
+        mWm.mSyncEngine.setSyncMethod(transition.getSyncId(), BLASTSyncEngine.METHOD_NONE);
         final SurfaceControl mockSnapshot = mock(SurfaceControl.class);
         transition.setContainerFreezer(new Transition.IContainerFreezer() {
             @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index d7e4c55..a68a573 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -42,7 +42,6 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
-import static com.android.server.wm.BLASTSyncEngine.METHOD_BLAST;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY;
 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION;
@@ -1001,7 +1000,7 @@
         BLASTSyncEngine.TransactionReadyListener transactionListener =
                 mock(BLASTSyncEngine.TransactionReadyListener.class);
 
-        int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "", METHOD_BLAST);
+        final int id = bse.startSyncSet(transactionListener, BLAST_TIMEOUT_DURATION, "Test");
         bse.addToSyncSet(id, task);
         bse.setReady(id);
         bse.onSurfacePlacement();