Loading core/java/com/android/internal/util/LatencyTracker.java +13 −3 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 */ Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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"); } Loading packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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, Loading packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +8 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading @@ -155,6 +157,12 @@ public interface SysUIComponent { @SysUISingleton ContextComponentHelper getContextComponentHelper(); /** * Creates a UnfoldLatencyTracker. */ @SysUISingleton UnfoldLatencyTracker getUnfoldLatencyTracker(); /** * Main dependency providing module. */ Loading packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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) }) } packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * 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 Loading
core/java/com/android/internal/util/LatencyTracker.java +13 −3 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 */ Loading @@ -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 { Loading @@ -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; Loading Loading @@ -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"); } Loading
packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +6 −0 Original line number Diff line number Diff line Loading @@ -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, Loading
packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +8 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); } /** Loading @@ -155,6 +157,12 @@ public interface SysUIComponent { @SysUISingleton ContextComponentHelper getContextComponentHelper(); /** * Creates a UnfoldLatencyTracker. */ @SysUISingleton UnfoldLatencyTracker getUnfoldLatencyTracker(); /** * Main dependency providing module. */ Loading
packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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) }) }
packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * 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