Merge "Monitor fold to AOD latency" into tm-qpr-dev

This commit is contained in:
Nicolò Mazzucato
2022-08-03 11:52:06 +00:00
committed by Android (Google) Code Review
4 changed files with 259 additions and 31 deletions

View File

@@ -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");
}

View File

@@ -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 onAnimationCancel(Animator animation) {
endAction.run();
}
.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 onAnimationEnd(Animator animation) {
endAction.run();
}
})
.setUpdateListener(anim -> {
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
})
.start();
@Override
public void onAnimationCancel(Animator animation) {
cancelAction.run();
}
@Override
public void onAnimationEnd(Animator animation) {
endAction.run();
}
}).setUpdateListener(anim -> {
mKeyguardStatusViewController.animateFoldToAod(anim.getAnimatedFraction());
}).start();
}
/**

View File

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

View File

@@ -0,0 +1,166 @@
/*
* 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)
}
}