1 /*
<lambda>null2  * Copyright (C) 2024 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.perfetto
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.Transition
24 import android.tools.traces.wm.TransitionChange
25 import android.tools.traces.wm.TransitionType
26 import android.tools.traces.wm.TransitionsTrace
27 import android.tools.traces.wm.WmTransitionData
28 
29 class TransitionsTraceParser :
30     AbstractTraceParser<TraceProcessorSession, Transition, Transition, TransitionsTrace>() {
31 
32     override val traceName = "Transitions Trace"
33 
34     override fun createTrace(entries: Collection<Transition>): TransitionsTrace {
35         return TransitionsTrace(entries)
36     }
37 
38     override fun doDecodeByteArray(bytes: ByteArray): TraceProcessorSession {
39         error("This parser can only read from perfetto trace processor")
40     }
41 
42     override fun shouldParseEntry(entry: Transition) = true
43 
44     override fun getEntries(input: TraceProcessorSession): List<Transition> {
45         val transitions = ArrayList<Transition>()
46 
47         val handlerMapping = mutableMapOf<Int, String>()
48         input.query(getSqlQueryHandlerMappings()) {
49             it.forEach { mapping ->
50                 handlerMapping[(mapping["handler_id"] as Long).toInt()] =
51                     mapping["handler_name"] as String
52             }
53         }
54 
55         input.query(getSqlQueryTransitions()) { transitionsRows ->
56             val transitionRowsGrouped =
57                 transitionsRows.groupBy {
58                     it["transition_entry_id"]
59                         ?: error("transition_entry_id column should not be null")
60                 }
61 
62             transitionRowsGrouped.values.forEach { transitionRows ->
63                 transitions.add(buildTransition(transitionRows, handlerMapping))
64             }
65         }
66 
67         return transitions
68     }
69 
70     override fun getTimestamp(entry: Transition): Timestamp = entry.timestamp
71 
72     override fun onBeforeParse(input: TraceProcessorSession) {}
73 
74     override fun doParseEntry(entry: Transition) = entry
75 
76     companion object {
77         private fun getSqlQueryHandlerMappings() =
78             "SELECT handler_id, handler_name FROM window_manager_shell_transition_handlers;"
79 
80         private fun getSqlQueryTransitions() =
81             """
82                SELECT transitions.id AS transition_entry_id, args.key, args.display_value AS value, args.value_type
83                FROM window_manager_shell_transitions AS transitions
84                INNER JOIN args ON transitions.arg_set_id = args.arg_set_id;
85             """
86                 .trimIndent()
87 
88         private fun buildTransition(
89             transitionRows: List<Row>,
90             handlerMapping: Map<Int, String>
91         ): Transition {
92             val args = Args.build(transitionRows)
93             return Transition(
94                 id = args.getChild("id")?.getInt() ?: error("Missing transition id"),
95                 wmData =
96                     WmTransitionData(
97                         createTime = args.getChild("create_time_ns")?.getLong()?.toTimestamp(),
98                         sendTime = args.getChild("send_time_ns")?.getLong()?.toTimestamp(),
99                         abortTime = args.getChild("wm_abort_time_ns")?.getLong()?.toTimestamp(),
100                         finishTime = args.getChild("finish_time_ns")?.getLong()?.toTimestamp(),
101                         startingWindowRemoveTime =
102                             args
103                                 .getChild("starting_window_remove_time_ns")
104                                 ?.getLong()
105                                 ?.toTimestamp(),
106                         startTransactionId = args.getChild("start_transaction_id")?.getLong(),
107                         finishTransactionId = args.getChild("finish_transaction_id")?.getLong(),
108                         type = args.getChild("type")?.getInt()?.toTransitionType(),
109                         changes =
110                             args
111                                 .getChildren("targets")
112                                 ?.map {
113                                     TransitionChange(
114                                         it.getChild("mode")?.getInt()?.toTransitionType()
115                                             ?: error("Missing mode (${it.getChild("mode")})"),
116                                         it.getChild("layer_id")?.getInt()
117                                             ?: error("Missing layer id ${it.getChild("layer_id")}"),
118                                         it.getChild("window_id")?.getInt()
119                                             ?: error(
120                                                 "Missing window id ${it.getChild("window_id")}"
121                                             )
122                                     )
123                                 }
124                                 ?.ifEmpty { null },
125                     ),
126                 shellData =
127                     ShellTransitionData(
128                         dispatchTime = args.getChild("dispatch_time_ns")?.getLong()?.toTimestamp(),
129                         mergeRequestTime =
130                             args.getChild("merge_request_time_ns")?.getLong()?.toTimestamp(),
131                         mergeTime = args.getChild("merge_time_ns")?.getLong()?.toTimestamp(),
132                         abortTime = args.getChild("shell_abort_time_ns")?.getLong()?.toTimestamp(),
133                         handler = args.getChild("handler")?.getInt()?.let { handlerMapping[it] },
134                         mergeTarget = args.getChild("merge_target")?.getInt(),
135                     )
136             )
137         }
138 
139         private fun Long.toTimestamp() =
140             if (this == 0L) {
141                 null
142             } else {
143                 Timestamps.from(elapsedNanos = this)
144             }
145 
146         private fun Int.toTransitionType() = TransitionType.fromInt(this)
147     }
148 }
149