• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.common.traces.wm
18 
19 import android.tools.common.ITraceEntry
20 import android.tools.common.Timestamp
21 import android.tools.common.Timestamps
22 import android.tools.common.traces.surfaceflinger.LayersTrace
23 import android.tools.common.traces.surfaceflinger.Transaction
24 import android.tools.common.traces.surfaceflinger.TransactionsTrace
25 import kotlin.js.JsExport
26 import kotlin.js.JsName
27 
28 @JsExport
29 class Transition(
30     @JsName("id") val id: Int,
31     internal val wmData: WmTransitionData = WmTransitionData(),
32     internal val shellData: ShellTransitionData = ShellTransitionData(),
33 ) : ITraceEntry {
34     init {
35         require(
36             wmData.createTime != null ||
37                 wmData.sendTime != null ||
38                 wmData.abortTime != null ||
39                 wmData.finishTime != null ||
40                 wmData.startingWindowRemoveTime != null ||
41                 shellData.dispatchTime != null ||
42                 shellData.mergeRequestTime != null ||
43                 shellData.mergeTime != null ||
44                 shellData.abortTime != null
<lambda>null45         ) {
46             "Transition requires at least one non-null timestamp"
47         }
48     }
49 
50     override val timestamp =
51         wmData.createTime
52             ?: wmData.sendTime ?: shellData.dispatchTime ?: shellData.mergeRequestTime
53                 ?: shellData.mergeTime ?: shellData.abortTime ?: wmData.finishTime
54                 ?: wmData.abortTime ?: wmData.startingWindowRemoveTime
55                 ?: error("Missing non-null timestamp")
56 
57     @JsName("createTime") val createTime: Timestamp = wmData.createTime ?: Timestamps.min()
58     @JsName("sendTime") val sendTime: Timestamp = wmData.sendTime ?: Timestamps.min()
59     @JsName("abortTime") val abortTime: Timestamp? = wmData.abortTime
60     @JsName("finishTime")
61     val finishTime: Timestamp = wmData.finishTime ?: wmData.abortTime ?: Timestamps.max()
62     @JsName("startingWindowRemoveTime")
63     val startingWindowRemoveTime: Timestamp? = wmData.startingWindowRemoveTime
64 
65     @JsName("dispatchTime") val dispatchTime: Timestamp = shellData.dispatchTime ?: Timestamps.min()
66     @JsName("mergeRequestTime") val mergeRequestTime: Timestamp? = shellData.mergeRequestTime
67     @JsName("mergeTime") val mergeTime: Timestamp? = shellData.mergeTime
68     @JsName("shellAbortTime") val shellAbortTime: Timestamp? = shellData.abortTime
69 
70     @JsName("startTransactionId")
71     val startTransactionId: Long = wmData.startTransactionId?.toLong() ?: -1L
72     @JsName("finishTransactionId")
73     val finishTransactionId: Long = wmData.finishTransactionId?.toLong() ?: -1L
74     @JsName("type") val type: TransitionType = wmData.type ?: TransitionType.UNDEFINED
75     @JsName("changes") val changes: Array<TransitionChange> = wmData.changes ?: emptyArray()
76     @JsName("mergedInto") val mergedInto = shellData.mergedInto
77     @JsName("handler") val handler = shellData.handler
78 
79     @JsName("played") val played: Boolean = wmData.finishTime != null
80     @JsName("aborted")
81     val aborted: Boolean = wmData.abortTime != null || shellData.abortTime != null
82 
83     @JsName("getStartTransaction")
getStartTransactionnull84     fun getStartTransaction(transactionsTrace: TransactionsTrace): Transaction? {
85         val matches =
86             transactionsTrace.allTransactions.filter {
87                 it.id == this.startTransactionId ||
88                     it.mergedTransactionIds.contains(this.startTransactionId)
89             }
90         require(matches.size <= 1) {
91             "Too many transactions matches found for Transaction#${this.startTransactionId}."
92         }
93         return matches.firstOrNull()
94     }
95 
96     @JsName("getFinishTransaction")
getFinishTransactionnull97     fun getFinishTransaction(transactionsTrace: TransactionsTrace): Transaction? {
98         val matches =
99             transactionsTrace.allTransactions.filter {
100                 it.id == this.finishTransactionId ||
101                     it.mergedTransactionIds.contains(this.finishTransactionId)
102             }
103         require(matches.size <= 1) {
104             "Too many transactions matches found for Transaction#${this.finishTransactionId}."
105         }
106         return matches.firstOrNull()
107     }
108 
109     @JsName("isIncomplete")
110     val isIncomplete: Boolean
111         get() = !played || aborted
112 
113     @JsName("merge")
mergenull114     fun merge(transition: Transition): Transition {
115         require(transition.mergedInto == this.id) {
116             "Can't merge transition with mergedInto id ${transition.mergedInto} " +
117                 "into transition with id ${this.id}"
118         }
119 
120         val finishTransition =
121             if (transition.finishTime > this.finishTime) {
122                 transition
123             } else {
124                 this
125             }
126 
127         return Transition(
128             id = this.id,
129             wmData =
130                 WmTransitionData(
131                     createTime = wmData.createTime,
132                     sendTime = wmData.sendTime,
133                     abortTime = wmData.abortTime,
134                     finishTime = finishTransition.wmData.finishTime,
135                     startingWindowRemoveTime = wmData.startingWindowRemoveTime,
136                     startTransactionId = wmData.startTransactionId,
137                     finishTransactionId = finishTransition.wmData.finishTransactionId,
138                     type = wmData.type,
139                     changes =
140                         (wmData.changes ?: emptyArray())
141                             .toMutableList()
142                             .apply { addAll(transition.wmData.changes ?: emptyArray()) }
143                             .toTypedArray()
144                 ),
145             shellData = shellData
146         )
147     }
148 
toStringnull149     override fun toString(): String = Formatter(null, null).format(this)
150 
151     class Formatter(val layersTrace: LayersTrace?, val wmTrace: WindowManagerTrace?) {
152         private val changeFormatter = TransitionChange.Formatter(layersTrace, wmTrace)
153 
154         fun format(transition: Transition): String = buildString {
155             appendLine("Transition#${transition.id}(")
156             appendLine("type=${transition.type},")
157             appendLine("aborted=${transition.aborted},")
158             appendLine("played=${transition.played},")
159             appendLine("createTime=${transition.createTime},")
160             appendLine("sendTime=${transition.sendTime},")
161             appendLine("dispatchTime=${transition.dispatchTime},")
162             appendLine("mergeRequestTime=${transition.mergeRequestTime},")
163             appendLine("mergeTime=${transition.mergeTime},")
164             appendLine("shellAbortTime=${transition.shellAbortTime},")
165             appendLine("finishTime=${transition.finishTime},")
166             appendLine("startingWindowRemoveTime=${transition.startingWindowRemoveTime},")
167             appendLine("startTransactionId=${transition.startTransactionId},")
168             appendLine("finishTransactionId=${transition.finishTransactionId},")
169             appendLine("changes=[")
170             appendLine(
171                 transition.changes
172                     .joinToString(",\n") { changeFormatter.format(it) }
173                     .prependIndent()
174             )
175             appendLine("]")
176             appendLine(")")
177         }
178     }
179 
180     companion object {
181         @JsName("mergePartialTransitions")
mergePartialTransitionsnull182         fun mergePartialTransitions(transition1: Transition, transition2: Transition): Transition {
183             require(transition1.id == transition2.id) {
184                 "Can't merge transitions with mismatching ids"
185             }
186 
187             return Transition(
188                 id = transition1.id,
189                 transition1.wmData.merge(transition2.wmData),
190                 transition1.shellData.merge(transition2.shellData)
191             )
192         }
193     }
194 }
195