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

Commit e8359bcd authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Automerger Merge Worker
Browse files

Merge "Monitor fold to AOD latency" into tm-qpr-dev am: 560e645f

parents cf7e8cb0 560e645f
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -142,6 +142,11 @@ public class LatencyTracker {
     */
    public static final int ACTION_LOAD_SHARE_SHEET = 16;

    /**
     * Time it takes to show AOD display after folding the device.
     */
    public static final int ACTION_FOLD_TO_AOD = 17;

    private static final int[] ACTIONS_ALL = {
        ACTION_EXPAND_PANEL,
        ACTION_TOGGLE_RECENTS,
@@ -160,6 +165,7 @@ public class LatencyTracker {
        ACTION_UDFPS_ILLUMINATE,
        ACTION_SHOW_BACK_ARROW,
        ACTION_LOAD_SHARE_SHEET,
        ACTION_FOLD_TO_AOD,
    };

    /** @hide */
@@ -181,6 +187,7 @@ public class LatencyTracker {
        ACTION_UDFPS_ILLUMINATE,
        ACTION_SHOW_BACK_ARROW,
        ACTION_LOAD_SHARE_SHEET,
        ACTION_FOLD_TO_AOD
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Action {
@@ -204,6 +211,7 @@ public class LatencyTracker {
            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
            FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD
    };

    private static LatencyTracker sLatencyTracker;
@@ -297,6 +305,8 @@ public class LatencyTracker {
                return "ACTION_SHOW_BACK_ARROW";
            case 17:
                return "ACTION_LOAD_SHARE_SHEET";
            case 19:
                return "ACTION_FOLD_TO_AOD";
            default:
                throw new IllegalArgumentException("Invalid action");
        }
+28 −20
Original line number Diff line number Diff line
@@ -3881,29 +3881,37 @@ public final class NotificationPanelViewController extends PanelViewController {
    }

    /**
     * Starts fold to AOD animation
     * Starts fold to AOD animation.
     *
     * @param startAction invoked when the animation starts.
     * @param endAction invoked when the animation finishes, also if it was cancelled.
     * @param cancelAction invoked when the animation is cancelled, before endAction.
     */
    public void startFoldToAodAnimation(Runnable endAction) {
    public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
            Runnable cancelAction) {
        mView.animate()
            .translationX(0)
            .alpha(1f)
            .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
            .setInterpolator(EMPHASIZED_DECELERATE)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    startAction.run();
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                        endAction.run();
                    cancelAction.run();
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    endAction.run();
                }
                })
                .setUpdateListener(anim -> {
            }).setUpdateListener(anim -> {
                mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
                })
                .start();
            }).start();
    }

    /**
+55 −11
Original line number Diff line number Diff line
@@ -22,11 +22,12 @@ import android.os.Handler
import android.os.PowerManager
import android.provider.Settings
import androidx.core.view.OneShotPreDrawListener
import com.android.internal.util.LatencyTracker
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.ScreenOffAnimation
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
@@ -47,7 +48,8 @@ constructor(
    private val context: Context,
    private val deviceStateManager: DeviceStateManager,
    private val wakefulnessLifecycle: WakefulnessLifecycle,
    private val globalSettings: GlobalSettings
    private val globalSettings: GlobalSettings,
    private val latencyTracker: LatencyTracker,
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {

    private lateinit var mCentralSurfaces: CentralSurfaces
@@ -64,12 +66,14 @@ constructor(
    private var isAnimationPlaying = false

    private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
    private val foldToAodLatencyTracker = FoldToAodLatencyTracker()

    private val startAnimationRunnable = Runnable {
        mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation {
            // End action
            setAnimationState(playing = false)
        }
        mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation(
            /* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
            /* endAction= */ { setAnimationState(playing = false) },
            /* cancelAction= */ { setAnimationState(playing = false) },
        )
    }

    override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
