summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolo' Mazzucato <nicomazz@google.com> 2021-12-09 20:20:21 +0100
committer Nicolo' Mazzucato <nicomazz@google.com> 2021-12-14 21:57:48 +0100
commitea41bd1c529dfa6e096beb43e4b7256c2575666d (patch)
treee90c1edeb19c7db6dc5082417234e43dcac5b6b0
parent6049f433873a2b7e4d950fa7a2bc2394693c292d (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
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java16
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt110
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt37
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