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, Transition, android.tools.traces.wm.Transition, TransitionsTrace
33     >() {
34     override val traceName: String = "Transition trace (WM)"
35 
createTracenull36     override fun createTrace(
37         entries: Collection<android.tools.traces.wm.Transition>
38     ): TransitionsTrace {
39         return TransitionsTrace(entries)
40     }
41 
doDecodeByteArraynull42     override fun doDecodeByteArray(bytes: ByteArray): TransitionTraceProto =
43         TransitionTraceProto.parseFrom(bytes)
44 
45     override fun shouldParseEntry(entry: com.android.server.wm.shell.nano.Transition): Boolean {
46         return true
47     }
48 
getEntriesnull49     override fun getEntries(
50         input: TransitionTraceProto
51     ): Collection<com.android.server.wm.shell.nano.Transition> = input.transitions.toList()
52 
53     override fun getTimestamp(entry: com.android.server.wm.shell.nano.Transition): Timestamp {
54         requireValidTimestamp(entry)
55 
56         if (entry.createTimeNs != 0L) {
57             return Timestamps.from(elapsedNanos = entry.createTimeNs)
58         }
59         if (entry.sendTimeNs != 0L) {
60             return Timestamps.from(elapsedNanos = entry.sendTimeNs)
61         }
62         if (entry.abortTimeNs != 0L) {
63             return Timestamps.from(elapsedNanos = entry.abortTimeNs)
64         }
65         if (entry.finishTimeNs != 0L) {
66             return Timestamps.from(elapsedNanos = entry.finishTimeNs)
67         }
68         if (entry.startingWindowRemoveTimeNs != 0L) {
69             return Timestamps.from(elapsedNanos = entry.startingWindowRemoveTimeNs)
70         }
71 
72         error("No valid timestamp available in entry")
73     }
74 
onBeforeParsenull75     override fun onBeforeParse(input: TransitionTraceProto) {}
76 
doParsenull77     override fun doParse(
78         input: TransitionTraceProto,
79         from: Timestamp,
80         to: Timestamp,
81         addInitialEntry: Boolean
82     ): TransitionsTrace {
83         val uncompressedTransitionsTrace = super.doParse(input, from, to, addInitialEntry)
84         return uncompressedTransitionsTrace.asCompressed()
85     }
86 
doParseEntrynull87     override fun doParseEntry(
88         entry: com.android.server.wm.shell.nano.Transition
89     ): android.tools.traces.wm.Transition {
90         require(entry.id != 0) { "Entry needs a non null id" }
91         requireValidTimestamp(entry)
92 
93         val changes =
94             if (entry.targets.isEmpty()) {
95                 null
96             } else {
97                 entry.targets.map {
98                     TransitionChange(
99                         TransitionType.fromInt(it.mode),
100                         it.layerId,
101                         it.windowId,
102                     )
103                 }
104             }
105 
106         return android.tools.traces.wm.Transition(
107             entry.id,
108             wmData =
109                 WmTransitionData(
110                     createTime = entry.createTimeNs.toTimestamp(),
111                     sendTime = entry.sendTimeNs.toTimestamp(),
112                     abortTime = entry.abortTimeNs.toTimestamp(),
113                     finishTime = entry.finishTimeNs.toTimestamp(),
114                     startingWindowRemoveTime = entry.startingWindowRemoveTimeNs.toTimestamp(),
115                     startTransactionId = entry.startTransactionId.toTransactionId(),
116                     finishTransactionId = entry.finishTransactionId.toTransactionId(),
117                     type =
118                         if (entry.type == 0) {
119                             null
120                         } else {
121                             TransitionType.fromInt(entry.type)
122                         },
123                     changes = changes,
124                 ),
125         )
126     }
127 
Longnull128     private fun Long.toTimestamp() =
129         if (this == 0L) {
130             null
131         } else {
132             Timestamps.from(elapsedNanos = this)
133         }
134 
toTransactionIdnull135     private fun Long.toTransactionId() =
136         if (this == 0L) {
137             null
138         } else {
139             this
140         }
141 
142     companion object {
requireValidTimestampnull143         private fun requireValidTimestamp(entry: com.android.server.wm.shell.nano.Transition) {
144             require(
145                 entry.createTimeNs != 0L ||
146                     entry.sendTimeNs != 0L ||
147                     entry.abortTimeNs != 0L ||
148                     entry.finishTimeNs != 0L ||
149                     entry.startingWindowRemoveTimeNs != 0L
150             ) {
151                 "Requires at least one non-null timestamp"
152             }
153         }
154     }
155 }
156