Merge "Set transform hint before rotation transaction is applied" into udc-dev
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8bfa426..ef38e89 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2190,6 +2190,14 @@
         }
 
         mWmService.mDisplayManagerInternal.performTraversal(transaction);
+        if (shellTransitions) {
+            // Before setDisplayProjection is applied by the start transaction of transition,
+            // set the transform hint to avoid using surface in old rotation.
+            getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation);
+            // The sync transaction should already contains setDisplayProjection, so unset the
+            // hint to restore the natural state when the transaction is applied.
+            transaction.unsetFixedTransformHint(mSurfaceControl);
+        }
         scheduleAnimation();
 
         mWmService.mRotationWatcherController.dispatchDisplayRotationChange(mDisplayId, rotation);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 54dfdd9..abc9f8a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -865,6 +865,13 @@
             if (c.getSnapshot() != null) {
                 t.reparent(c.getSnapshot(), null);
             }
+            // The fixed transform hint was set in DisplayContent#applyRotation(). Make sure to
+            // clear the hint in case the start transaction is not applied.
+            if (c.hasFlags(FLAG_IS_DISPLAY) && c.getStartRotation() != c.getEndRotation()
+                    && c.getContainer() != null) {
+                t.unsetFixedTransformHint(WindowContainer.fromBinder(c.getContainer().asBinder())
+                        .asDisplayContent().mSurfaceControl);
+            }
         }
         for (int i = info.getRootCount() - 1; i >= 0; --i) {
             final SurfaceControl leash = info.getRoot(i).getLeash();
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 8af037b..b0feefe 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -823,6 +823,7 @@
             // Can reset track-count now that everything is idle.
             mTrackCount = 0;
             validateStates();
+            mAtm.mWindowManager.onAnimationFinished();
         }
     }
 
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index fe7cd4a..37e5da5 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -79,6 +79,7 @@
                   android:turnScreenOn="true"
                   android:showWhenLocked="true" />
         <activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
+                  android:configChanges="screenLayout|screenSize|smallestScreenSize|orientation"
                   android:turnScreenOn="true"
                   android:showWhenLocked="true" />
         <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity"
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
index a27a5fd..342ab83 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
@@ -16,19 +16,34 @@
 
 package com.android.server.wm;
 
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
+import android.util.Log;
 import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * Class for testing {@link SurfaceControl}.
  *
@@ -106,6 +121,50 @@
         }
     }
 
+    @Test
+    public void testSurfaceChangedOnRotation() {
+        final Instrumentation instrumentation = getInstrumentation();
+        final Context context = instrumentation.getContext();
+        final Activity activity = instrumentation.startActivitySync(new Intent().setComponent(
+                new ComponentName(context, ActivityOptionsTest.MainActivity.class))
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        final SurfaceView sv = new SurfaceView(activity);
+        final AtomicInteger surfaceChangedCount = new AtomicInteger();
+        instrumentation.runOnMainSync(() -> activity.setContentView(sv));
+        sv.getHolder().addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(@NonNull SurfaceHolder holder) {
+            }
+            @Override
+            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+                    int height) {
+                surfaceChangedCount.getAndIncrement();
+                Log.i("surfaceChanged", "width=" + width + " height=" + height
+                        + " getTransformHint="
+                        + sv.getViewRootImpl().getSurfaceControl().getTransformHint());
+            }
+            @Override
+            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+            }
+        });
+        final int rotation = activity.getResources().getConfiguration()
+                .windowConfiguration.getRotation();
+        activity.setRequestedOrientation(activity.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_PORTRAIT
+                ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+                : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        instrumentation.getUiAutomation().syncInputTransactions();
+        instrumentation.waitForIdleSync();
+        final int newRotation = activity.getResources().getConfiguration()
+                .windowConfiguration.getRotation();
+        final int count = surfaceChangedCount.get();
+        activity.finishAndRemoveTask();
+        // The first count is triggered from creation, so the target number is 2.
+        if (rotation != newRotation && count > 2) {
+            fail("More than once surfaceChanged for rotation change: " + count);
+        }
+    }
+
     private SurfaceControl buildTestSurface() {
         return new SurfaceControl.Builder()
                 .setContainerLayer()