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 Log.d( 78 LOG_TAG, 79 "Estimating start timestamp for $cujEntry " + 80 "with associated transition: $associatedTransition", 81 ) 82 83 val startTimestamp = 84 estimateScenarioStartTimestamp(cujEntry, associatedTransition, reader) 85 val endTimestamp = estimateScenarioEndTimestamp(cujEntry, associatedTransition, reader) 86 87 TraceSlice( 88 startTimestamp, 89 endTimestamp, 90 associatedCuj = cujEntry.cuj, 91 associatedTransition = associatedTransition, 92 ) 93 } 94 } 95 96 private fun estimateScenarioStartTimestamp( 97 cujEntry: Cuj, 98 associatedTransition: Transition?, 99 reader: Reader, 100 ): Timestamp { 101 val interpolatedStartTimestamp = 102 if (associatedTransition != null) { 103 Utils.interpolateStartTimestampFromTransition(associatedTransition, reader) 104 } else { 105 null 106 } 107 108 return Timestamps.from( 109 elapsedNanos = 110 min( 111 cujEntry.startTimestamp.elapsedNanos, 112 interpolatedStartTimestamp?.elapsedNanos ?: cujEntry.startTimestamp.elapsedNanos, 113 ), 114 systemUptimeNanos = 115 min( 116 cujEntry.startTimestamp.systemUptimeNanos, 117 interpolatedStartTimestamp?.systemUptimeNanos 118 ?: cujEntry.startTimestamp.systemUptimeNanos, 119 ), 120 unixNanos = 121 min( 122 cujEntry.startTimestamp.unixNanos, 123 interpolatedStartTimestamp?.unixNanos ?: cujEntry.startTimestamp.unixNanos, 124 ), 125 ) 126 } 127 128 private fun estimateScenarioEndTimestamp( 129 cujEntry: Cuj, 130 associatedTransition: Transition?, 131 reader: Reader, 132 ): Timestamp { 133 val interpolatedEndTimestamp = 134 if (associatedTransition != null) { 135 Utils.interpolateFinishTimestampFromTransition( 136 associatedTransition, 137 reader, 138 cujEntry.toString(), 139 ) 140 } else { 141 val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace") 142 val nextSfEntry = layersTrace.getFirstEntryWithOnDisplayAfter(cujEntry.endTimestamp) 143 Utils.getFullTimestampAt(nextSfEntry, reader) 144 } 145 146 return Timestamps.from( 147 elapsedNanos = 148 max(cujEntry.endTimestamp.elapsedNanos, interpolatedEndTimestamp.elapsedNanos), 149 systemUptimeNanos = 150 max( 151 cujEntry.endTimestamp.systemUptimeNanos, 152 interpolatedEndTimestamp.systemUptimeNanos, 153 ), 154 unixNanos = max(cujEntry.endTimestamp.unixNanos, interpolatedEndTimestamp.unixNanos), 155 ) 156 } 157 } 158