diff options
| author | 2021-12-09 20:20:21 +0100 | |
|---|---|---|
| committer | 2021-12-14 21:57:48 +0100 | |
| commit | ea41bd1c529dfa6e096beb43e4b7256c2575666d (patch) | |
| tree | e90c1edeb19c7db6dc5082417234e43dcac5b6b0 | |
| parent | 6049f433873a2b7e4d950fa7a2bc2394693c292d (diff) | |
Add ACTION_SWITCH_DISPLAY_UNFOLD to LatencyTracker
Thanks to this, we will be able to create a dashboard of the time needed to turn on the screen after unfolding the device.
Test: atest DeviceFoldStateProviderTest
Test: atest KeyguardViewMediatorTest
Test: atest UnfoldLatencyTrackerTest
Bug: 209963184
Change-Id: I078be6431e1053bd9658b5756ab2d54f6f28c630
7 files changed, 262 insertions, 14 deletions
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 4c519f4c779f..e95ba884fed4 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -122,6 +122,11 @@ public class LatencyTracker { */ public static final int ACTION_USER_SWITCH = 12; + /** + * Time it takes to turn on the inner screen for a foldable device. + */ + public static final int ACTION_SWITCH_DISPLAY_UNFOLD = 13; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -135,7 +140,8 @@ public class LatencyTracker { ACTION_ROTATE_SCREEN_SENSOR, ACTION_ROTATE_SCREEN_CAMERA_CHECK, ACTION_LOCKSCREEN_UNLOCK, - ACTION_USER_SWITCH + ACTION_USER_SWITCH, + ACTION_SWITCH_DISPLAY_UNFOLD }; /** @hide */ @@ -152,7 +158,8 @@ public class LatencyTracker { ACTION_ROTATE_SCREEN_SENSOR, ACTION_ROTATE_SCREEN_CAMERA_CHECK, ACTION_LOCKSCREEN_UNLOCK, - ACTION_USER_SWITCH + ACTION_USER_SWITCH, + ACTION_SWITCH_DISPLAY_UNFOLD }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -171,7 +178,8 @@ public class LatencyTracker { FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK, - FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH + FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH, + FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD }; private static LatencyTracker sLatencyTracker; @@ -257,6 +265,8 @@ public class LatencyTracker { return "ACTION_LOCKSCREEN_UNLOCK"; case 13: return "ACTION_USER_SWITCH"; + case 14: + return "ACTION_SWITCH_DISPLAY_UNFOLD"; default: throw new IllegalArgumentException("Invalid action"); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index ea93a3b9375e..ac946cacdc5b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -33,6 +33,12 @@ import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider import java.lang.IllegalStateException import java.util.concurrent.Executor +/** + * Factory for [UnfoldTransitionProgressProvider]. + * + * This is needed as Launcher has to create the object manually. + * Sysui create it using dagger (see [UnfoldTransitionModule]). + */ fun createUnfoldTransitionProgressProvider( context: Context, config: UnfoldTransitionConfig, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index e3376789c0d1..af0697996f43 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -28,6 +28,7 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper; import com.android.systemui.people.PeopleProvider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.unfold.UnfoldLatencyTracker; import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; @@ -135,6 +136,7 @@ public interface SysUIComponent { // No init method needed, just needs to be gotten so that it's created. getMediaTttChipController(); getMediaTttCommandLineHelper(); + getUnfoldLatencyTracker().init(); } /** @@ -156,6 +158,12 @@ public interface SysUIComponent { ContextComponentHelper getContextComponentHelper(); /** + * Creates a UnfoldLatencyTracker. + */ + @SysUISingleton + UnfoldLatencyTracker getUnfoldLatencyTracker(); + + /** * Main dependency providing module. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt new file mode 100644 index 000000000000..7f63d6c5e778 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2021 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.content.Context +import android.hardware.devicestate.DeviceStateManager +import com.android.internal.util.LatencyTracker +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.UiBackground +import com.android.systemui.keyguard.ScreenLifecycle +import java.util.concurrent.Executor +import javax.inject.Inject + +/** + * Logs performance metrics regarding time to turn the inner screen on. + * + * This class assumes that [onFoldEvent] is always called before [onScreenTurnedOn]. + * This should be used from only one process. + * For now, the focus is on the time the inner display is visible, but in the future, it is easily + * possible to monitor the time to go from the inner screen to the outer. + */ +@SysUISingleton +class UnfoldLatencyTracker @Inject constructor( + private val latencyTracker: LatencyTracker, + private val deviceStateManager: DeviceStateManager, + @UiBackground private val uiBgExecutor: Executor, + private val context: Context, + private val screenLifecycle: ScreenLifecycle +) : ScreenLifecycle.Observer { + + private var folded: Boolean? = null + private val foldStateListener = FoldStateListener(context) + private val isFoldable: Boolean + get() = context.resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates).isNotEmpty() + + /** Registers for relevant events only if the device is foldable. */ + fun init() { + if (!isFoldable) { + return + } + deviceStateManager.registerCallback(uiBgExecutor, foldStateListener) + screenLifecycle.addObserver(this) + } + + /** + * To be called when the screen becomes visible. + * + * This is safe to call also when unsure whether the device is not a foldable, as it emits the + * end action event only if we previously received a fold state. + */ + override fun onScreenTurnedOn() { + if (folded == false) { + latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) + } + } + + private fun onFoldEvent(folded: Boolean) { + if (this.folded != folded) { + this.folded = folded + if (!folded) { // unfolding started + latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) + } + } + } + + private inner class FoldStateListener(context: Context) : + DeviceStateManager.FoldStateListener(context, { onFoldEvent(it) }) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt new file mode 100644 index 000000000000..f600b12993b9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2021 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.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.internal.util.LatencyTracker +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.ScreenLifecycle +import com.android.systemui.unfold.util.FoldableDeviceStates +import com.android.systemui.unfold.util.FoldableTestUtils +import com.android.systemui.util.mockito.any +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.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class UnfoldLatencyTrackerTest : SysuiTestCase() { + + @Mock + lateinit var latencyTracker: LatencyTracker + + @Mock + lateinit var deviceStateManager: DeviceStateManager + + @Mock + lateinit var screenLifecycle: ScreenLifecycle + + @Captor + private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> + + @Captor + private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer> + + private lateinit var deviceStates: FoldableDeviceStates + + private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + unfoldLatencyTracker = UnfoldLatencyTracker( + latencyTracker, + deviceStateManager, + context.mainExecutor, + context, + screenLifecycle + ) + deviceStates = FoldableTestUtils.findDeviceStates(context) + + verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture()) + verify(screenLifecycle).addObserver(screenLifecycleCaptor.capture()) + } + + @Test + fun unfold_eventPropagated() { + sendFoldEvent(folded = false) + sendScreenTurnedOnEvent() + + verify(latencyTracker).onActionStart(any()) + verify(latencyTracker).onActionEnd(any()) + } + + @Test + fun fold_eventNotPropagated() { + sendFoldEvent(folded = true) + sendScreenTurnedOnEvent() // outer display on. + + verifyNoMoreInteractions(latencyTracker) + } + + @Test + fun onScreenTurnedOn_stateNeverSet_eventNotPropagated() { + sendScreenTurnedOnEvent() + + verifyNoMoreInteractions(latencyTracker) + } + + private fun sendFoldEvent(folded: Boolean) { + val state = if (folded) deviceStates.folded else deviceStates.unfolded + foldStateListenerCaptor.value.onStateChanged(state) + } + + private fun sendScreenTurnedOnEvent() { + screenLifecycleCaptor.value.onScreenTurnedOn() + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index be1720d07d11..46e0b1238013 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -26,9 +26,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.unfold.util.FoldableDeviceStates +import com.android.systemui.unfold.util.FoldableTestUtils import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat -import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -70,8 +71,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { private val foldUpdates: MutableList<Int> = arrayListOf() private val hingeAngleUpdates: MutableList<Float> = arrayListOf() - private var foldedDeviceState: Int = 0 - private var unfoldedDeviceState: Int = 0 + private lateinit var deviceStates: FoldableDeviceStates private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @@ -79,13 +79,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - val foldedDeviceStates: IntArray = context.resources.getIntArray( - com.android.internal.R.array.config_foldedDeviceStates) - assumeTrue("Test should be launched on a foldable device", - foldedDeviceStates.isNotEmpty()) - - foldedDeviceState = foldedDeviceStates.maxOrNull()!! - unfoldedDeviceState = foldedDeviceState + 1 + deviceStates = FoldableTestUtils.findDeviceStates(context) foldStateProvider = DeviceFoldStateProvider( context, @@ -282,7 +276,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } private fun setFoldState(folded: Boolean) { - val state = if (folded) foldedDeviceState else unfoldedDeviceState + val state = if (folded) deviceStates.folded else deviceStates.unfolded foldStateListenerCaptor.value.onStateChanged(state) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt new file mode 100644 index 000000000000..01b535964ba6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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.util + +import android.content.Context +import org.junit.Assume.assumeTrue + +object FoldableTestUtils { + + /** Finds device state for folded and unfolded. */ + fun findDeviceStates(context: Context): FoldableDeviceStates { + val foldedDeviceStates: IntArray = context.resources.getIntArray( + com.android.internal.R.array.config_foldedDeviceStates) + assumeTrue("Test should be launched on a foldable device", + foldedDeviceStates.isNotEmpty()) + + val folded = foldedDeviceStates.maxOrNull()!! + val unfolded = folded + 1 + return FoldableDeviceStates(folded = folded, unfolded = unfolded) + } +} + +data class FoldableDeviceStates(val folded: Int, val unfolded: Int)
\ No newline at end of file |