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