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