• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.tools.common.flicker.config
18 
19 import android.tools.common.PlatformConsts.SPLIT_SCREEN_TRANSITION_HANDLER
20 import android.tools.common.flicker.extractors.ITransitionMatcher
21 import android.tools.common.flicker.extractors.TransitionsTransform
22 import android.tools.common.traces.component.ComponentNameMatcher
23 import android.tools.common.traces.surfaceflinger.LayersTrace
24 import android.tools.common.traces.wm.Transition
25 import android.tools.common.traces.wm.TransitionType
26 import android.tools.common.traces.wm.WmTransitionData
27 
28 object TransitionFilters {
29     val OPEN_APP_TRANSITION_FILTER: TransitionsTransform = { ts, _, _ ->
30         ts.filter { t ->
31             t.changes.any {
32                 it.transitMode == TransitionType.OPEN || // cold launch
33                 it.transitMode == TransitionType.TO_FRONT // warm launch
34             }
35         }
36     }
37 
38     val CLOSE_APP_TO_LAUNCHER_FILTER: TransitionsTransform = { ts, _, reader ->
39         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
40         val layers =
41             layersTrace.entries.flatMap { it.flattenedLayers.asList() }.distinctBy { it.id }
42         val launcherLayers = layers.filter { ComponentNameMatcher.LAUNCHER.layerMatchesAnyOf(it) }
43 
44         ts.filter { t ->
45             t.changes.any {
46                 it.transitMode == TransitionType.CLOSE || it.transitMode == TransitionType.TO_BACK
47             } &&
48                 t.changes.any { change ->
49                     launcherLayers.any { it.id == change.layerId }
50                     change.transitMode == TransitionType.TO_FRONT
51                 }
52         }
53     }
54 
55     // TODO: Quick switch with split screen support (b/285142231)
56     val QUICK_SWITCH_TRANSITION_FILTER: TransitionsTransform = { ts, _, reader ->
57         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
58 
59         val enterQuickswitchTransitions =
60             ts.filter { t ->
61                 t.changes.size == 3 &&
62                     t.changes.any {
63                         it.transitMode == TransitionType.TO_FRONT &&
64                             isLauncherTopLevelTaskLayer(it.layerId, layersTrace)
65                     } && // LAUNCHER
66                     t.changes.any {
67                         it.transitMode == TransitionType.TO_FRONT &&
68                             isWallpaperTokenLayer(it.layerId, layersTrace)
69                     } && // WALLPAPER
70                     t.changes.any { it.transitMode == TransitionType.TO_BACK } // closing app
71             }
72 
73         val finalTransitions = mutableListOf<Transition>()
74         for (enterQuickswitchTransition in enterQuickswitchTransitions) {
75             val matchingExitQuickswitchTransitions =
76                 ts.filter { t ->
77                     t.changes.size == 2 &&
78                         t.changes.any {
79                             it.transitMode == TransitionType.TO_BACK
80                         } && // closing app
81                         t.changes.any {
82                             it.transitMode == TransitionType.TO_FRONT
83                         } && // opening app
84                         t.mergedInto ==
85                             enterQuickswitchTransition
86                                 .id // transition merged into previous transition
87                 }
88 
89             if (matchingExitQuickswitchTransitions.isEmpty()) {
90                 continue
91             }
92 
93             require(matchingExitQuickswitchTransitions.size == 1) {
94                 "Expected 1 transition to have the exit quickswitch properties but got " +
95                     "${matchingExitQuickswitchTransitions.size}"
96             }
97             finalTransitions.add(
98                 enterQuickswitchTransition.merge(matchingExitQuickswitchTransitions[0])
99             )
100         }
101 
102         finalTransitions
103     }
104 
105     val QUICK_SWITCH_TRANSITION_POST_PROCESSING: TransitionsTransform = { transitions, _, reader ->
106         require(transitions.size == 1) { "Expected 1 transition but got ${transitions.size}" }
107 
108         val transition = transitions[0]
109 
110         require(transition.changes.size == 5)
111         require(transition.changes.count { it.transitMode == TransitionType.TO_BACK } == 2)
112         require(transition.changes.count { it.transitMode == TransitionType.TO_FRONT } == 3)
113 
114         val layersTrace = reader.readLayersTrace() ?: error("Missing layers trace")
115         val wallpaperId =
116             transition.changes
117                 .map { it.layerId }
118                 .firstOrNull { isWallpaperTokenLayer(it, layersTrace) }
119                 ?: error("Missing wallpaper layer in transition")
120         val launcherId =
121             transition.changes
122                 .map { it.layerId }
123                 .firstOrNull { isLauncherTopLevelTaskLayer(it, layersTrace) }
124                 ?: error("Missing launcher layer in transition")
125 
126         val filteredChanges =
127             transition.changes.filter { it.layerId != wallpaperId && it.layerId != launcherId }
128 
129         val closingAppChange = filteredChanges.first { it.transitMode == TransitionType.TO_BACK }
130         val openingAppChange = filteredChanges.first { it.transitMode == TransitionType.TO_FRONT }
131 
132         listOf(
133             Transition(
134                 transition.id,
135                 WmTransitionData(
136                     createTime = transition.wmData.createTime,
137                     sendTime = transition.wmData.sendTime,
138                     abortTime = transition.wmData.abortTime,
139                     finishTime = transition.wmData.finishTime,
140                     startingWindowRemoveTime = transition.wmData.startingWindowRemoveTime,
141                     startTransactionId = transition.wmData.startTransactionId,
142                     finishTransactionId = transition.wmData.finishTransactionId,
143                     type = transition.wmData.type,
144                     changes = arrayOf(closingAppChange, openingAppChange),
145                 ),
146                 transition.shellData
147             )
148         )
149     }
150 
151     private fun isLauncherTopLevelTaskLayer(layerId: Int, layersTrace: LayersTrace): Boolean {
152         return layersTrace.entries.any { entry ->
153             val launcherLayer =
154                 entry.flattenedLayers.firstOrNull { layer ->
155                     ComponentNameMatcher.LAUNCHER.or(ComponentNameMatcher.AOSP_LAUNCHER)
156                         .layerMatchesAnyOf(layer)
157                 }
158                     ?: return@any false
159 
160             var curLayer = launcherLayer
161             while (!curLayer.isTask && curLayer.parent != null) {
162                 curLayer = curLayer.parent ?: error("unreachable")
163             }
164             if (!curLayer.isTask) {
165                 error("Expected a task layer above the launcher layer")
166             }
167 
168             var launcherTopLevelTaskLayer = curLayer
169             // Might have nested task layers
170             while (
171                 launcherTopLevelTaskLayer.parent != null &&
172                     launcherTopLevelTaskLayer.parent!!.isTask
173             ) {
174                 launcherTopLevelTaskLayer = launcherTopLevelTaskLayer.parent ?: error("unreachable")
175             }
176 
177             return@any launcherTopLevelTaskLayer.id == layerId
178         }
179     }
180 
181     private fun isWallpaperTokenLayer(layerId: Int, layersTrace: LayersTrace): Boolean {
182         return layersTrace.entries.any { entry ->
183             entry.flattenedLayers.any { layer ->
184                 layer.id == layerId &&
185                     ComponentNameMatcher.WALLPAPER_WINDOW_TOKEN.layerMatchesAnyOf(layer)
186             }
187         }
188     }
189 
190     val APP_CLOSE_TO_PIP_TRANSITION_FILTER: TransitionsTransform = { ts, _, _ ->
191         ts.filter { it.type == TransitionType.PIP }
192     }
193 
194     val ENTER_SPLIT_SCREEN_MATCHER =
195         object : ITransitionMatcher {
196             override fun findAll(transitions: Collection<Transition>): Collection<Transition> {
197                 return transitions.filter { isSplitscreenEnterTransition(it) }
198             }
199         }
200 
201     val EXIT_SPLIT_SCREEN_FILTER: TransitionsTransform = { ts, _, _ ->
202         ts.filter { isSplitscreenExitTransition(it) }
203     }
204 
205     val RESIZE_SPLIT_SCREEN_FILTER: TransitionsTransform = { ts, _, _ ->
206         ts.filter { isSplitscreenResizeTransition(it) }
207     }
208 
209     fun isSplitscreenEnterTransition(transition: Transition): Boolean {
210         return transition.handler == SPLIT_SCREEN_TRANSITION_HANDLER &&
211             transition.type == TransitionType.TO_FRONT
212     }
213 
214     fun isSplitscreenExitTransition(transition: Transition): Boolean {
215         return transition.type == TransitionType.SPLIT_DISMISS ||
216             transition.type == TransitionType.SPLIT_DISMISS_SNAP
217     }
218 
219     fun isSplitscreenResizeTransition(transition: Transition): Boolean {
220         // This transition doesn't have a special type
221         return transition.type == TransitionType.CHANGE &&
222             transition.changes.size == 2 &&
223             transition.changes.all { change -> change.transitMode == TransitionType.CHANGE }
224     }
225 }
226