blob: be3ead52cdb1d1e24bb060c1926e7913f7516ddc [file] [log] [blame]
/*
* 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.server.wm.flicker.service
import com.android.server.wm.traces.common.errors.ErrorState
import com.android.server.wm.traces.common.errors.ErrorTrace
import com.android.server.wm.traces.common.layers.LayersTrace
import com.android.server.wm.traces.common.service.ITransitionAssertor
import com.android.server.wm.traces.common.tags.Tag
import com.android.server.wm.traces.common.tags.TagTrace
import com.android.server.wm.traces.common.tags.Transition
import com.android.server.wm.traces.common.tags.TransitionTag
import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
/**
* Invokes the configured assertors and summarizes the results.
*/
class AssertionEngine(private val logger: (String) -> Unit) {
private val flickerAssertors = mapOf<ITransitionAssertor, Transition>(
// TODO: Add new detectors to invoke
)
fun analyze(
wmTrace: WindowManagerTrace,
layersTrace: LayersTrace,
tagTrace: TagTrace
): ErrorTrace {
val allStates = mutableListOf<ErrorState>()
val transitionTags = getTransitionTags(tagTrace)
flickerAssertors.forEach { (assertor, transition) ->
allStates.addAll(
splitWmTraceByTags(wmTrace, transitionTags, transition)
.flatMap { block ->
assertor.analyzeWmTrace(block).entries.asList()
}
)
allStates.addAll(
splitLayersTraceByTags(layersTrace, transitionTags, transition)
.flatMap { block ->
assertor.analyzeLayersTrace(block).entries.asList()
}
)
}
/* Ensure all error states with same timestamp are merged */
val errorStates = allStates.distinct()
.groupBy({ it.timestamp }, { it.errors.asList() })
.mapValues { (key, value) ->
ErrorState(value.flatten().toTypedArray(), key.toString()) }
.values.toTypedArray()
return ErrorTrace(errorStates, source = "")
}
/**
* Extracts all [TransitionTag]s from a [TagTrace].
*
* @param tagTrace Tag Trace
* @return a list with [TransitionTag]
*/
fun getTransitionTags(tagTrace: TagTrace): List<TransitionTag> {
return tagTrace.entries.flatMap { state ->
state.tags.filter { tag -> tag.isStartTag }
.map {
TransitionTag(
tag = it,
startTimestamp = state.timestamp,
endTimestamp = getEndTagTimestamp(tagTrace, it)
)
}
}
}
private fun getEndTagTimestamp(tagTrace: TagTrace, tag: Tag): Long {
val finalTag = tag.copy(isStartTag = false)
return tagTrace.entries.firstOrNull { state -> state.tags.contains(finalTag) }?.timestamp
?: throw RuntimeException("All open tags should be closed!")
}
/**
* Splits a wmTrace by a [Transition].
*
* @param wmTrace Window Manager trace
* @param transitionTags a list with all [TransitionTag]s
* @param transition the [Transition] to filter the list by
* @return a list with [WindowManagerTrace] blocks
*/
fun splitWmTraceByTags(
wmTrace: WindowManagerTrace,
transitionTags: List<TransitionTag>,
transition: Transition
): List<WindowManagerTrace> {
val wmTags = transitionTags
.filter { transitionTag ->
transitionTag.tag.taskId > 0 ||
transitionTag.tag.windowToken.isNotEmpty() ||
transitionTag.isEmpty()
}.filter { transitionTag -> transitionTag.tag.transition == transition }
return wmTags.map { tag -> wmTrace.filter(tag.startTimestamp, tag.endTimestamp) }
}
/**
* Splits a layersTrace by a [Transition].
*
* @param layersTrace Surface Flinger trace
* @param transitionTags a list with all [TransitionTag]s
* @param transition the [Transition] to filter the list by
* @return a list with [LayersTrace] blocks
*/
fun splitLayersTraceByTags(
layersTrace: LayersTrace,
transitionTags: List<TransitionTag>,
transition: Transition
): List<LayersTrace> {
val layersTags = transitionTags
.filter { transitionTag -> transitionTag.tag.layerId > 0 || transitionTag.isEmpty() }
.filter { transitionTag -> transitionTag.tag.transition == transition }
return layersTags.map { tag ->
layersTrace.filter(tag.startTimestamp, tag.endTimestamp)
}
}
}