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.flicker
18 
19 import android.tools.Scenario
20 import android.tools.io.Reader
21 import android.tools.traces.TRACE_CONFIG_REQUIRE_CHANGES
22 import android.tools.traces.io.ResultReaderWithLru
23 import android.tools.traces.io.ResultWriter
24 import android.tools.traces.monitors.PerfettoTraceMonitor
25 import android.tools.traces.monitors.ScreenRecorder
26 import android.tools.traces.monitors.TraceMonitor
27 import android.tools.traces.monitors.events.EventLogMonitor
28 import android.tools.traces.monitors.view.ViewTraceMonitor
29 import android.tools.traces.monitors.wm.LegacyShellTransitionTraceMonitor
30 import android.tools.traces.monitors.wm.LegacyWmTransitionTraceMonitor
31 import android.tools.traces.monitors.wm.WindowManagerTraceMonitor
32 import android.tools.traces.surfaceflinger.LayersTrace
33 import android.tools.traces.wm.TransitionChange
34 import android.tools.traces.wm.WindowManagerTrace
35 import androidx.test.platform.app.InstrumentationRegistry
36 import java.io.File
37 import kotlin.io.path.createTempDirectory
38 
39 object Utils {
40     // Order matters since this is used to start traces in the order the monitors are defined here
41     // and stop them in reverse order.
42     val ALL_MONITORS: List<TraceMonitor> =
43         mutableListOf(
44                 ScreenRecorder(InstrumentationRegistry.getInstrumentation().targetContext),
45                 WindowManagerTraceMonitor(),
46             )
47             .apply {
48                 val perfettoMonitorBuilder = PerfettoTraceMonitor.newBuilder()
49                 perfettoMonitorBuilder.enableLayersTrace().enableTransactionsTrace()
50 
51                 if (android.tracing.Flags.perfettoViewCaptureTracing()) {
52                     perfettoMonitorBuilder.enableViewCaptureTrace()
53                 } else {
54                     this.add(ViewTraceMonitor())
55                 }
56 
57                 if (android.tracing.Flags.perfettoTransitionTracing()) {
58                     perfettoMonitorBuilder.enableTransitionsTrace()
59                 } else {
60                     this.add(LegacyWmTransitionTraceMonitor())
61                     this.add(LegacyShellTransitionTraceMonitor())
62                 }
63 
64                 if (android.tracing.Flags.perfettoProtologTracing()) {
65                     perfettoMonitorBuilder.enableProtoLog()
66                 }
67 
68                 if (android.tracing.Flags.perfettoIme()) {
69                     perfettoMonitorBuilder.enableImeTrace()
70                 }
71 
72                 this.add(perfettoMonitorBuilder.build())
73             }
74             .apply {
75                 // Start this trace last, since we get our CUJ tags from it and don't want to
76                 // extract CUJ slices of the trace that are missing data from the other traces.
77                 this.add(EventLogMonitor())
78             }
79 
80     fun captureTrace(
81         scenario: Scenario,
82         outputDir: File = createTempDirectory().toFile(),
83         monitors: List<TraceMonitor> = ALL_MONITORS,
84         actions: (writer: ResultWriter) -> Unit
85     ): Reader {
86         val writer = ResultWriter().forScenario(scenario).withOutputDir(outputDir).setRunComplete()
87         monitors.fold({ actions.invoke(writer) }) { action, monitor ->
88             { monitor.withTracing(writer) { action() } }
89         }()
90         val result = writer.write()
91 
92         return ResultReaderWithLru(result, TRACE_CONFIG_REQUIRE_CHANGES)
93     }
94 }
95 
Stringnull96 fun String.camelToSnakeCase(): String {
97     return this.fold(StringBuilder()) { acc, c ->
98             acc.let {
99                 val lowerC = c.lowercase()
100                 acc.append(if (acc.isNotEmpty() && c.isUpperCase()) "_$lowerC" else lowerC)
101             }
102         }
103         .toString()
104 }
105 
isAppTransitionChangenull106 fun isAppTransitionChange(
107     transitionChange: TransitionChange,
108     layersTrace: LayersTrace?,
109     wmTrace: WindowManagerTrace?
110 ): Boolean {
111     require(layersTrace != null || wmTrace != null) {
112         "Requires at least one of wm of layers trace to not be null"
113     }
114 
115     val layerDescriptors =
116         layersTrace?.let {
117             it.getLayerDescriptorById(transitionChange.layerId)
118                 ?: error("Failed to find layer with id ${transitionChange.layerId}")
119         }
120     val windowDescriptor =
121         wmTrace?.let {
122             it.getWindowDescriptorById(transitionChange.windowId)
123                 ?: error("Failed to find layer with id ${transitionChange.windowId}")
124         }
125     return (layerDescriptors?.isAppLayer ?: true) && (windowDescriptor?.isAppWindow ?: true)
126 }
127