Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b5f97ac authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Set transform hint before rotation transaction is applied

Unlike legacy transition applies setDisplayProjection immediately
when applying rotation change, shell transition applies the
display projection in shell side when the animation is ready.
So if uses the surface when receiving configuration change but
the transaction is not applied yet, it may get an intermediate
inconsistent transform hint.

With this change:
Rotation changed -> send config -> apply pending transaction with
fixed transform hint -> transition ready -> shell starts animation
and applies display projection and unsets fixed transform hint.

Bug: 270282500
Test: atest SurfaceControlTests#testSurfaceChangedOnRotation
Change-Id: Id4ad45adb9719b078f46040a09f786092c325a8a
parent dfc317e1
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -2181,6 +2181,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        }

        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);
+7 −0
Original line number Diff line number Diff line
@@ -806,6 +806,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
            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();
+1 −0
Original line number Diff line number Diff line
@@ -725,6 +725,7 @@ class TransitionController {
        // Run state-validation checks when no transitions are active anymore.
        if (!inTransition()) {
            validateStates();
            mAtm.mWindowManager.onAnimationFinished();
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -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"
+59 −0
Original line number Diff line number Diff line
@@ -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 @@ public class SurfaceControlTests {
        }
    }

    @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()