• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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