1 /* <lambda>null2 * Copyright (C) 2021 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.common.windowmanager 18 19 import com.android.server.wm.traces.common.ITraceEntry 20 import com.android.server.wm.traces.common.prettyTimestamp 21 import com.android.server.wm.traces.common.windowmanager.windows.Activity 22 import com.android.server.wm.traces.common.windowmanager.windows.DisplayContent 23 import com.android.server.wm.traces.common.windowmanager.windows.KeyguardControllerState 24 import com.android.server.wm.traces.common.windowmanager.windows.RootWindowContainer 25 import com.android.server.wm.traces.common.windowmanager.windows.Task 26 import com.android.server.wm.traces.common.windowmanager.windows.TaskFragment 27 import com.android.server.wm.traces.common.windowmanager.windows.WindowContainer 28 import com.android.server.wm.traces.common.windowmanager.windows.WindowManagerPolicy 29 import com.android.server.wm.traces.common.windowmanager.windows.WindowState 30 31 /** 32 * Represents a single WindowManager trace entry. 33 * 34 * This is a generic object that is reused by both Flicker and Winscope and cannot 35 * access internal Java/Android functionality 36 * 37 * The timestamp constructor must be a string due to lack of Kotlin/KotlinJS Long compatibility 38 * 39 **/ 40 open class WindowManagerState( 41 val where: String, 42 val policy: WindowManagerPolicy?, 43 val focusedApp: String, 44 val focusedDisplayId: Int, 45 val focusedWindow: String, 46 val inputMethodWindowAppToken: String, 47 val isHomeRecentsComponent: Boolean, 48 val isDisplayFrozen: Boolean, 49 val pendingActivities: Array<String>, 50 val root: RootWindowContainer, 51 val keyguardControllerState: KeyguardControllerState, 52 _timestamp: String = "0" 53 ) : ITraceEntry { 54 override val timestamp: Long = _timestamp.toLong() 55 val isVisible: Boolean = true 56 val stableId: String get() = this::class.simpleName ?: error("Unable to determine class") 57 val name: String get() = prettyTimestamp(timestamp) 58 59 val windowContainers: Array<WindowContainer> 60 get() = root.collectDescendants() 61 62 val children: Array<WindowContainer> 63 get() = root.children.reversedArray() 64 65 // Displays in z-order with the top most at the front of the list, starting with primary. 66 val displays: Array<DisplayContent> 67 get() = windowContainers.filterIsInstance<DisplayContent>().toTypedArray() 68 69 // Root tasks in z-order with the top most at the front of the list, starting with primary display. 70 val rootTasks: Array<Task> 71 get() = displays.flatMap { it.rootTasks.toList() }.toTypedArray() 72 73 // TaskFragments in z-order with the top most at the front of the list. 74 val taskFragments: Array<TaskFragment> 75 get() = windowContainers.filterIsInstance<TaskFragment>().toTypedArray() 76 77 // Windows in z-order with the top most at the front of the list. 78 val windowStates: Array<WindowState> 79 get() = windowContainers.filterIsInstance<WindowState>().toTypedArray() 80 81 @Deprecated("Please use windowStates instead", replaceWith = ReplaceWith("windowStates")) 82 val windows: Array<WindowState> 83 get() = windowStates 84 85 val appWindows: Array<WindowState> 86 get() = windowStates.filter { it.isAppWindow }.toTypedArray() 87 val nonAppWindows: Array<WindowState> 88 get() = windowStates.filterNot { it.isAppWindow }.toTypedArray() 89 val aboveAppWindows: Array<WindowState> 90 get() = windowStates.takeWhile { !appWindows.contains(it) }.toTypedArray() 91 val belowAppWindows: Array<WindowState> 92 get() = windowStates 93 .dropWhile { !appWindows.contains(it) }.drop(appWindows.size).toTypedArray() 94 val visibleWindows: Array<WindowState> 95 get() = windowStates 96 .filter { it.isVisible } 97 .filter { window -> 98 val activities = getActivitiesForWindow(window.title) 99 val activity = activities.firstOrNull { it.children.contains(window) } 100 activity?.isVisible ?: true 101 } 102 .toTypedArray() 103 val visibleAppWindows: Array<WindowState> 104 get() = visibleWindows.filter { it.isAppWindow }.toTypedArray() 105 val topVisibleAppWindow: String 106 get() = visibleAppWindows 107 .map { it.title } 108 .firstOrNull() ?: "" 109 val pinnedWindows: Array<WindowState> 110 get() = visibleWindows 111 .filter { it.windowingMode == WINDOWING_MODE_PINNED } 112 .toTypedArray() 113 114 /** 115 * Checks if the device state supports rotation, i.e., if the rotation sensor is 116 * enabled (e.g., launcher) and if the rotation not fixed 117 */ 118 val canRotate: Boolean 119 get() = policy?.isFixedOrientation != true && policy?.isOrientationNoSensor != true 120 val focusedDisplay: DisplayContent? get() = getDisplay(focusedDisplayId) 121 val focusedStackId: Int get() = focusedDisplay?.focusedRootTaskId ?: -1 122 val focusedActivity: String get() { 123 val focusedDisplay = focusedDisplay 124 return if (focusedDisplay != null && focusedDisplay.resumedActivity.isNotEmpty()) { 125 focusedDisplay.resumedActivity 126 } else { 127 getActivitiesForWindow(focusedWindow, focusedDisplayId).firstOrNull()?.name ?: "" 128 } 129 } 130 val resumedActivities: Array<String> 131 get() = rootTasks.flatMap { it.resumedActivities.toList() }.toTypedArray() 132 val resumedActivitiesCount: Int get() = resumedActivities.size 133 val stackCount: Int get() = rootTasks.size 134 val homeTask: Task? get() = getStackByActivityType(ACTIVITY_TYPE_HOME)?.topTask 135 val recentsTask: Task? get() = getStackByActivityType(ACTIVITY_TYPE_RECENTS)?.topTask 136 val homeActivity: Activity? get() = homeTask?.activities?.lastOrNull() 137 val recentsActivity: Activity? get() = recentsTask?.activities?.lastOrNull() 138 val isRecentsActivityVisible: Boolean get() = recentsActivity?.isVisible ?: false 139 val frontWindow: String? get() = windowStates.map { it.title }.firstOrNull() 140 val inputMethodWindowState: WindowState? 141 get() = getWindowStateForAppToken(inputMethodWindowAppToken) 142 143 fun getDefaultDisplay(): DisplayContent? = 144 displays.firstOrNull { it.id == DEFAULT_DISPLAY } 145 146 fun getDisplay(displayId: Int): DisplayContent? = 147 displays.firstOrNull { it.id == displayId } 148 149 fun countStacks(windowingMode: Int, activityType: Int): Int { 150 var count = 0 151 for (stack in rootTasks) { 152 if (activityType != ACTIVITY_TYPE_UNDEFINED && activityType != stack.activityType) { 153 continue 154 } 155 if (windowingMode != WINDOWING_MODE_UNDEFINED && windowingMode != stack.windowingMode) { 156 continue 157 } 158 ++count 159 } 160 return count 161 } 162 163 fun getRootTask(taskId: Int): Task? = 164 rootTasks.firstOrNull { it.rootTaskId == taskId } 165 166 fun getRotation(displayId: Int): Int = 167 getDisplay(displayId)?.rotation ?: error("Default display not found") 168 169 fun getOrientation(displayId: Int): Int = 170 getDisplay(displayId)?.lastOrientation ?: error("Default display not found") 171 172 fun getStackByActivityType(activityType: Int): Task? = 173 rootTasks.firstOrNull { it.activityType == activityType } 174 175 fun getStandardStackByWindowingMode(windowingMode: Int): Task? = 176 rootTasks.firstOrNull { 177 it.activityType == ACTIVITY_TYPE_STANDARD && 178 it.windowingMode == windowingMode 179 } 180 181 /** 182 * Get the all activities on display with id [displayId], containing a window whose title 183 * contains [partialWindowTitle] 184 * 185 * @param partialWindowTitle window title to search 186 * @param displayId display where to search the activity 187 */ 188 fun getActivitiesForWindow( 189 partialWindowTitle: String, 190 displayId: Int = DEFAULT_DISPLAY 191 ): List<Activity> { 192 return displays.firstOrNull { it.id == displayId }?.rootTasks?.mapNotNull { stack -> 193 stack.getActivity { activity -> 194 activity.hasWindow(partialWindowTitle) 195 } 196 } ?: emptyList() 197 } 198 199 fun containsActivity(activityName: String): Boolean = 200 rootTasks.any { it.containsActivity(activityName) } 201 202 fun isActivityVisible(activityName: String): Boolean { 203 val activity = rootTasks.mapNotNull { it.getActivity(activityName) }.firstOrNull() 204 return activity?.isVisible ?: false 205 } 206 207 fun hasActivityState(activityName: String, activityState: String): Boolean = 208 rootTasks.any { it.getActivity(activityName)?.state == activityState } 209 210 fun pendingActivityContain(activityName: String): Boolean { 211 return pendingActivities.contains(activityName) 212 } 213 214 fun getMatchingVisibleWindowState(windowName: String): Array<WindowState> { 215 return windowStates.filter { it.isSurfaceShown && it.title.contains(windowName) } 216 .toTypedArray() 217 } 218 219 fun getNavBarWindow(displayId: Int): WindowState? { 220 val navWindow = windowStates.filter { it.isValidNavBarType && it.displayId == displayId } 221 222 // We may need some time to wait for nav bar showing. 223 // It's Ok to get 0 nav bar here. 224 if (navWindow.size > 1) { 225 throw IllegalStateException("There should be at most one navigation bar on a display") 226 } 227 return navWindow.firstOrNull() 228 } 229 230 fun getWindowStateForAppToken(appToken: String): WindowState? = 231 windowStates.firstOrNull { it.token == appToken } 232 233 /** 234 * Check if there exists a window record with matching windowName. 235 */ 236 fun containsWindow(windowName: String): Boolean = 237 windowStates.any { it.title.contains(windowName) } 238 239 /** 240 * Check if at least one window which matches the specified name has shown it's surface. 241 */ 242 fun isWindowSurfaceShown(windowName: String): Boolean = 243 getMatchingVisibleWindowState(windowName).isNotEmpty() 244 245 /** 246 * Check if at least one window which matches provided window name is visible. 247 */ 248 fun isWindowVisible(windowName: String): Boolean = 249 visibleWindows.any { it.title == windowName } 250 251 /** 252 * Checks if the state has any window in PIP mode 253 */ 254 fun hasPipWindow(): Boolean = pinnedWindows.isNotEmpty() 255 256 /** 257 * Checks that an activity [windowName] is in PIP mode 258 */ 259 fun isInPipMode(windowName: String): Boolean { 260 return pinnedWindows.any { it.title.contains(windowName) } 261 } 262 263 fun getZOrder(w: WindowState): Int = windowStates.size - windowStates.indexOf(w) 264 265 fun defaultMinimalTaskSize(displayId: Int): Int = 266 dpToPx(DEFAULT_RESIZABLE_TASK_SIZE_DP.toFloat(), getDisplay(displayId)!!.dpi) 267 268 fun defaultMinimalDisplaySizeForSplitScreen(displayId: Int): Int { 269 return dpToPx(DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP.toFloat(), 270 getDisplay(displayId)!!.dpi) 271 } 272 273 fun getIsIncompleteReason(): String { 274 return buildString { 275 if (rootTasks.isEmpty()) { 276 append("No stacks found...") 277 } 278 if (focusedStackId == -1) { 279 append("No focused stack found...") 280 } 281 if (focusedActivity.isEmpty()) { 282 append("No focused activity found...") 283 } 284 if (resumedActivities.isEmpty()) { 285 append("No resumed activities found...") 286 } 287 if (windowStates.isEmpty()) { 288 append("No Windows found...") 289 } 290 if (focusedWindow.isEmpty()) { 291 append("No Focused Window...") 292 } 293 if (focusedApp.isEmpty()) { 294 append("No Focused App...") 295 } 296 if (keyguardControllerState.isKeyguardShowing) { 297 append("Keyguard showing...") 298 } 299 } 300 } 301 302 fun isComplete(): Boolean = !isIncomplete() 303 fun isIncomplete(): Boolean { 304 return rootTasks.isEmpty() || focusedStackId == -1 || windowStates.isEmpty() || 305 // overview screen has no focused window 306 ((focusedApp.isEmpty() || focusedWindow.isEmpty()) && homeActivity == null) || 307 (focusedActivity.isEmpty() || resumedActivities.isEmpty()) && 308 !keyguardControllerState.isKeyguardShowing 309 } 310 311 fun asTrace(): WindowManagerTrace = WindowManagerTrace(arrayOf(this)) 312 313 override fun toString(): String { 314 return "${prettyTimestamp(timestamp)} (timestamp=$timestamp)" 315 } 316 317 companion object { 318 const val STATE_INITIALIZING = "INITIALIZING" 319 const val STATE_RESUMED = "RESUMED" 320 const val STATE_PAUSED = "PAUSED" 321 const val STATE_STOPPED = "STOPPED" 322 const val STATE_DESTROYED = "DESTROYED" 323 const val APP_STATE_IDLE = "APP_STATE_IDLE" 324 internal const val ACTIVITY_TYPE_UNDEFINED = 0 325 internal const val ACTIVITY_TYPE_STANDARD = 1 326 internal const val DEFAULT_DISPLAY = 0 327 internal const val DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440 328 internal const val ACTIVITY_TYPE_HOME = 2 329 internal const val ACTIVITY_TYPE_RECENTS = 3 330 internal const val WINDOWING_MODE_UNDEFINED = 0 331 private const val DENSITY_DEFAULT = 160 332 /** 333 * @see android.app.WindowConfiguration.WINDOWING_MODE_PINNED 334 */ 335 private const val WINDOWING_MODE_PINNED = 2 336 337 /** 338 * @see android.view.WindowManager.LayoutParams 339 */ 340 internal const val TYPE_NAVIGATION_BAR_PANEL = 2024 341 342 // Default minimal size of resizable task, used if none is set explicitly. 343 // Must be kept in sync with 'default_minimal_size_resizable_task' 344 // dimen from frameworks/base. 345 internal const val DEFAULT_RESIZABLE_TASK_SIZE_DP = 220 346 347 fun dpToPx(dp: Float, densityDpi: Int): Int { 348 return (dp * densityDpi / DENSITY_DEFAULT + 0.5f).toInt() 349 } 350 } 351 override fun equals(other: Any?): Boolean { 352 return other is WindowManagerState && other.timestamp == this.timestamp 353 } 354 } 355