Loading packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateRepository.kt 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.system import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow /** Provides whether the device is folded. */ interface DeviceStateRepository { val isFolded: Flow<Boolean> } @Singleton class DeviceStateRepositoryImpl @Inject constructor( private val foldProvider: FoldProvider, @UnfoldMain private val executor: Executor, ) : DeviceStateRepository { override val isFolded: Flow<Boolean> get() = callbackFlow { val callback = FoldCallback { isFolded -> trySend(isFolded) } foldProvider.registerCallback(callback, executor) awaitClose { foldProvider.unregisterCallback(callback) } } .buffer(capacity = Channel.CONFLATED) } packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ abstract class SystemUnfoldSharedModule { @Binds abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider @Binds abstract fun deviceStateRepository(provider: DeviceStateRepositoryImpl): DeviceStateRepository @Binds @UnfoldMain abstract fun mainExecutor(@Main executor: Executor): Executor Loading packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.os.Trace /** * Utility class used to log state changes easily in a track with a custom name. * * Example of usage: * ```kotlin * class MyClass { * val screenStateLogger = TraceStateLogger("Screen state") * * fun onTurnedOn() { screenStateLogger.log("on") } * fun onTurnedOff() { screenStateLogger.log("off") } * } * ``` * * This creates a new slice in a perfetto trace only if the state is different than the previous * one. */ class TraceStateLogger( private val trackName: String, private val logOnlyIfDifferent: Boolean = true, private val instantEvent: Boolean = true ) { private var previousValue: String? = null /** If needed, logs the value to a track with name [trackName]. */ fun log(newValue: String) { if (instantEvent) { Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, newValue) } if (logOnlyIfDifferent && previousValue == newValue) return Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, 0) Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, newValue, 0) previousValue = newValue } } packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +22 −3 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.unfold import android.content.ContentResolver import android.content.Context import android.hardware.devicestate.DeviceStateManager import android.os.Trace import android.util.Log import com.android.internal.util.LatencyTracker import com.android.systemui.dagger.SysUISingleton Loading Loading @@ -57,6 +58,7 @@ constructor( private var folded: Boolean? = null private var isTransitionEnabled: Boolean? = null private val foldStateListener = FoldStateListener(context) private var unfoldInProgress = false private val isFoldable: Boolean get() = context.resources Loading Loading @@ -95,7 +97,7 @@ constructor( // the unfold animation (e.g. it could be disabled because of battery saver). // When animation is enabled finishing of the tracking will be done in onTransitionStarted. if (folded == false && isTransitionEnabled == false) { latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldEnded() if (DEBUG) { Log.d(TAG, "onScreenTurnedOn: ending ACTION_SWITCH_DISPLAY_UNFOLD") Loading @@ -116,7 +118,7 @@ constructor( } if (folded == false && isTransitionEnabled == true) { latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldEnded() if (DEBUG) { Log.d(TAG, "onTransitionStarted: ending ACTION_SWITCH_DISPLAY_UNFOLD") Loading @@ -124,6 +126,22 @@ constructor( } } private fun onUnfoldStarted() { if (unfoldInProgress) return unfoldInProgress = true // As LatencyTracker might be disabled, let's also log a parallel slice to the trace to be // able to debug all cases. latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, UNFOLD_IN_PROGRESS_TRACE_NAME, /* cookie= */ 0) } private fun onUnfoldEnded() { if (!unfoldInProgress) return unfoldInProgress = false latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) Trace.endAsyncSection(UNFOLD_IN_PROGRESS_TRACE_NAME, 0) } private fun onFoldEvent(folded: Boolean) { val oldFolded = this.folded Loading @@ -139,7 +157,7 @@ constructor( // unfolding the device. if (oldFolded != null && !folded) { // Unfolding started latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldStarted() isTransitionEnabled = transitionProgressProvider.isPresent && contentResolver.areAnimationsEnabled() Loading @@ -159,4 +177,5 @@ constructor( } private const val TAG = "UnfoldLatencyTracker" private const val UNFOLD_IN_PROGRESS_TRACE_NAME = "Switch displays during unfold" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE) packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.os.Trace import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.unfold.system.DeviceStateRepository import com.android.systemui.unfold.updates.FoldStateRepository import com.android.systemui.util.TraceStateLogger import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * Logs several unfold related details in a trace. Mainly used for debugging and investigate * droidfooders traces. */ @SysUISingleton class UnfoldTraceLogger @Inject constructor( private val context: Context, private val foldStateRepository: FoldStateRepository, @Application private val applicationScope: CoroutineScope, private val deviceStateRepository: DeviceStateRepository ) : CoreStartable { private val isFoldable: Boolean get() = context.resources .getIntArray(com.android.internal.R.array.config_foldedDeviceStates) .isNotEmpty() override fun start() { if (!isFoldable) return applicationScope.launch { val foldUpdateLogger = TraceStateLogger("FoldUpdate") foldStateRepository.foldUpdate.collect { foldUpdateLogger.log(it.name) } } applicationScope.launch { foldStateRepository.hingeAngle.collect { Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt()) } } applicationScope.launch { val foldedStateLogger = TraceStateLogger("FoldedState") deviceStateRepository.isFolded.collect { isFolded -> foldedStateLogger.log( if (isFolded) { "folded" } else { "unfolded" } ) } } } } Loading
packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateRepository.kt 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.system import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow /** Provides whether the device is folded. */ interface DeviceStateRepository { val isFolded: Flow<Boolean> } @Singleton class DeviceStateRepositoryImpl @Inject constructor( private val foldProvider: FoldProvider, @UnfoldMain private val executor: Executor, ) : DeviceStateRepository { override val isFolded: Flow<Boolean> get() = callbackFlow { val callback = FoldCallback { isFolded -> trySend(isFolded) } foldProvider.registerCallback(callback, executor) awaitClose { foldProvider.unregisterCallback(callback) } } .buffer(capacity = Channel.CONFLATED) }
packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,9 @@ abstract class SystemUnfoldSharedModule { @Binds abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider @Binds abstract fun deviceStateRepository(provider: DeviceStateRepositoryImpl): DeviceStateRepository @Binds @UnfoldMain abstract fun mainExecutor(@Main executor: Executor): Executor Loading
packages/SystemUI/shared/src/com/android/systemui/util/TraceStateLogger.kt 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.util import android.os.Trace /** * Utility class used to log state changes easily in a track with a custom name. * * Example of usage: * ```kotlin * class MyClass { * val screenStateLogger = TraceStateLogger("Screen state") * * fun onTurnedOn() { screenStateLogger.log("on") } * fun onTurnedOff() { screenStateLogger.log("off") } * } * ``` * * This creates a new slice in a perfetto trace only if the state is different than the previous * one. */ class TraceStateLogger( private val trackName: String, private val logOnlyIfDifferent: Boolean = true, private val instantEvent: Boolean = true ) { private var previousValue: String? = null /** If needed, logs the value to a track with name [trackName]. */ fun log(newValue: String) { if (instantEvent) { Trace.instantForTrack(Trace.TRACE_TAG_APP, trackName, newValue) } if (logOnlyIfDifferent && previousValue == newValue) return Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, trackName, 0) Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, newValue, 0) previousValue = newValue } }
packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt +22 −3 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.unfold import android.content.ContentResolver import android.content.Context import android.hardware.devicestate.DeviceStateManager import android.os.Trace import android.util.Log import com.android.internal.util.LatencyTracker import com.android.systemui.dagger.SysUISingleton Loading Loading @@ -57,6 +58,7 @@ constructor( private var folded: Boolean? = null private var isTransitionEnabled: Boolean? = null private val foldStateListener = FoldStateListener(context) private var unfoldInProgress = false private val isFoldable: Boolean get() = context.resources Loading Loading @@ -95,7 +97,7 @@ constructor( // the unfold animation (e.g. it could be disabled because of battery saver). // When animation is enabled finishing of the tracking will be done in onTransitionStarted. if (folded == false && isTransitionEnabled == false) { latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldEnded() if (DEBUG) { Log.d(TAG, "onScreenTurnedOn: ending ACTION_SWITCH_DISPLAY_UNFOLD") Loading @@ -116,7 +118,7 @@ constructor( } if (folded == false && isTransitionEnabled == true) { latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldEnded() if (DEBUG) { Log.d(TAG, "onTransitionStarted: ending ACTION_SWITCH_DISPLAY_UNFOLD") Loading @@ -124,6 +126,22 @@ constructor( } } private fun onUnfoldStarted() { if (unfoldInProgress) return unfoldInProgress = true // As LatencyTracker might be disabled, let's also log a parallel slice to the trace to be // able to debug all cases. latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, UNFOLD_IN_PROGRESS_TRACE_NAME, /* cookie= */ 0) } private fun onUnfoldEnded() { if (!unfoldInProgress) return unfoldInProgress = false latencyTracker.onActionEnd(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) Trace.endAsyncSection(UNFOLD_IN_PROGRESS_TRACE_NAME, 0) } private fun onFoldEvent(folded: Boolean) { val oldFolded = this.folded Loading @@ -139,7 +157,7 @@ constructor( // unfolding the device. if (oldFolded != null && !folded) { // Unfolding started latencyTracker.onActionStart(LatencyTracker.ACTION_SWITCH_DISPLAY_UNFOLD) onUnfoldStarted() isTransitionEnabled = transitionProgressProvider.isPresent && contentResolver.areAnimationsEnabled() Loading @@ -159,4 +177,5 @@ constructor( } private const val TAG = "UnfoldLatencyTracker" private const val UNFOLD_IN_PROGRESS_TRACE_NAME = "Switch displays during unfold" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 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.os.Trace import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.unfold.system.DeviceStateRepository import com.android.systemui.unfold.updates.FoldStateRepository import com.android.systemui.util.TraceStateLogger import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * Logs several unfold related details in a trace. Mainly used for debugging and investigate * droidfooders traces. */ @SysUISingleton class UnfoldTraceLogger @Inject constructor( private val context: Context, private val foldStateRepository: FoldStateRepository, @Application private val applicationScope: CoroutineScope, private val deviceStateRepository: DeviceStateRepository ) : CoreStartable { private val isFoldable: Boolean get() = context.resources .getIntArray(com.android.internal.R.array.config_foldedDeviceStates) .isNotEmpty() override fun start() { if (!isFoldable) return applicationScope.launch { val foldUpdateLogger = TraceStateLogger("FoldUpdate") foldStateRepository.foldUpdate.collect { foldUpdateLogger.log(it.name) } } applicationScope.launch { foldStateRepository.hingeAngle.collect { Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt()) } } applicationScope.launch { val foldedStateLogger = TraceStateLogger("FoldedState") deviceStateRepository.isFolded.collect { isFolded -> foldedStateLogger.log( if (isFolded) { "folded" } else { "unfolded" } ) } } } }