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