Merge "Add ACTION_SWITCH_DISPLAY_UNFOLD to LatencyTracker"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9975e2c412
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,6 +157,12 @@ public interface SysUIComponent {
|
||||
@SysUISingleton
|
||||
ContextComponentHelper getContextComponentHelper();
|
||||
|
||||
/**
|
||||
* Creates a UnfoldLatencyTracker.
|
||||
*/
|
||||
@SysUISingleton
|
||||
UnfoldLatencyTracker getUnfoldLatencyTracker();
|
||||
|
||||
/**
|
||||
* Main dependency providing module.
|
||||
*/
|
||||
|
||||
@@ -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) })
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user