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 const 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( 130 associatedTransition, 131 reader, 132 cujEntry.toString(), 133 ) 134 } else { 135 val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace") 136 val nextSfEntry = layersTrace.getFirstEntryWithOnDisplayAfter(cujEntry.endTimestamp) 137 Utils.getFullTimestampAt(nextSfEntry, reader) 138 } 139 140 return Timestamps.from( 141 elapsedNanos = 142 max(cujEntry.endTimestamp.elapsedNanos, interpolatedEndTimestamp.elapsedNanos), 143 systemUptimeNanos = 144 max( 145 cujEntry.endTimestamp.systemUptimeNanos, 146 interpolatedEndTimestamp.systemUptimeNanos, 147 ), 148 unixNanos = max(cujEntry.endTimestamp.unixNanos, interpolatedEndTimestamp.unixNanos), 149 ) 150 } 151 } 152