• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2020 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 com.android.server.wm.traces.parser.windowmanager
18 
19 import android.app.nano.WindowConfigurationProto
20 import android.content.nano.ConfigurationProto
21 import android.graphics.nano.RectProto
22 import android.util.Log
23 import android.view.nano.ViewProtoEnums
24 import android.view.nano.WindowLayoutParamsProto
25 import com.android.server.wm.nano.ActivityRecordProto
26 import com.android.server.wm.nano.AppTransitionProto
27 import com.android.server.wm.nano.ConfigurationContainerProto
28 import com.android.server.wm.nano.DisplayAreaProto
29 import com.android.server.wm.nano.DisplayContentProto
30 import com.android.server.wm.nano.KeyguardControllerProto
31 import com.android.server.wm.nano.RootWindowContainerProto
32 import com.android.server.wm.nano.TaskFragmentProto
33 import com.android.server.wm.nano.TaskProto
34 import com.android.server.wm.nano.WindowContainerChildProto
35 import com.android.server.wm.nano.WindowContainerProto
36 import com.android.server.wm.nano.WindowManagerPolicyProto
37 import com.android.server.wm.nano.WindowManagerServiceDumpProto
38 import com.android.server.wm.nano.WindowManagerTraceFileProto
39 import com.android.server.wm.nano.WindowStateProto
40 import com.android.server.wm.nano.WindowTokenProto
41 import com.android.server.wm.traces.common.Rect
42 import com.android.server.wm.traces.common.Size
43 import com.android.server.wm.traces.common.windowmanager.WindowManagerState
44 import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace
45 import com.android.server.wm.traces.common.windowmanager.windows.Activity
46 import com.android.server.wm.traces.common.windowmanager.windows.Configuration
47 import com.android.server.wm.traces.common.windowmanager.windows.ConfigurationContainer
48 import com.android.server.wm.traces.common.windowmanager.windows.DisplayArea
49 import com.android.server.wm.traces.common.windowmanager.windows.DisplayContent
50 import com.android.server.wm.traces.common.windowmanager.windows.KeyguardControllerState
51 import com.android.server.wm.traces.common.windowmanager.windows.RootWindowContainer
52 import com.android.server.wm.traces.common.windowmanager.windows.Task
53 import com.android.server.wm.traces.common.windowmanager.windows.TaskFragment
54 import com.android.server.wm.traces.common.windowmanager.windows.WindowConfiguration
55 import com.android.server.wm.traces.common.windowmanager.windows.WindowContainer
56 import com.android.server.wm.traces.common.windowmanager.windows.WindowLayoutParams
57 import com.android.server.wm.traces.common.windowmanager.windows.WindowManagerPolicy
58 import com.android.server.wm.traces.common.windowmanager.windows.WindowState
59 import com.android.server.wm.traces.common.windowmanager.windows.WindowToken
60 import com.android.server.wm.traces.parser.LOG_TAG
61 import com.google.protobuf.nano.InvalidProtocolBufferNanoException
62 import kotlin.math.max
63 import kotlin.system.measureTimeMillis
64 
65 object WindowManagerTraceParser {
66     private const val TRANSIT_ACTIVITY_OPEN = "TRANSIT_ACTIVITY_OPEN"
67     private const val TRANSIT_ACTIVITY_CLOSE = "TRANSIT_ACTIVITY_CLOSE"
68     private const val TRANSIT_TASK_OPEN = "TRANSIT_TASK_OPEN"
69     private const val TRANSIT_TASK_CLOSE = "TRANSIT_TASK_CLOSE"
70     private const val TRANSIT_WALLPAPER_OPEN = "TRANSIT_WALLPAPER_OPEN"
71     private const val TRANSIT_WALLPAPER_CLOSE = "TRANSIT_WALLPAPER_CLOSE"
72     private const val TRANSIT_WALLPAPER_INTRA_OPEN = "TRANSIT_WALLPAPER_INTRA_OPEN"
73     private const val TRANSIT_WALLPAPER_INTRA_CLOSE = "TRANSIT_WALLPAPER_INTRA_CLOSE"
74     private const val TRANSIT_KEYGUARD_GOING_AWAY = "TRANSIT_KEYGUARD_GOING_AWAY"
75     private const val TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER =
76         "TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER"
77     private const val TRANSIT_KEYGUARD_OCCLUDE = "TRANSIT_KEYGUARD_OCCLUDE"
78     private const val TRANSIT_KEYGUARD_UNOCCLUDE = "TRANSIT_KEYGUARD_UNOCCLUDE"
79     private const val TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = "TRANSIT_TRANSLUCENT_ACTIVITY_OPEN"
80     private const val TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = "TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE"
81 
82     /**
83      * Parses [WindowManagerTraceFileProto] from [data] and uses the proto to generates
84      * a list of trace entries.
85      *
86      * @param data binary proto data
87      */
88     @JvmOverloads
89     @JvmStatic
90     fun parseFromTrace(
91         data: ByteArray?
92     ): WindowManagerTrace {
93         var fileProto: WindowManagerTraceFileProto? = null
94         try {
95             measureTimeMillis {
96                 fileProto = WindowManagerTraceFileProto.parseFrom(data)
97             }.also {
98                 Log.v(LOG_TAG, "Parsing proto (WM Trace): ${it}ms")
99             }
100         } catch (e: InvalidProtocolBufferNanoException) {
101             throw RuntimeException(e)
102         }
103 
104         return fileProto?.let { parseFromTrace(it) }
105                 ?: error("Unable to read trace file")
106     }
107 
108     /**
109      * Uses the proto to generates a list of trace entries.
110      *
111      * @param proto Parsed proto data
112      */
113     @JvmOverloads
114     @JvmStatic
115     fun parseFromTrace(
116         proto: WindowManagerTraceFileProto
117     ): WindowManagerTrace {
118         val entries = mutableListOf<WindowManagerState>()
119         var traceParseTime = 0L
120         for (entryProto in proto.entry) {
121             val entryParseTime = measureTimeMillis {
122                 val entry = newTraceEntry(entryProto.windowManagerService,
123                     entryProto.elapsedRealtimeNanos, entryProto.where)
124                 entries.add(entry)
125             }
126             traceParseTime += entryParseTime
127         }
128 
129         Log.v(LOG_TAG, "Parsing duration (WM Trace): ${traceParseTime}ms " +
130             "(avg ${traceParseTime / max(entries.size, 1)}ms per entry)")
131         return WindowManagerTrace(entries.toTypedArray())
132     }
133 
134     /**
135      * Parses [WindowManagerServiceDumpProto] from [proto] dump and uses the proto to generates
136      * a list of trace entries.
137      *
138      * @param proto Parsed proto data
139      */
140     @JvmStatic
141     fun parseFromDump(proto: WindowManagerServiceDumpProto): WindowManagerTrace {
142         return WindowManagerTrace(
143                 arrayOf(newTraceEntry(proto, timestamp = 0, where = "")))
144     }
145 
146     /**
147      * Parses [WindowManagerServiceDumpProto] from [data] and uses the proto to generates
148      * a list of trace entries.
149      *
150      * @param data binary proto data
151      */
152     @JvmStatic
153     fun parseFromDump(data: ByteArray?): WindowManagerTrace {
154         val fileProto = try {
155             WindowManagerServiceDumpProto.parseFrom(data)
156         } catch (e: InvalidProtocolBufferNanoException) {
157             throw RuntimeException(e)
158         }
159         return parseFromDump(fileProto)
160     }
161 
162     private fun newTraceEntry(
163         proto: WindowManagerServiceDumpProto,
164         timestamp: Long,
165         where: String
166     ): WindowManagerState {
167         return WindowManagerState(
168             where = where,
169             policy = newWindowManagerPolicy(proto.policy),
170             focusedApp = proto.focusedApp,
171             focusedDisplayId = proto.focusedDisplayId,
172             focusedWindow = proto.focusedWindow?.title ?: "",
173             inputMethodWindowAppToken = if (proto.inputMethodWindow != null) {
174                 Integer.toHexString(proto.inputMethodWindow.hashCode)
175             } else {
176                 ""
177             },
178             isHomeRecentsComponent = proto.rootWindowContainer.isHomeRecentsComponent,
179             isDisplayFrozen = proto.displayFrozen,
180             pendingActivities = proto.rootWindowContainer.pendingActivities
181                 .map { it.title }.toTypedArray(),
182             root = newRootWindowContainer(proto.rootWindowContainer),
183             keyguardControllerState = newKeyguardControllerState(
184                 proto.rootWindowContainer.keyguardController),
185             _timestamp = timestamp.toString()
186         )
187     }
188 
189     private fun newWindowManagerPolicy(proto: WindowManagerPolicyProto): WindowManagerPolicy {
190         return WindowManagerPolicy(
191             focusedAppToken = proto.focusedAppToken ?: "",
192             forceStatusBar = proto.forceStatusBar,
193             forceStatusBarFromKeyguard = proto.forceStatusBarFromKeyguard,
194             keyguardDrawComplete = proto.keyguardDrawComplete,
195             keyguardOccluded = proto.keyguardOccluded,
196             keyguardOccludedChanged = proto.keyguardOccludedChanged,
197             keyguardOccludedPending = proto.keyguardOccludedPending,
198             lastSystemUiFlags = proto.lastSystemUiFlags,
199             orientation = proto.orientation,
200             rotation = proto.rotation,
201             rotationMode = proto.rotationMode,
202             screenOnFully = proto.screenOnFully,
203             windowManagerDrawComplete = proto.windowManagerDrawComplete
204         )
205     }
206 
207     private fun newRootWindowContainer(proto: RootWindowContainerProto): RootWindowContainer {
208         return RootWindowContainer(
209             newWindowContainer(
210                 proto.windowContainer,
211                 proto.windowContainer.children
212                     .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree = false) }
213             ) ?: error("Window container should not be null")
214         )
215     }
216 
217     private fun newKeyguardControllerState(
218         proto: KeyguardControllerProto?
219     ): KeyguardControllerState {
220         return KeyguardControllerState(
221             isAodShowing = proto?.aodShowing ?: false,
222             isKeyguardShowing = proto?.keyguardShowing ?: false,
223             keyguardOccludedStates = proto?.keyguardOccludedStates
224                 ?.map { it.displayId to it.keyguardOccluded }
225                 ?.toMap()
226                 ?: emptyMap()
227         )
228     }
229 
230     private fun newWindowContainerChild(
231         proto: WindowContainerChildProto,
232         isActivityInTree: Boolean
233     ): WindowContainer? {
234         return newDisplayContent(proto.displayContent, isActivityInTree)
235             ?: newDisplayArea(proto.displayArea, isActivityInTree)
236             ?: newTask(proto.task, isActivityInTree)
237             ?: newTaskFragment(proto.taskFragment, isActivityInTree)
238             ?: newActivity(proto.activity)
239             ?: newWindowToken(proto.windowToken, isActivityInTree)
240             ?: newWindowState(proto.window, isActivityInTree)
241             ?: newWindowContainer(proto.windowContainer, children = emptyList())
242     }
243 
244     private fun newDisplayContent(
245         proto: DisplayContentProto?,
246         isActivityInTree: Boolean
247     ): DisplayContent? {
248         return if (proto == null) {
249             null
250         } else {
251             DisplayContent(
252                 id = proto.id,
253                 focusedRootTaskId = proto.focusedRootTaskId,
254                 resumedActivity = proto.resumedActivity?.title ?: "",
255                 singleTaskInstance = proto.singleTaskInstance,
256                 defaultPinnedStackBounds = proto.pinnedTaskController?.defaultBounds?.toRect()
257                     ?: Rect.EMPTY,
258                 pinnedStackMovementBounds = proto.pinnedTaskController?.movementBounds?.toRect()
259                     ?: Rect.EMPTY,
260                 displayRect = Rect(0, 0, proto.displayInfo?.logicalWidth
261                     ?: 0, proto.displayInfo?.logicalHeight ?: 0),
262                 appRect = Rect(0, 0, proto.displayInfo?.appWidth ?: 0,
263                     proto.displayInfo?.appHeight
264                         ?: 0),
265                 dpi = proto.dpi,
266                 flags = proto.displayInfo?.flags ?: 0,
267                 stableBounds = proto.displayFrames?.stableBounds?.toRect() ?: Rect.EMPTY,
268                 surfaceSize = proto.surfaceSize,
269                 focusedApp = proto.focusedApp,
270                 lastTransition = appTransitionToString(
271                     proto.appTransition?.lastUsedAppTransition ?: 0),
272                 appTransitionState = appStateToString(
273                     proto.appTransition?.appTransitionState ?: 0),
274                 rotation = proto.displayRotation?.rotation ?: 0,
275                 lastOrientation = proto.displayRotation?.lastOrientation ?: 0,
276                 windowContainer = newWindowContainer(
277                     proto.rootDisplayArea.windowContainer,
278                     proto.rootDisplayArea.windowContainer.children
279                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) },
280                     nameOverride = proto.displayInfo?.name ?: ""
281                 ) ?: error("Window container should not be null")
282             )
283         }
284     }
285 
286     private fun newDisplayArea(proto: DisplayAreaProto?, isActivityInTree: Boolean): DisplayArea? {
287         return if (proto == null) {
288             null
289         } else {
290             DisplayArea(
291                 isTaskDisplayArea = proto.isTaskDisplayArea,
292                 windowContainer = newWindowContainer(
293                     proto.windowContainer,
294                     proto.windowContainer.children
295                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) }
296                 ) ?: error("Window container should not be null")
297             )
298         }
299     }
300 
301     private fun newTask(proto: TaskProto?, isActivityInTree: Boolean): Task? {
302         return if (proto == null) {
303             null
304         } else {
305             Task(
306                 activityType = proto.taskFragment?.activityType ?: proto.activityType,
307                 isFullscreen = proto.fillsParent,
308                 bounds = proto.bounds.toRect(),
309                 taskId = proto.id,
310                 rootTaskId = proto.rootTaskId,
311                 displayId = proto.taskFragment?.displayId ?: proto.displayId,
312                 lastNonFullscreenBounds = proto.lastNonFullscreenBounds?.toRect() ?: Rect.EMPTY,
313                 realActivity = proto.realActivity,
314                 origActivity = proto.origActivity,
315                 resizeMode = proto.resizeMode,
316                 _resumedActivity = proto.resumedActivity?.title ?: "",
317                 animatingBounds = proto.animatingBounds,
318                 surfaceWidth = proto.surfaceWidth,
319                 surfaceHeight = proto.surfaceHeight,
320                 createdByOrganizer = proto.createdByOrganizer,
321                 minWidth = proto.taskFragment?.minWidth ?: proto.minWidth,
322                 minHeight = proto.taskFragment?.minHeight ?: proto.minHeight,
323                 windowContainer = newWindowContainer(
324                     proto.taskFragment?.windowContainer ?: proto.windowContainer,
325                     if (proto.taskFragment != null) {
326                         proto.taskFragment.windowContainer.children
327                                 .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) }
328                     } else {
329                         proto.windowContainer.children
330                                 .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) }
331                     }
332                 ) ?: error("Window container should not be null")
333             )
334         }
335     }
336 
337     private fun newTaskFragment(proto: TaskFragmentProto?, isActivityInTree: Boolean):
338             TaskFragment? {
339         return if (proto == null) {
340             null
341         } else {
342             TaskFragment(
343                 activityType = proto.activityType,
344                 displayId = proto.displayId,
345                 minWidth = proto.minWidth,
346                 minHeight = proto.minHeight,
347                 windowContainer = newWindowContainer(
348                     proto.windowContainer,
349                     proto.windowContainer.children
350                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) }
351                 ) ?: error("Window container should not be null")
352             )
353         }
354     }
355 
356     private fun newActivity(proto: ActivityRecordProto?): Activity? {
357         return if (proto == null) {
358             null
359         } else {
360             Activity(
361                 name = proto.name,
362                 state = proto.state,
363                 visible = proto.visible,
364                 frontOfTask = proto.frontOfTask,
365                 procId = proto.procId,
366                 isTranslucent = proto.translucent,
367                 windowContainer = newWindowContainer(
368                     proto.windowToken.windowContainer,
369                     proto.windowToken.windowContainer.children
370                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree = true) }
371                 ) ?: error("Window container should not be null")
372             )
373         }
374     }
375 
376     private fun newWindowToken(proto: WindowTokenProto?, isActivityInTree: Boolean): WindowToken? {
377         return if (proto == null) {
378             null
379         } else {
380             WindowToken(
381                 newWindowContainer(
382                     proto.windowContainer,
383                     proto.windowContainer.children
384                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) }
385                 ) ?: error("Window container should not be null")
386             )
387         }
388     }
389 
390     private fun newWindowState(proto: WindowStateProto?, isActivityInTree: Boolean): WindowState? {
391         return if (proto == null) {
392             null
393         } else {
394             val identifierName = proto.windowContainer.identifier?.title ?: ""
395             WindowState(
396                 attributes = newWindowLayerParams(proto.attributes),
397                 displayId = proto.displayId,
398                 stackId = proto.stackId,
399                 layer = proto.animator?.surface?.layer ?: 0,
400                 isSurfaceShown = proto.animator?.surface?.shown ?: false,
401                 windowType = when {
402                     identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX) ->
403                         WindowState.WINDOW_TYPE_STARTING
404                     proto.animatingExit -> WindowState.WINDOW_TYPE_EXITING
405                     identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX) ->
406                         WindowState.WINDOW_TYPE_STARTING
407                     else -> 0
408                 },
409                 requestedSize = Size(proto.requestedWidth, proto.requestedHeight),
410                 surfacePosition = proto.surfacePosition?.toRect(),
411                 frame = proto.windowFrames?.frame?.toRect() ?: Rect.EMPTY,
412                 containingFrame = proto.windowFrames?.containingFrame?.toRect() ?: Rect.EMPTY,
413                 parentFrame = proto.windowFrames?.parentFrame?.toRect() ?: Rect.EMPTY,
414                 contentFrame = proto.windowFrames?.contentFrame?.toRect() ?: Rect.EMPTY,
415                 contentInsets = proto.windowFrames?.contentInsets?.toRect() ?: Rect.EMPTY,
416                 surfaceInsets = proto.surfaceInsets?.toRect() ?: Rect.EMPTY,
417                 givenContentInsets = proto.givenContentInsets?.toRect() ?: Rect.EMPTY,
418                 crop = proto.animator?.lastClipRect?.toRect() ?: Rect.EMPTY,
419                 windowContainer = newWindowContainer(
420                     proto.windowContainer,
421                     proto.windowContainer.children
422                         .mapNotNull { p -> newWindowContainerChild(p, isActivityInTree) },
423                     nameOverride = when {
424                         // Existing code depends on the prefix being removed
425                         identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX) ->
426                             identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length)
427                         identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX) ->
428                             identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length)
429                         else -> identifierName
430                     }
431                 ) ?: error("Window container should not be null"),
432                 isAppWindow = isActivityInTree
433             )
434         }
435     }
436 
437     private fun newWindowLayerParams(proto: WindowLayoutParamsProto?): WindowLayoutParams {
438         return WindowLayoutParams(
439             type = proto?.type ?: 0,
440             x = proto?.x ?: 0,
441             y = proto?.y ?: 0,
442             width = proto?.width ?: 0,
443             height = proto?.height ?: 0,
444             horizontalMargin = proto?.horizontalMargin ?: 0f,
445             verticalMargin = proto?.verticalMargin ?: 0f,
446             gravity = proto?.gravity ?: 0,
447             softInputMode = proto?.softInputMode ?: 0,
448             format = proto?.format ?: 0,
449             windowAnimations = proto?.windowAnimations ?: 0,
450             alpha = proto?.alpha ?: 0f,
451             screenBrightness = proto?.screenBrightness ?: 0f,
452             buttonBrightness = proto?.buttonBrightness ?: 0f,
453             rotationAnimation = proto?.rotationAnimation ?: 0,
454             preferredRefreshRate = proto?.preferredRefreshRate ?: 0f,
455             preferredDisplayModeId = proto?.preferredDisplayModeId ?: 0,
456             hasSystemUiListeners = proto?.hasSystemUiListeners ?: false,
457             inputFeatureFlags = proto?.inputFeatureFlags ?: 0,
458             userActivityTimeout = proto?.userActivityTimeout ?: 0,
459             colorMode = proto?.colorMode ?: 0,
460             flags = proto?.flags ?: 0,
461             privateFlags = proto?.privateFlags ?: 0,
462             systemUiVisibilityFlags = proto?.systemUiVisibilityFlags ?: 0,
463             subtreeSystemUiVisibilityFlags = proto?.subtreeSystemUiVisibilityFlags ?: 0,
464             appearance = proto?.appearance ?: 0,
465             behavior = proto?.behavior ?: 0,
466             fitInsetsTypes = proto?.fitInsetsTypes ?: 0,
467             fitInsetsSides = proto?.fitInsetsSides ?: 0,
468             fitIgnoreVisibility = proto?.fitIgnoreVisibility ?: false
469         )
470     }
471 
472     private fun newConfigurationContainer(
473         proto: ConfigurationContainerProto?
474     ): ConfigurationContainer {
475         return ConfigurationContainer(
476             overrideConfiguration = newConfiguration(proto?.overrideConfiguration),
477             fullConfiguration = newConfiguration(proto?.fullConfiguration),
478             mergedOverrideConfiguration = newConfiguration(proto?.mergedOverrideConfiguration)
479         )
480     }
481 
482     private fun newConfiguration(proto: ConfigurationProto?): Configuration? {
483         return if (proto == null) {
484             null
485         } else {
486             Configuration(
487                 windowConfiguration = if (proto.windowConfiguration != null) {
488                     newWindowConfiguration(proto.windowConfiguration)
489                 } else {
490                     null
491                 },
492                 densityDpi = proto.densityDpi,
493                 orientation = proto.orientation,
494                 screenHeightDp = proto.screenHeightDp,
495                 screenWidthDp = proto.screenWidthDp,
496                 smallestScreenWidthDp = proto.smallestScreenWidthDp,
497                 screenLayout = proto.screenLayout,
498                 uiMode = proto.uiMode
499             )
500         }
501     }
502 
503     private fun newWindowConfiguration(proto: WindowConfigurationProto): WindowConfiguration {
504         return WindowConfiguration(
505             _appBounds = proto.appBounds?.toRect(),
506             _bounds = proto.bounds?.toRect(),
507             _maxBounds = proto.maxBounds?.toRect(),
508             windowingMode = proto.windowingMode,
509             activityType = proto.activityType
510         )
511     }
512 
513     private fun newWindowContainer(
514         proto: WindowContainerProto?,
515         children: List<WindowContainer>,
516         nameOverride: String? = null
517     ): WindowContainer? {
518         return if (proto == null) {
519             null
520         } else {
521             WindowContainer(
522                 title = nameOverride ?: proto.identifier?.title ?: "",
523                 token = proto.identifier?.hashCode?.toString(16) ?: "",
524                 orientation = proto.orientation,
525                 _isVisible = proto.visible,
526                 configurationContainer = newConfigurationContainer(
527                     proto.configurationContainer),
528                 layerId = proto.surfaceControl?.layerId ?: 0,
529                 children = children.toTypedArray()
530             )
531         }
532     }
533 
534     private fun appTransitionToString(transition: Int): String {
535         return when (transition) {
536             ViewProtoEnums.TRANSIT_UNSET -> "TRANSIT_UNSET"
537             ViewProtoEnums.TRANSIT_NONE -> "TRANSIT_NONE"
538             ViewProtoEnums.TRANSIT_ACTIVITY_OPEN -> TRANSIT_ACTIVITY_OPEN
539             ViewProtoEnums.TRANSIT_ACTIVITY_CLOSE -> TRANSIT_ACTIVITY_CLOSE
540             ViewProtoEnums.TRANSIT_TASK_OPEN -> TRANSIT_TASK_OPEN
541             ViewProtoEnums.TRANSIT_TASK_CLOSE -> TRANSIT_TASK_CLOSE
542             ViewProtoEnums.TRANSIT_TASK_TO_FRONT -> "TRANSIT_TASK_TO_FRONT"
543             ViewProtoEnums.TRANSIT_TASK_TO_BACK -> "TRANSIT_TASK_TO_BACK"
544             ViewProtoEnums.TRANSIT_WALLPAPER_CLOSE -> TRANSIT_WALLPAPER_CLOSE
545             ViewProtoEnums.TRANSIT_WALLPAPER_OPEN -> TRANSIT_WALLPAPER_OPEN
546             ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_OPEN -> TRANSIT_WALLPAPER_INTRA_OPEN
547             ViewProtoEnums.TRANSIT_WALLPAPER_INTRA_CLOSE -> TRANSIT_WALLPAPER_INTRA_CLOSE
548             ViewProtoEnums.TRANSIT_TASK_OPEN_BEHIND -> "TRANSIT_TASK_OPEN_BEHIND"
549             ViewProtoEnums.TRANSIT_ACTIVITY_RELAUNCH -> "TRANSIT_ACTIVITY_RELAUNCH"
550             ViewProtoEnums.TRANSIT_DOCK_TASK_FROM_RECENTS -> "TRANSIT_DOCK_TASK_FROM_RECENTS"
551             ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY -> TRANSIT_KEYGUARD_GOING_AWAY
552             ViewProtoEnums.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER ->
553                 TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
554             ViewProtoEnums.TRANSIT_KEYGUARD_OCCLUDE -> TRANSIT_KEYGUARD_OCCLUDE
555             ViewProtoEnums.TRANSIT_KEYGUARD_UNOCCLUDE -> TRANSIT_KEYGUARD_UNOCCLUDE
556             ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN ->
557                 TRANSIT_TRANSLUCENT_ACTIVITY_OPEN
558             ViewProtoEnums.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE ->
559                 TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE
560             ViewProtoEnums.TRANSIT_CRASHING_ACTIVITY_CLOSE -> "TRANSIT_CRASHING_ACTIVITY_CLOSE"
561             else -> error("Invalid lastUsedAppTransition")
562         }
563     }
564 
565     private fun appStateToString(appState: Int): String {
566         return when (appState) {
567             AppTransitionProto.APP_STATE_IDLE -> "APP_STATE_IDLE"
568             AppTransitionProto.APP_STATE_READY -> "APP_STATE_READY"
569             AppTransitionProto.APP_STATE_RUNNING -> "APP_STATE_RUNNING"
570             AppTransitionProto.APP_STATE_TIMEOUT -> "APP_STATE_TIMEOUT"
571             else -> error("Invalid AppTransitionState")
572         }
573     }
574 
575     private fun RectProto.toRect() = Rect(this.left, this.top, this.right, this.bottom)
576 }
577