Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf977786 authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge "Improve unfold related logging in perfetto traces" into main

parents 416e75d3 2b2a5651
Loading
Loading
Loading
Loading
+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)
}
+3 −0
Original line number Diff line number Diff line
@@ -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
+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
    }
}
+22 −3
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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")
@@ -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")
@@ -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

@@ -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()

@@ -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)
+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