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     ): Timestamp {
58         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
59         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
60         val transactionsTrace =
61             reader.readTransactionsTrace() ?: error("Missing transactions trace")
62 
63         // There is a delay between when we flag that transition as finished with the CUJ tags
64         // and when it is actually finished on the SF side. We try and account for that by
65         // checking when the finish transaction is actually applied.
66         // TODO: Figure out how to get the vSyncId that the Jank tracker actually gets to avoid
67         //       relying on the transition and have a common end point.
68         val finishTransactionAppliedTimestamp =
69             transition.getFinishTransaction(transactionsTrace)?.let {
70                 layersTrace.getEntryForTransaction(it).timestamp
71             }
72 
73         val elapsedNanos: Long
74         val systemUptimeNanos: Long
75         val unixNanos: Long
76         val sfEntryAtTransitionFinished: LayerTraceEntry
77         if (finishTransactionAppliedTimestamp == null) {
78             // If we don't have a finishTransactionAppliedTimestamp it's likely because the finish
79             // transaction was merged into another transaction so we can't match the id, so we need
80             // to fallback on the finish time reported on the WM side.
81             val wmEntryAtTransitionFinished =
82                 wmTrace.entries.firstOrNull { it.timestamp >= transition.finishTime }
83 
84             elapsedNanos =
85                 wmEntryAtTransitionFinished?.timestamp?.elapsedNanos
86                     ?: transition.finishTime.elapsedNanos
87 
88             unixNanos =
89                 if (wmEntryAtTransitionFinished != null) {
90                     wmEntryAtTransitionFinished.timestamp.unixNanos
91                 } else {
92                     require(wmTrace.entries.isNotEmpty()) { "WM trace should not be empty!" }
93                     val closestWmEntry =
94                         wmTrace.entries.minByOrNull {
95                             abs(it.timestamp.elapsedNanos - transition.finishTime.elapsedNanos)
96                         }
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                         ?: error(
111                             "No WM trace entry with timestamp greater than or equal to the " +
112                                 "layers trace the finish transaction was applied in"
113                         ))
114                     .timestamp
115                     .elapsedNanos
116             systemUptimeNanos =
117                 layersTrace
118                     .getEntryAt(finishTransactionAppliedTimestamp)
119                     .timestamp
120                     .systemUptimeNanos
121             unixNanos =
122                 layersTrace.getEntryAt(finishTransactionAppliedTimestamp).timestamp.unixNanos
123         }
124 
125         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
126     }
127 
getFullTimestampAtnull128     fun getFullTimestampAt(layersTraceEntry: LayerTraceEntry, reader: Reader): Timestamp {
129         val wmTrace = reader.readWmTrace() ?: error("Missing WM trace")
130 
131         val elapsedNanos =
132             (wmTrace.entries.firstOrNull { it.timestamp >= layersTraceEntry.timestamp }
133                     ?: wmTrace.entries.last())
134                 .timestamp
135                 .elapsedNanos
136         val systemUptimeNanos = layersTraceEntry.timestamp.systemUptimeNanos
137         val unixNanos = layersTraceEntry.timestamp.unixNanos
138 
139         return Timestamps.from(elapsedNanos, systemUptimeNanos, unixNanos)
140     }
141 
getOnDisplayFornull142     fun getOnDisplayFor(layerTraceEntry: LayerTraceEntry): Display {
143         val displays = layerTraceEntry.displays.filter { !it.isVirtual }
144         require(displays.isNotEmpty()) { "Failed to get a display for provided entry" }
145         val onDisplays = displays.filter { it.isOn }
146         require(onDisplays.isNotEmpty()) { "No on displays found for entry" }
147         require(onDisplays.size == 1) { "More than one on display found!" }
148         return onDisplays.first()
149     }
150 }
151