xref: /aosp_15_r20/platform_testing/libraries/flicker/src/android/tools/flicker/extractors/Utils.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
2  * 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.surfaceflinger.Display
23 import android.tools.traces.surfaceflinger.LayerTraceEntry
24 import android.tools.traces.wm.Transition
25 import kotlin.math.abs
26 
27 object Utils {
interpolateStartTimestampFromTransitionnull28     fun interpolateStartTimestampFromTransition(transition: Transition, reader: Reader): Timestamp {
29         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
30         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
31         val transactionsTrace =
32             reader.readTransactionsTrace() ?: error("Missing transactions trace")
33 
34         val lastWmEntryBeforeTransitionCreated = wmTrace.getEntryAt(transition.createTime)
35         val elapsedNanos = lastWmEntryBeforeTransitionCreated.timestamp.elapsedNanos
36         val unixNanos = lastWmEntryBeforeTransitionCreated.timestamp.unixNanos
37 
38         val startTransactionAppliedTimestamp =
39             transition.getStartTransaction(transactionsTrace)?.let {
40                 layersTrace.getEntryForTransaction(it).timestamp
41             }
42 
43         // If we don't have a startTransactionAppliedTimestamp it's likely because the start
44         // transaction was merged into another transaction so we can't match the id, so we need to
45         // fallback on the send time reported on the WM side.
46         val systemUptimeNanos =
47             startTransactionAppliedTimestamp?.systemUptimeNanos
48                 ?: transition.createTime.systemUptimeNanos
49 
50         // This fallback doesn't really work because then systemUptimeNanos is 0...
51         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
52     }
53 
interpolateFinishTimestampFromTransitionnull54     fun interpolateFinishTimestampFromTransition(
55         transition: Transition,
56         reader: Reader,
57         debugString: String? = null,
58     ): Timestamp {
59         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
60         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
61         val transactionsTrace =
62             reader.readTransactionsTrace() ?: error("Missing transactions trace")
63 
64         // There is a delay between when we flag that transition as finished with the CUJ tags
65         // and when it is actually finished on the SF side. We try and account for that by
66         // checking when the finish transaction is actually applied.
67         // TODO: Figure out how to get the vSyncId that the Jank tracker actually gets to avoid
68         //       relying on the transition and have a common end point.
69         val finishTransactionAppliedTimestamp =
70             transition.getFinishTransaction(transactionsTrace)?.let {
71                 layersTrace.getEntryForTransaction(it).timestamp
72             }
73 
74         val elapsedNanos: Long
75         val systemUptimeNanos: Long
76         val unixNanos: Long
77         val sfEntryAtTransitionFinished: LayerTraceEntry
78         if (finishTransactionAppliedTimestamp == null) {
79             // If we don't have a finishTransactionAppliedTimestamp it's likely because the finish
80             // transaction was merged into another transaction so we can't match the id, so we need
81             // to fallback on the finish time reported on the WM side.
82             val wmEntryAtTransitionFinished =
83                 wmTrace.entries.firstOrNull { it.timestamp >= transition.finishTime }
84 
85             elapsedNanos =
86                 wmEntryAtTransitionFinished?.timestamp?.elapsedNanos
87                     ?: transition.finishTime.elapsedNanos
88 
89             unixNanos =
90                 if (wmEntryAtTransitionFinished != null) {
91                     wmEntryAtTransitionFinished.timestamp.unixNanos
92                 } else {
93                     require(wmTrace.entries.isNotEmpty()) { "WM trace should not be empty!" }
94                     val closestWmEntry =
95                         wmTrace.entries.minByOrNull {
96                             abs(it.timestamp.elapsedNanos - transition.finishTime.elapsedNanos)
97                         } ?: error("WM entry was unexpectedly empty!")
98                     val offset =
99                         closestWmEntry.timestamp.unixNanos - closestWmEntry.timestamp.elapsedNanos
100                     transition.finishTime.elapsedNanos + offset
101                 }
102 
103             sfEntryAtTransitionFinished =
104                 layersTrace.entries.firstOrNull { it.timestamp.unixNanos >= unixNanos }
105                     ?: error("No SF entry for finish timestamp")
106             systemUptimeNanos = sfEntryAtTransitionFinished.timestamp.systemUptimeNanos
107         } else {
108             elapsedNanos =
109                 (wmTrace.entries.firstOrNull { it.timestamp >= finishTransactionAppliedTimestamp }
110                         ?: wmTrace.entries.last())
111                     .timestamp
112                     .elapsedNanos
113             systemUptimeNanos =
114                 layersTrace
115                     .getEntryAt(finishTransactionAppliedTimestamp)
116                     .timestamp
117                     .systemUptimeNanos
118             unixNanos =
119                 layersTrace.getEntryAt(finishTransactionAppliedTimestamp).timestamp.unixNanos
120         }
121 
122         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
123     }
124 
getFullTimestampAtnull125     fun getFullTimestampAt(layersTraceEntry: LayerTraceEntry, reader: Reader): Timestamp {
126         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
127 
128         val elapsedNanos =
129             (wmTrace.entries.firstOrNull { it.timestamp >= layersTraceEntry.timestamp }
130                     ?: wmTrace.entries.last())
131                 .timestamp
132                 .elapsedNanos
133         val systemUptimeNanos = layersTraceEntry.timestamp.systemUptimeNanos
134         val unixNanos = layersTraceEntry.timestamp.unixNanos
135 
136         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
137     }
138 
getOnDisplayFornull139     fun getOnDisplayFor(layerTraceEntry: LayerTraceEntry): Display {
140         val displays = layerTraceEntry.displays.filter { !it.isVirtual }
141         require(displays.isNotEmpty()) { "Failed to get a display for provided entry" }
142         val onDisplays = displays.filter { it.isOn }
143         require(onDisplays.isNotEmpty()) { "No on displays found for entry" }
144         require(onDisplays.size == 1) { "More than one on display found!" }
145         return onDisplays.first()
146     }
147 }
148