1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.tools.flicker.extractors 18 19 import android.tools.Timestamp 20 import android.tools.Timestamps 21 import android.tools.io.Reader 22 import android.tools.traces.events.Cuj 23 import android.tools.traces.events.CujType 24 import android.tools.traces.wm.Transition 25 import android.util.Log 26 import kotlin.math.max 27 import kotlin.math.min 28 29 class TaggedScenarioExtractor( 30 private val targetTag: CujType, 31 private val transitionMatcher: TransitionMatcher?, 32 private val adjustCuj: CujAdjust, 33 private val additionalCujFilter: ((Cuj) -> Boolean)? = null, 34 private val ignoreIfNoMatchingTransition: Boolean = false, 35 ) : ScenarioExtractor { 36 companion object { 37 val LOG_TAG = "FlickerTaggedScenarioExtractor" 38 } 39 40 override fun extract(reader: Reader): List<TraceSlice> { 41 val cujTrace = reader.readCujTrace() ?: error("Missing CUJ trace") 42 43 val targetCujEntries = 44 cujTrace.entries 45 .filter { it.cuj === targetTag } 46 .filter { !it.canceled } 47 .filter { additionalCujFilter?.invoke(it) ?: true } 48 .map { adjustCuj.adjustCuj(it, reader) } 49 50 if (targetCujEntries.isEmpty()) { 51 // No scenarios to extract here 52 return emptyList() 53 } 54 55 return targetCujEntries.mapNotNull { cujEntry -> 56 val associatedTransitions = transitionMatcher?.getMatches(reader, cujEntry) 57 58 if ((associatedTransitions?.size ?: 0) > 1) { 59 Log.w( 60 LOG_TAG, 61 "Got more than one associated transition: " + 62 "[${associatedTransitions?.joinToString()}]. " + 63 "Picking first transition in list." 64 ) 65 } 66 67 val associatedTransition = associatedTransitions?.firstOrNull() 68 69 if (ignoreIfNoMatchingTransition && associatedTransition == null) { 70 return@mapNotNull null 71 } 72 73 require( 74 cujEntry.startTimestamp.hasAllTimestamps && cujEntry.endTimestamp.hasAllTimestamps 75 ) 76 77 val startTimestamp = 78 estimateScenarioStartTimestamp(cujEntry, associatedTransition, reader) 79 val endTimestamp = estimateScenarioEndTimestamp(cujEntry, associatedTransition, reader) 80 81 TraceSlice( 82 startTimestamp, 83 endTimestamp, 84 associatedCuj = cujEntry.cuj, 85 associatedTransition = associatedTransition 86 ) 87 } 88 } 89 90 private fun estimateScenarioStartTimestamp( 91 cujEntry: Cuj, 92 associatedTransition: Transition?, 93 reader: Reader 94 ): Timestamp { 95 val interpolatedStartTimestamp = 96 if (associatedTransition != null) { 97 Utils.interpolateStartTimestampFromTransition(associatedTransition, reader) 98 } else { 99 null 100 } 101 102 return Timestamps.from( 103 elapsedNanos = 104 min( 105 cujEntry.startTimestamp.elapsedNanos, 106 interpolatedStartTimestamp?.elapsedNanos ?: cujEntry.startTimestamp.elapsedNanos 107 ), 108 systemUptimeNanos = 109 min( 110 cujEntry.startTimestamp.systemUptimeNanos, 111 interpolatedStartTimestamp?.systemUptimeNanos 112 ?: cujEntry.startTimestamp.systemUptimeNanos 113 ), 114 unixNanos = 115 min( 116 cujEntry.startTimestamp.unixNanos, 117 interpolatedStartTimestamp?.unixNanos ?: cujEntry.startTimestamp.unixNanos 118 ) 119 ) 120 } 121 122 private fun estimateScenarioEndTimestamp( 123 cujEntry: Cuj, 124 associatedTransition: Transition?, 125 reader: Reader 126 ): Timestamp { 127 val interpolatedEndTimestamp = 128 if (associatedTransition != null) { 129 Utils.interpolateFinishTimestampFromTransition(associatedTransition, reader) 130 } else { 131 val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace") 132 val nextSfEntry = layersTrace.getFirstEntryWithOnDisplayAfter(cujEntry.endTimestamp) 133 Utils.getFullTimestampAt(nextSfEntry, reader) 134 } 135 136 return Timestamps.from( 137 elapsedNanos = 138 max(cujEntry.endTimestamp.elapsedNanos, interpolatedEndTimestamp.elapsedNanos), 139 systemUptimeNanos = 140 max( 141 cujEntry.endTimestamp.systemUptimeNanos, 142 interpolatedEndTimestamp.systemUptimeNanos 143 ), 144 unixNanos = max(cujEntry.endTimestamp.unixNanos, interpolatedEndTimestamp.unixNanos) 145 ) 146 } 147 } 148