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.traces.parsers.wm
18 
19 import android.tools.Timestamp
20 import android.tools.Timestamps
21 import android.tools.parsers.AbstractTraceParser
22 import android.tools.traces.wm.ShellTransitionData
23 import android.tools.traces.wm.TransitionsTrace
24 import com.android.wm.shell.nano.Transition
25 import com.android.wm.shell.nano.WmShellTransitionTraceProto
26 
27 /** Parser for [TransitionsTrace] objects */
28 class ShellTransitionTraceParser :
29     AbstractTraceParser<
30         WmShellTransitionTraceProto,
31         Transition,
32         android.tools.traces.wm.Transition,
33         TransitionsTrace
34     >() {
35     override val traceName: String = "Transition trace (shell)"
36 
createTracenull37     override fun createTrace(
38         entries: Collection<android.tools.traces.wm.Transition>
39     ): TransitionsTrace {
40         return TransitionsTrace(entries)
41     }
42 
doDecodeByteArraynull43     override fun doDecodeByteArray(bytes: ByteArray): WmShellTransitionTraceProto =
44         WmShellTransitionTraceProto.parseFrom(bytes)
45 
46     override fun shouldParseEntry(entry: com.android.wm.shell.nano.Transition): Boolean {
47         return true
48     }
49 
getEntriesnull50     override fun getEntries(
51         input: WmShellTransitionTraceProto
52     ): Collection<com.android.wm.shell.nano.Transition> = input.transitions.toList()
53 
54     override fun getTimestamp(entry: com.android.wm.shell.nano.Transition): Timestamp {
55         requireValidTimestamp(entry)
56 
57         if (entry.dispatchTimeNs != 0L) {
58             return Timestamps.from(elapsedNanos = entry.dispatchTimeNs)
59         }
60         if (entry.mergeRequestTimeNs != 0L) {
61             return Timestamps.from(elapsedNanos = entry.mergeRequestTimeNs)
62         }
63         if (entry.mergeTimeNs != 0L) {
64             return Timestamps.from(elapsedNanos = entry.mergeTimeNs)
65         }
66         if (entry.abortTimeNs != 0L) {
67             return Timestamps.from(elapsedNanos = entry.abortTimeNs)
68         }
69 
70         error("No valid timestamp for entry")
71     }
72 
73     private val handlerMapping = mutableMapOf<Int, String>()
74 
onBeforeParsenull75     override fun onBeforeParse(input: WmShellTransitionTraceProto) {
76         handlerMapping.clear()
77         for (mapping in input.handlerMappings) {
78             handlerMapping[mapping.id] = mapping.name
79         }
80     }
81 
doParsenull82     override fun doParse(
83         input: WmShellTransitionTraceProto,
84         from: Timestamp,
85         to: Timestamp,
86         addInitialEntry: Boolean
87     ): TransitionsTrace {
88         val uncompressedTransitionsTrace = super.doParse(input, from, to, addInitialEntry)
89         return uncompressedTransitionsTrace.asCompressed()
90     }
91 
doParseEntrynull92     override fun doParseEntry(
93         entry: com.android.wm.shell.nano.Transition
94     ): android.tools.traces.wm.Transition {
95         require(entry.id != 0) { "Entry needs a non null id" }
96         requireValidTimestamp(entry)
97 
98         return android.tools.traces.wm.Transition(
99             entry.id,
100             shellData =
101                 ShellTransitionData(
102                     dispatchTime =
103                         if (entry.dispatchTimeNs == 0L) {
104                             null
105                         } else {
106                             Timestamps.from(elapsedNanos = entry.dispatchTimeNs)
107                         },
108                     mergeRequestTime =
109                         if (entry.mergeRequestTimeNs == 0L) {
110                             null
111                         } else {
112                             Timestamps.from(elapsedNanos = entry.mergeRequestTimeNs)
113                         },
114                     mergeTime =
115                         if (entry.mergeTimeNs == 0L) {
116                             null
117                         } else {
118                             Timestamps.from(elapsedNanos = entry.mergeTimeNs)
119                         },
120                     abortTime =
121                         if (entry.abortTimeNs == 0L) {
122                             null
123                         } else {
124                             Timestamps.from(elapsedNanos = entry.abortTimeNs)
125                         },
126                     handler = handlerMapping[entry.handler],
127                     mergeTarget = if (entry.mergeTarget == 0) null else entry.mergeTarget
128                 )
129         )
130     }
131 
132     companion object {
requireValidTimestampnull133         private fun requireValidTimestamp(entry: com.android.wm.shell.nano.Transition) {
134             require(
135                 entry.dispatchTimeNs != 0L ||
136                     entry.mergeRequestTimeNs != 0L ||
137                     entry.mergeTimeNs != 0L ||
138                     entry.abortTimeNs != 0L
139             ) {
140                 "Requires at least one non-null timestamp"
141             }
142         }
143     }
144 }
145