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

Commit b6ba4334 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Send interaction hint when starting window animation

If we are starting a window animation, there is a very strong
correlation that usually just before the user took some kind of
action.

However, in some cases the power hint may be lost of there are not
continiously produced frames between the user interaction and the
window animation, like when opening the IME but it takes a while
until the IME comes up, or when the status bar automatically hides
after being temporarily shown in a fullscreen app.

Test: SurfaceAnimationRunnerTest
Test: Go to an immersive_sticky app, swipe down from top, wait
until status bar hides, make sure interaction hint gets sent.
Bug: 111253599

Change-Id: Id517d48a63d4c528265a808c49043d936d804625
parent 5c75b5b6
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.hardware.power.V1_0.PowerHint;
import android.os.PowerManagerInternal;
import android.util.ArrayMap;
import android.view.Choreographer;
import android.view.SurfaceControl;
@@ -57,6 +59,7 @@ class SurfaceAnimationRunner {
    private final AnimationHandler mAnimationHandler;
    private final Transaction mFrameTransaction;
    private final AnimatorFactory mAnimatorFactory;
    private final PowerManagerInternal mPowerManagerInternal;
    private boolean mApplyScheduled;

    @GuardedBy("mLock")
@@ -70,13 +73,15 @@ class SurfaceAnimationRunner {
    @GuardedBy("mLock")
    private boolean mAnimationStartDeferred;

    SurfaceAnimationRunner() {
        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction());
    SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal) {
        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction(),
                powerManagerInternal);
    }

    @VisibleForTesting
    SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
            AnimatorFactory animatorFactory, Transaction frameTransaction) {
            AnimatorFactory animatorFactory, Transaction frameTransaction,
            PowerManagerInternal powerManagerInternal) {
        SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
                0 /* timeout */);
        mFrameTransaction = frameTransaction;
@@ -87,6 +92,7 @@ class SurfaceAnimationRunner {
        mAnimatorFactory = animatorFactory != null
                ? animatorFactory
                : SfValueAnimator::new;
        mPowerManagerInternal = powerManagerInternal;
    }

    /**
@@ -231,6 +237,7 @@ class SurfaceAnimationRunner {
        synchronized (mLock) {
            startPendingAnimationsLocked();
        }
        mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
    }

    private void scheduleApplyTransaction() {
+1 −1
Original line number Diff line number Diff line
@@ -1061,7 +1061,7 @@ public class WindowManagerService extends IWindowManager.Stub
                PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
        mHoldingScreenWakeLock.setReferenceCounted(false);

        mSurfaceAnimationRunner = new SurfaceAnimationRunner();
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mPowerManagerInternal);

        mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
+20 −8
Original line number Diff line number Diff line
@@ -20,24 +20,23 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.animation.AnimationHandler;
import android.animation.AnimationHandler.AnimationFrameCallbackProvider;
import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Point;
import android.os.PowerManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import android.view.Choreographer;
import android.view.Choreographer.FrameCallback;
import android.view.SurfaceControl;
@@ -46,7 +45,6 @@ import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;

import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
import com.android.server.wm.SurfaceAnimationRunner.AnimatorFactory;

import org.junit.Before;
import org.junit.Rule;
@@ -71,6 +69,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
    @Mock SurfaceControl mMockSurface;
    @Mock Transaction mMockTransaction;
    @Mock AnimationSpec mMockAnimationSpec;
    @Mock PowerManagerInternal mMockPowerManager;
    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();

    private SurfaceAnimationRunner mSurfaceAnimationRunner;
@@ -81,7 +80,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
        super.setUp();
        mFinishCallbackLatch = new CountDownLatch(1);
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(null /* callbackProvider */, null,
                mMockTransaction);
                mMockTransaction, mMockPowerManager);
    }

    private void finishedCallback() {
@@ -113,7 +112,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
    @Test
    public void testCancel_notStarted() throws Exception {
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
                mMockTransaction);
                mMockTransaction, mMockPowerManager);
        mSurfaceAnimationRunner
                .startAnimation(createTranslateAnimation(), mMockSurface, mMockTransaction,
                this::finishedCallback);
@@ -126,7 +125,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
    @Test
    public void testCancel_running() throws Exception {
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
                mMockTransaction);
                mMockTransaction, mMockPowerManager);
        mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
                mMockTransaction, this::finishedCallback);
        waitUntilNextFrame();
@@ -156,7 +155,7 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
                    listener.onAnimationUpdate(animation);
                });
            }
        }, mMockTransaction);
        }, mMockTransaction, mMockPowerManager);
        when(mMockAnimationSpec.getDuration()).thenReturn(200L);
        mSurfaceAnimationRunner.startAnimation(mMockAnimationSpec, mMockSurface, mMockTransaction,
                this::finishedCallback);
@@ -184,6 +183,19 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase {
        assertFinishCallbackCalled();
    }

    @Test
    public void testPowerHint() throws Exception {
        mSurfaceAnimationRunner = new SurfaceAnimationRunner(new NoOpFrameCallbackProvider(), null,
                mMockTransaction, mMockPowerManager);
        mSurfaceAnimationRunner.startAnimation(createTranslateAnimation(), mMockSurface,
                mMockTransaction, this::finishedCallback);
        waitUntilNextFrame();

        // TODO: For some reason we don't have access to PowerHint definition from the tests. For
        // now let's just verify that we got some kind of hint.
        verify(mMockPowerManager).powerHint(anyInt(), anyInt());
    }

    private void waitUntilNextFrame() throws Exception {
        final CountDownLatch latch = new CountDownLatch(1);
        mSurfaceAnimationRunner.mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,