1 /*
<lambda>null2 * 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 @file:JvmName("Utils")
18
19 package android.tools.device.traces
20
21 import android.app.UiAutomation
22 import android.os.IBinder
23 import android.os.ParcelFileDescriptor
24 import android.tools.common.Logger
25 import android.tools.common.MILLISECOND_AS_NANOSECONDS
26 import android.tools.common.io.TraceType
27 import android.tools.common.traces.DeviceStateDump
28 import android.tools.common.traces.NullableDeviceStateDump
29 import android.tools.common.traces.surfaceflinger.LayerTraceEntry
30 import android.tools.common.traces.wm.WindowManagerState
31 import android.tools.device.traces.parsers.DeviceDumpParser
32 import androidx.test.platform.app.InstrumentationRegistry
33 import java.text.SimpleDateFormat
34 import java.util.Date
35 import java.util.Locale
36 import java.util.TimeZone
37
38 fun formatRealTimestamp(timestampNs: Long): String {
39 val timestampMs = timestampNs / MILLISECOND_AS_NANOSECONDS
40 val remainderNs = timestampNs % MILLISECOND_AS_NANOSECONDS
41 val date = Date(timestampMs)
42
43 val timeFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH)
44 timeFormatter.timeZone = TimeZone.getTimeZone("UTC")
45
46 return "${timeFormatter.format(date)}${remainderNs.toString().padStart(6, '0')}"
47 }
48
executeShellCommandnull49 fun executeShellCommand(cmd: String): ByteArray {
50 val uiAutomation: UiAutomation = InstrumentationRegistry.getInstrumentation().uiAutomation
51 val fileDescriptor = uiAutomation.executeShellCommand(cmd)
52 ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor).use { inputStream ->
53 return inputStream.readBytes()
54 }
55 }
56
doBinderDumpnull57 private fun doBinderDump(name: String): ByteArray {
58 // create an fd for the binder transaction
59 val pipe = ParcelFileDescriptor.createPipe()
60 val source = pipe[0]
61 val sink = pipe[1]
62
63 // ServiceManager isn't accessible from tests, so use reflection
64 // this should return an IBinder
65 val service =
66 Class.forName("android.os.ServiceManager")
67 .getMethod("getServiceOrThrow", String::class.java)
68 .invoke(null, name) as IBinder?
69
70 // this is equal to ServiceManager::PROTO_ARG
71 val args = arrayOf("--proto")
72 service?.dump(sink.fileDescriptor, args)
73 sink.close()
74
75 // convert the FD into a ByteArray
76 ParcelFileDescriptor.AutoCloseInputStream(source).use { inputStream ->
77 return inputStream.readBytes()
78 }
79 }
80
getCurrentWindowManagerStatenull81 private fun getCurrentWindowManagerState() = doBinderDump("window")
82
83 private fun getCurrentLayersState() = doBinderDump("SurfaceFlinger")
84
85 /**
86 * Gets the current device state dump containing the [WindowManagerState] (optional) and the
87 * [LayerTraceEntry] (optional) in raw (byte) data.
88 *
89 * @param dumpTypes Flags determining which types of traces should be included in the dump
90 */
91 fun getCurrentState(
92 vararg dumpTypes: TraceType = arrayOf(TraceType.SF_DUMP, TraceType.WM_DUMP)
93 ): Pair<ByteArray, ByteArray> {
94 if (dumpTypes.isEmpty()) {
95 throw IllegalArgumentException("No dump specified")
96 }
97
98 val traceTypes = dumpTypes.filter { it.isTrace }
99 if (traceTypes.isNotEmpty()) {
100 throw IllegalArgumentException("Only dump types are supported. Invalid types: $traceTypes")
101 }
102
103 Logger.d(LOG_TAG, "Requesting new device state dump")
104 val wmTraceData =
105 if (dumpTypes.contains(TraceType.WM_DUMP)) {
106 getCurrentWindowManagerState()
107 } else {
108 ByteArray(0)
109 }
110 val layersTraceData =
111 if (dumpTypes.contains(TraceType.SF_DUMP)) {
112 getCurrentLayersState()
113 } else {
114 ByteArray(0)
115 }
116
117 return Pair(wmTraceData, layersTraceData)
118 }
119
120 /**
121 * Gets the current device state dump containing the [WindowManagerState] (optional) and the
122 * [LayerTraceEntry] (optional) parsed
123 *
124 * @param dumpTypes Flags determining which types of traces should be included in the dump
125 * @param clearCacheAfterParsing If the caching used while parsing the proto should be
126 *
127 * ```
128 * cleared or remain in memory
129 * ```
130 */
131 @JvmOverloads
getCurrentStateDumpNullablenull132 fun getCurrentStateDumpNullable(
133 vararg dumpTypes: TraceType = arrayOf(TraceType.SF_DUMP, TraceType.WM_DUMP),
134 clearCacheAfterParsing: Boolean = true
135 ): NullableDeviceStateDump {
136 val currentStateDump = getCurrentState(*dumpTypes)
137 return DeviceDumpParser.fromNullableDump(
138 currentStateDump.first,
139 currentStateDump.second,
140 clearCacheAfterParsing = clearCacheAfterParsing
141 )
142 }
143
144 @JvmOverloads
getCurrentStateDumpnull145 fun getCurrentStateDump(
146 vararg dumpTypes: TraceType = arrayOf(TraceType.SF_DUMP, TraceType.WM_DUMP),
147 clearCacheAfterParsing: Boolean = true
148 ): DeviceStateDump {
149 val currentStateDump = getCurrentState(*dumpTypes)
150 return DeviceDumpParser.fromDump(
151 currentStateDump.first,
152 currentStateDump.second,
153 clearCacheAfterParsing = clearCacheAfterParsing
154 )
155 }
156