@@ -82,11 +86,13 @@ constructor(
    /** Returns true if we should run fold to AOD animation */
    override fun shouldPlayAnimation(): Boolean = shouldPlayAnimation

    override fun startAnimation(): Boolean =
        if (alwaysOnEnabled &&
    private fun shouldStartAnimation(): Boolean =
        alwaysOnEnabled &&
            wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
            globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
        ) {

    override fun startAnimation(): Boolean =
        if (shouldStartAnimation()) {
            setAnimationState(playing = true)
            mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
            true
@@ -97,6 +103,7 @@ constructor(

    override fun onStartedWakingUp() {
        if (isAnimationPlaying) {
            foldToAodLatencyTracker.cancel()
            handler.removeCallbacks(startAnimationRunnable)
            mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
        }
@@ -137,7 +144,8 @@ constructor(
            // but we should wait for the initial animation preparations to be drawn
            // (setting initial alpha/translation)
            OneShotPreDrawListener.add(
                mCentralSurfaces.notificationPanelViewController.view, onReady
                mCentralSurfaces.notificationPanelViewController.view,
                onReady
            )
        } else {
            // No animation, call ready callback immediately
@@ -209,5 +217,41 @@ constructor(
                    isFoldHandled = false
                }
                this.isFolded = isFolded
            })
                if (isFolded) {
                    foldToAodLatencyTracker.onFolded()
                }
            }
        )

    /**
     * Tracks the latency of fold to AOD using [LatencyTracker].
     *
     * Events that trigger start and end are:
     *
     * - Start: Once [DeviceStateManager] sends the folded signal [FoldToAodLatencyTracker.onFolded]
     * is called and latency tracking starts.
     * - End: Once the fold -> AOD animation starts, [FoldToAodLatencyTracker.onAnimationStarted] is
     * called, and latency tracking stops.
     */
    private inner class FoldToAodLatencyTracker {

        /** Triggers the latency logging, if needed. */
        fun onFolded() {
            if (shouldStartAnimation()) {
                latencyTracker.onActionStart(LatencyTracker.ACTION_FOLD_TO_AOD)
            }
        }
        /**
         * Called once the Fold -> AOD animation is started.
         *
         * For latency tracking, this determines the end of the fold to aod action.
         */
        fun onAnimationStarted() {
            latencyTracker.onActionEnd(LatencyTracker.ACTION_FOLD_TO_AOD)
        }

        fun cancel() {
            latencyTracker.onActionCancel(LatencyTracker.ACTION_FOLD_TO_AOD)
        }
    }
}
+166 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.unfold

import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
import android.os.Handler
import android.os.PowerManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.view.ViewGroup
import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.unfold.util.FoldableDeviceStates
import com.android.systemui.unfold.util.FoldableTestUtils
import com.android.systemui.util.mockito.any
import com.android.systemui.util.settings.GlobalSettings
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations

@RunWith(AndroidTestingRunner::class)
@SmallTest
@RunWithLooper
class FoldAodAnimationControllerTest : SysuiTestCase() {

    @Mock lateinit var deviceStateManager: DeviceStateManager

    @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle

    @Mock lateinit var globalSettings: GlobalSettings

    @Mock lateinit var latencyTracker: LatencyTracker

    @Mock lateinit var centralSurfaces: CentralSurfaces

    @Mock lateinit var lightRevealScrim: LightRevealScrim

    @Mock lateinit var notificationPanelViewController: NotificationPanelViewController

    @Mock lateinit var viewGroup: ViewGroup

    @Mock lateinit var viewTreeObserver: ViewTreeObserver

    @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>

    private lateinit var deviceStates: FoldableDeviceStates

    private lateinit var testableLooper: TestableLooper

    lateinit var foldAodAnimationController: FoldAodAnimationController

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        testableLooper = TestableLooper.get(this)

        foldAodAnimationController =
            FoldAodAnimationController(
                    Handler(testableLooper.looper),
                    context.mainExecutor,
                    context,
                    deviceStateManager,
                    wakefulnessLifecycle,
                    globalSettings,
                    latencyTracker,
                )
                .apply { initialize(centralSurfaces, lightRevealScrim) }
        deviceStates = FoldableTestUtils.findDeviceStates(context)

        whenever(notificationPanelViewController.view).thenReturn(viewGroup)
        whenever(viewGroup.viewTreeObserver).thenReturn(viewTreeObserver)
        whenever(wakefulnessLifecycle.lastSleepReason)
            .thenReturn(PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD)
        whenever(centralSurfaces.notificationPanelViewController)
            .thenReturn(notificationPanelViewController)
        whenever(notificationPanelViewController.startFoldToAodAnimation(any(), any(), any()))
            .then {
                val onActionStarted = it.arguments[0] as Runnable
                onActionStarted.run()
            }
        verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture())

        foldAodAnimationController.setIsDozing(dozing = true)
        setAodEnabled(enabled = true)
        sendFoldEvent(folded = false)
    }

    @Test
    fun onFolded_aodDisabled_doesNotLogLatency() {
        setAodEnabled(enabled = false)

        fold()
        simulateScreenTurningOn()

        verifyNoMoreInteractions(latencyTracker)
    }

    @Test
    fun onFolded_aodEnabled_logsLatency() {
        setAodEnabled(enabled = true)

        fold()
        simulateScreenTurningOn()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker).onActionEnd(any())
    }

    @Test
    fun onFolded_animationCancelled_doesNotLogLatency() {
        setAodEnabled(enabled = true)

        fold()
        foldAodAnimationController.onScreenTurningOn({})
        foldAodAnimationController.onStartedWakingUp()
        testableLooper.processAllMessages()

        verify(latencyTracker).onActionStart(any())
        verify(latencyTracker).onActionCancel(any())
    }

    private fun simulateScreenTurningOn() {
        foldAodAnimationController.onScreenTurningOn({})
        foldAodAnimationController.onScreenTurnedOn()
        testableLooper.processAllMessages()
    }

    private fun fold() = sendFoldEvent(folded = true)

    private fun setAodEnabled(enabled: Boolean) =
        foldAodAnimationController.onAlwaysOnChanged(alwaysOn = enabled)

    private fun sendFoldEvent(folded: Boolean) {
        val state = if (folded) deviceStates.folded else deviceStates.unfolded
        foldStateListenerCaptor.value.onStateChanged(state)
    }
}