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.TransitionChange
23 import android.tools.traces.wm.TransitionType
24 import android.tools.traces.wm.TransitionsTrace
25 import android.tools.traces.wm.WmTransitionData
26 import com.android.server.wm.shell.nano.Transition
27 import com.android.server.wm.shell.nano.TransitionTraceProto
28 
29 /** Parser for [TransitionsTrace] objects */
30 class WmTransitionTraceParser :
31     AbstractTraceParser<
32         TransitionTraceProto,
33         Transition,
34         android.tools.traces.wm.Transition,
35         TransitionsTrace,
36     >() {
37     override val traceName: String = "Transition trace (WM)"
38 
createTracenull39     override fun createTrace(
40         entries: Collection<android.tools.traces.wm.Transition>
41     ): TransitionsTrace {
42         return TransitionsTrace(entries)
43     }
44 
doDecodeByteArraynull45     override fun doDecodeByteArray(bytes: ByteArray): TransitionTraceProto =
46         TransitionTraceProto.parseFrom(bytes)
47 
48     override fun shouldParseEntry(entry: com.android.server.wm.shell.nano.Transition): Boolean {
49         return true
50     }
51 
getEntriesnull52     override fun getEntries(
53         input: TransitionTraceProto
54     ): Collection<com.android.server.wm.shell.nano.Transition> = input.transitions.toList()
55 
56     override fun getTimestamp(entry: com.android.server.wm.shell.nano.Transition): Timestamp {
57         requireValidTimestamp(entry)
58 
59         if (entry.createTimeNs != 0L) {
60             return Timestamps.from(elapsedNanos = entry.createTimeNs)
61         }
62         if (entry.sendTimeNs != 0L) {
63             return Timestamps.from(elapsedNanos = entry.sendTimeNs)
64         }
65         if (entry.abortTimeNs != 0L) {
66             return Timestamps.from(elapsedNanos = entry.abortTimeNs)
67         }
68         if (entry.finishTimeNs != 0L) {
69             return Timestamps.from(elapsedNanos = entry.finishTimeNs)
70         }
71         if (entry.startingWindowRemoveTimeNs != 0L) {
72             return Timestamps.from(elapsedNanos = entry.startingWindowRemoveTimeNs)
73         }
74 
75         error("No valid timestamp available in entry")
76     }
77 
onBeforeParsenull78     override fun onBeforeParse(input: TransitionTraceProto) {}
79 
doParsenull80     override fun doParse(
81         input: TransitionTraceProto,
82         from: Timestamp,
83         to: Timestamp,
84         addInitialEntry: Boolean,
85     ): TransitionsTrace {
86         val uncompressedTransitionsTrace = super.doParse(input, from, to, addInitialEntry)
87         return uncompressedTransitionsTrace.asCompressed()
88     }
89 
doParseEntrynull90     override fun doParseEntry(
91         entry: com.android.server.wm.shell.nano.Transition
92     ): android.tools.traces.wm.Transition {
93         require(entry.id != 0) { "Entry needs a non null id" }
94         requireValidTimestamp(entry)
95 
96         val changes =
97             if (entry.targets.isEmpty()) {
98                 null
99             } else {
100                 entry.targets.map {
101                     TransitionChange(TransitionType.fromInt(it.mode), it.layerId, it.windowId)
102                 }
103             }
104 
105         return android.tools.traces.wm.Transition(
106             entry.id,
107             wmData =
108                 WmTransitionData(
109                     createTime = entry.createTimeNs.toTimestamp(),
110                     sendTime = entry.sendTimeNs.toTimestamp(),
111                     abortTime = entry.abortTimeNs.toTimestamp(),
112                     finishTime = entry.finishTimeNs.toTimestamp(),
113                     startingWindowRemoveTime = entry.startingWindowRemoveTimeNs.toTimestamp(),
114                     startTransactionId = entry.startTransactionId.toTransactionId(),
115                     finishTransactionId = entry.finishTransactionId.toTransactionId(),
116                     type =
117                         if (entry.type == 0) {
118                             null
119                         } else {
120                             TransitionType.fromInt(entry.type)
121                         },
122                     changes = changes,
123                 ),
124         )
125     }
126 
Longnull127     private fun Long.toTimestamp() =
128         if (this == 0L) {
129             null
130         } else {
131             Timestamps.from(elapsedNanos = this)
132         }
133 
toTransactionIdnull134     private fun Long.toTransactionId() =
135         if (this == 0L) {
136             null
137         } else {
138             this
139         }
140 
141     companion object {
requireValidTimestampnull142         private fun requireValidTimestamp(entry: com.android.server.wm.shell.nano.Transition) {
143             require(
144                 entry.createTimeNs != 0L ||
145                     entry.sendTimeNs != 0L ||
146                     entry.abortTimeNs != 0L ||
147                     entry.finishTimeNs != 0L ||
148                     entry.startingWindowRemoveTimeNs != 0L
149             ) {
150                 "Requires at least one non-null timestamp"
151             }
152         }
153     }
154 }
155