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.flicker.subject.wm 18 19 import android.tools.Rotation 20 import android.tools.flicker.subject.FlickerTraceSubject 21 import android.tools.flicker.subject.region.RegionTraceSubject 22 import android.tools.function.AssertionPredicate 23 import android.tools.io.Reader 24 import android.tools.traces.component.ComponentNameMatcher 25 import android.tools.traces.component.IComponentMatcher 26 import android.tools.traces.region.RegionTrace 27 import android.tools.traces.wm.WindowManagerTrace 28 import android.tools.traces.wm.WindowState 29 import java.util.function.Predicate 30 31 /** 32 * Subject for [WindowManagerTrace] objects, used to make assertions over behaviors that occur 33 * throughout a whole trace. 34 * 35 * To make assertions over a trace it is recommended to create a subject using 36 * [WindowManagerTraceSubject](myTrace). 37 * 38 * Example: 39 * ``` 40 * val trace = WindowManagerTraceParser().parse(myTraceFile) 41 * val subject = WindowManagerTraceSubject(trace) 42 * .contains("ValidWindow") 43 * .notContains("ImaginaryWindow") 44 * .showsAboveAppWindow("NavigationBar") 45 * .forAllEntries() 46 * ``` 47 * 48 * Example2: 49 * ``` 50 * val trace = WindowManagerTraceParser().parse(myTraceFile) 51 * val subject = WindowManagerTraceSubject(trace) { 52 * check(myCustomAssertion(this)) { "My assertion lazy message" } 53 * } 54 * ``` 55 */ 56 class WindowManagerTraceSubject 57 @JvmOverloads 58 constructor(val trace: WindowManagerTrace, override val reader: Reader? = null) : 59 FlickerTraceSubject<WindowManagerStateSubject>(), 60 IWindowManagerSubject<WindowManagerTraceSubject, RegionTraceSubject> { 61 62 override val subjects by lazy { 63 trace.entries.map { WindowManagerStateSubject(it, reader, this) } 64 } 65 66 /** {@inheritDoc} */ 67 override fun then(): WindowManagerTraceSubject = apply { super.then() } 68 69 /** {@inheritDoc} */ 70 override fun skipUntilFirstAssertion(): WindowManagerTraceSubject = apply { 71 super.skipUntilFirstAssertion() 72 } 73 74 /** {@inheritDoc} */ 75 override fun isEmpty(): WindowManagerTraceSubject = apply { 76 check { "Trace is empty" }.that(trace.entries.isEmpty()).isEqual(true) 77 } 78 79 /** {@inheritDoc} */ 80 override fun isNotEmpty(): WindowManagerTraceSubject = apply { 81 check { "Trace is not empty" }.that(trace.entries.isEmpty()).isEqual(false) 82 } 83 84 /** 85 * @return List of [WindowStateSubject]s matching [componentMatcher] in the order they 86 * 87 * ``` 88 * appear on the trace 89 * 90 * @param componentMatcher 91 * ``` 92 * 93 * Components to search 94 */ 95 fun windowStates(componentMatcher: IComponentMatcher): List<WindowStateSubject> = windowStates { 96 componentMatcher.windowMatchesAnyOf(it) 97 } 98 99 /** 100 * @return List of [WindowStateSubject]s matching [predicate] in the order they 101 * 102 * ``` 103 * appear on the trace 104 * 105 * @param predicate 106 * ``` 107 * 108 * To search 109 */ 110 fun windowStates(predicate: Predicate<WindowState>): List<WindowStateSubject> { 111 return subjects.mapNotNull { it.windowState { window -> predicate.test(window) } } 112 } 113 114 /** {@inheritDoc} */ 115 override fun notContains(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 116 notContains(componentMatcher, isOptional = false) 117 118 /** See [notContains] */ 119 fun notContains( 120 componentMatcher: IComponentMatcher, 121 isOptional: Boolean, 122 ): WindowManagerTraceSubject = apply { 123 addAssertion("notContains(${componentMatcher.toWindowIdentifier()})", isOptional) { 124 it.notContains(componentMatcher) 125 } 126 } 127 128 /** {@inheritDoc} */ 129 override fun isAboveAppWindowVisible( 130 componentMatcher: IComponentMatcher 131 ): WindowManagerTraceSubject = isAboveAppWindowVisible(componentMatcher, isOptional = false) 132 133 /** See [isAboveAppWindowVisible] */ 134 fun isAboveAppWindowVisible( 135 componentMatcher: IComponentMatcher, 136 isOptional: Boolean, 137 ): WindowManagerTraceSubject = apply { 138 addAssertion( 139 "isAboveAppWindowVisible(${componentMatcher.toWindowIdentifier()})", 140 isOptional, 141 ) { 142 it.isAboveAppWindowVisible(componentMatcher) 143 } 144 } 145 146 /** {@inheritDoc} */ 147 override fun isAboveAppWindowInvisible( 148 componentMatcher: IComponentMatcher 149 ): WindowManagerTraceSubject = isAboveAppWindowInvisible(componentMatcher, isOptional = false) 150 151 /** See [isAboveAppWindowInvisible] */ 152 fun isAboveAppWindowInvisible( 153 componentMatcher: IComponentMatcher, 154 isOptional: Boolean, 155 ): WindowManagerTraceSubject = apply { 156 addAssertion( 157 "isAboveAppWindowInvisible(${componentMatcher.toWindowIdentifier()})", 158 isOptional, 159 ) { 160 it.isAboveAppWindowInvisible(componentMatcher) 161 } 162 } 163 164 /** {@inheritDoc} */ 165 override fun isBelowAppWindowVisible( 166 componentMatcher: IComponentMatcher 167 ): WindowManagerTraceSubject = isBelowAppWindowVisible(componentMatcher, isOptional = false) 168 169 /** See [isBelowAppWindowVisible] */ 170 fun isBelowAppWindowVisible( 171 componentMatcher: IComponentMatcher, 172 isOptional: Boolean, 173 ): WindowManagerTraceSubject = apply { 174 addAssertion( 175 "isBelowAppWindowVisible(${componentMatcher.toWindowIdentifier()})", 176 isOptional, 177 ) { 178 it.isBelowAppWindowVisible(componentMatcher) 179 } 180 } 181 182 /** {@inheritDoc} */ 183 override fun isBelowAppWindowInvisible( 184 componentMatcher: IComponentMatcher 185 ): WindowManagerTraceSubject = isBelowAppWindowInvisible(componentMatcher, isOptional = false) 186 187 /** See [isBelowAppWindowInvisible] */ 188 fun isBelowAppWindowInvisible( 189 componentMatcher: IComponentMatcher, 190 isOptional: Boolean, 191 ): WindowManagerTraceSubject = apply { 192 addAssertion( 193 "isBelowAppWindowInvisible(${componentMatcher.toWindowIdentifier()})", 194 isOptional, 195 ) { 196 it.isBelowAppWindowInvisible(componentMatcher) 197 } 198 } 199 200 /** {@inheritDoc} */ 201 override fun isNonAppWindowVisible( 202 componentMatcher: IComponentMatcher 203 ): WindowManagerTraceSubject = isNonAppWindowVisible(componentMatcher, isOptional = false) 204 205 /** See [isNonAppWindowVisible] */ 206 fun isNonAppWindowVisible( 207 componentMatcher: IComponentMatcher, 208 isOptional: Boolean, 209 ): WindowManagerTraceSubject = apply { 210 addAssertion( 211 "isNonAppWindowVisible(${componentMatcher.toWindowIdentifier()})", 212 isOptional, 213 ) { 214 it.isNonAppWindowVisible(componentMatcher) 215 } 216 } 217 218 /** {@inheritDoc} */ 219 override fun isNonAppWindowInvisible( 220 componentMatcher: IComponentMatcher, 221 mustExist: Boolean, 222 ): WindowManagerTraceSubject = 223 isNonAppWindowInvisible(componentMatcher, isOptional = false, mustExist) 224 225 /** See [isNonAppWindowInvisible] */ 226 fun isNonAppWindowInvisible( 227 componentMatcher: IComponentMatcher, 228 isOptional: Boolean, 229 mustExist: Boolean = false, 230 ): WindowManagerTraceSubject = apply { 231 addAssertion( 232 "isNonAppWindowInvisible(${componentMatcher.toWindowIdentifier()})", 233 isOptional, 234 ) { 235 it.isNonAppWindowInvisible(componentMatcher, mustExist) 236 } 237 } 238 239 /** {@inheritDoc} */ 240 override fun isAppWindowOnTop(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 241 isAppWindowOnTop(componentMatcher, isOptional = false) 242 243 /** See [isAppWindowOnTop] */ 244 fun isAppWindowOnTop( 245 componentMatcher: IComponentMatcher, 246 isOptional: Boolean, 247 ): WindowManagerTraceSubject = apply { 248 addAssertion("isAppWindowOnTop(${componentMatcher.toWindowIdentifier()})", isOptional) { 249 it.isAppWindowOnTop(componentMatcher) 250 } 251 } 252 253 /** {@inheritDoc} */ 254 override fun isAppWindowNotOnTop( 255 componentMatcher: IComponentMatcher 256 ): WindowManagerTraceSubject = isAppWindowNotOnTop(componentMatcher, isOptional = false) 257 258 /** See [isAppWindowNotOnTop] */ 259 fun isAppWindowNotOnTop( 260 componentMatcher: IComponentMatcher, 261 isOptional: Boolean, 262 ): WindowManagerTraceSubject = apply { 263 addAssertion("appWindowNotOnTop(${componentMatcher.toWindowIdentifier()})", isOptional) { 264 it.isAppWindowNotOnTop(componentMatcher) 265 } 266 } 267 268 /** {@inheritDoc} */ 269 override fun isAppWindowVisible( 270 componentMatcher: IComponentMatcher 271 ): WindowManagerTraceSubject = isAppWindowVisible(componentMatcher, isOptional = false) 272 273 /** See [isAppWindowVisible] */ 274 fun isAppWindowVisible( 275 componentMatcher: IComponentMatcher, 276 isOptional: Boolean, 277 ): WindowManagerTraceSubject = apply { 278 addAssertion("isAppWindowVisible(${componentMatcher.toWindowIdentifier()})", isOptional) { 279 it.isAppWindowVisible(componentMatcher) 280 } 281 } 282 283 /** {@inheritDoc} */ 284 override fun hasNoVisibleAppWindow(): WindowManagerTraceSubject = 285 hasNoVisibleAppWindow(isOptional = false) 286 287 /** See [hasNoVisibleAppWindow] */ 288 fun hasNoVisibleAppWindow(isOptional: Boolean): WindowManagerTraceSubject = apply { 289 addAssertion("hasNoVisibleAppWindow()", isOptional) { it.hasNoVisibleAppWindow() } 290 } 291 292 /** {@inheritDoc} */ 293 override fun isKeyguardShowing(): WindowManagerTraceSubject = 294 isKeyguardShowing(isOptional = false) 295 296 /** See [isKeyguardShowing] */ 297 fun isKeyguardShowing(isOptional: Boolean): WindowManagerTraceSubject = apply { 298 addAssertion("isKeyguardShowing()", isOptional) { it.isKeyguardShowing() } 299 } 300 301 /** {@inheritDoc} */ 302 override fun isAppSnapshotStartingWindowVisibleFor( 303 componentMatcher: IComponentMatcher 304 ): WindowManagerTraceSubject = 305 isAppSnapshotStartingWindowVisibleFor(componentMatcher, isOptional = false) 306 307 /** See [isAppSnapshotStartingWindowVisibleFor] */ 308 fun isAppSnapshotStartingWindowVisibleFor( 309 componentMatcher: IComponentMatcher, 310 isOptional: Boolean, 311 ): WindowManagerTraceSubject = apply { 312 addAssertion( 313 "isAppSnapshotStartingWindowVisibleFor(${componentMatcher.toWindowIdentifier()})", 314 isOptional, 315 ) { 316 it.isAppSnapshotStartingWindowVisibleFor(componentMatcher) 317 } 318 } 319 320 /** {@inheritDoc} */ 321 override fun isAppWindowInvisible( 322 componentMatcher: IComponentMatcher, 323 mustExist: Boolean, 324 ): WindowManagerTraceSubject = 325 isAppWindowInvisible(componentMatcher, isOptional = false, mustExist) 326 327 /** See [isAppWindowInvisible] */ 328 fun isAppWindowInvisible( 329 componentMatcher: IComponentMatcher, 330 isOptional: Boolean, 331 mustExist: Boolean = false, 332 ): WindowManagerTraceSubject = apply { 333 addAssertion("isAppWindowInvisible(${componentMatcher.toWindowIdentifier()})", isOptional) { 334 it.isAppWindowInvisible(componentMatcher, mustExist) 335 } 336 } 337 338 /** {@inheritDoc} */ 339 override fun doNotOverlap( 340 vararg componentMatcher: IComponentMatcher 341 ): WindowManagerTraceSubject = apply { 342 val repr = componentMatcher.joinToString(", ") { it.toWindowIdentifier() } 343 addAssertion("noWindowsOverlap($repr)") { it.doNotOverlap(*componentMatcher) } 344 } 345 346 /** {@inheritDoc} */ 347 override fun isAboveWindow( 348 aboveWindowComponentMatcher: IComponentMatcher, 349 belowWindowComponentMatcher: IComponentMatcher, 350 ): WindowManagerTraceSubject = apply { 351 val aboveWindowTitle = aboveWindowComponentMatcher.toWindowIdentifier() 352 val belowWindowTitle = belowWindowComponentMatcher.toWindowIdentifier() 353 addAssertion("$aboveWindowTitle is above $belowWindowTitle") { 354 it.isAboveWindow(aboveWindowComponentMatcher, belowWindowComponentMatcher) 355 } 356 } 357 358 /** See [isAppWindowInvisible] */ 359 override fun visibleRegion(componentMatcher: IComponentMatcher?): RegionTraceSubject { 360 val regionTrace = 361 RegionTrace( 362 componentMatcher, 363 subjects.map { it.visibleRegion(componentMatcher).regionEntry }, 364 ) 365 366 return RegionTraceSubject(regionTrace, reader) 367 } 368 369 /** {@inheritDoc} */ 370 override fun contains(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 371 contains(componentMatcher, isOptional = false) 372 373 /** See [contains] */ 374 fun contains( 375 componentMatcher: IComponentMatcher, 376 isOptional: Boolean, 377 ): WindowManagerTraceSubject = apply { 378 addAssertion("contains(${componentMatcher.toWindowIdentifier()})", isOptional) { 379 it.contains(componentMatcher) 380 } 381 } 382 383 /** {@inheritDoc} */ 384 override fun containsAboveAppWindow( 385 componentMatcher: IComponentMatcher 386 ): WindowManagerTraceSubject = containsAboveAppWindow(componentMatcher, isOptional = false) 387 388 /** See [containsAboveAppWindow] */ 389 fun containsAboveAppWindow( 390 componentMatcher: IComponentMatcher, 391 isOptional: Boolean, 392 ): WindowManagerTraceSubject = apply { 393 addAssertion( 394 "containsAboveAppWindow(${componentMatcher.toWindowIdentifier()})", 395 isOptional, 396 ) { 397 it.containsAboveAppWindow(componentMatcher) 398 } 399 } 400 401 /** {@inheritDoc} */ 402 override fun containsAppWindow(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 403 containsAppWindow(componentMatcher, isOptional = false) 404 405 /** See [containsAppWindow] */ 406 fun containsAppWindow( 407 componentMatcher: IComponentMatcher, 408 isOptional: Boolean, 409 ): WindowManagerTraceSubject = apply { 410 addAssertion("containsAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) { 411 it.containsAboveAppWindow(componentMatcher) 412 } 413 } 414 415 /** {@inheritDoc} */ 416 override fun containsBelowAppWindow( 417 componentMatcher: IComponentMatcher 418 ): WindowManagerTraceSubject = containsBelowAppWindow(componentMatcher, isOptional = false) 419 420 /** See [containsBelowAppWindow] */ 421 fun containsBelowAppWindow( 422 componentMatcher: IComponentMatcher, 423 isOptional: Boolean, 424 ): WindowManagerTraceSubject = apply { 425 addAssertion( 426 "containsBelowAppWindows(${componentMatcher.toWindowIdentifier()})", 427 isOptional, 428 ) { 429 it.containsBelowAppWindow(componentMatcher) 430 } 431 } 432 433 /** {@inheritDoc} */ 434 override fun containsNonAppWindow( 435 componentMatcher: IComponentMatcher 436 ): WindowManagerTraceSubject = containsNonAppWindow(componentMatcher, isOptional = false) 437 438 /** See [containsNonAppWindow] */ 439 fun containsNonAppWindow( 440 componentMatcher: IComponentMatcher, 441 isOptional: Boolean, 442 ): WindowManagerTraceSubject = apply { 443 addAssertion("containsNonAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) { 444 it.containsNonAppWindow(componentMatcher) 445 } 446 } 447 448 /** {@inheritDoc} */ 449 override fun isHomeActivityInvisible(): WindowManagerTraceSubject = 450 isHomeActivityInvisible(isOptional = false) 451 452 /** See [isHomeActivityInvisible] */ 453 fun isHomeActivityInvisible(isOptional: Boolean): WindowManagerTraceSubject = apply { 454 addAssertion("isHomeActivityInvisible", isOptional) { it.isHomeActivityInvisible() } 455 } 456 457 /** {@inheritDoc} */ 458 override fun isHomeActivityVisible(): WindowManagerTraceSubject = 459 isHomeActivityVisible(isOptional = false) 460 461 /** See [isHomeActivityVisible] */ 462 fun isHomeActivityVisible(isOptional: Boolean): WindowManagerTraceSubject = apply { 463 addAssertion("isHomeActivityVisible", isOptional) { it.isHomeActivityVisible() } 464 } 465 466 /** {@inheritDoc} */ 467 override fun hasRotation(rotation: Rotation, displayId: Int): WindowManagerTraceSubject = 468 hasRotation(rotation, displayId, isOptional = false) 469 470 /** See [hasRotation] */ 471 fun hasRotation( 472 rotation: Rotation, 473 displayId: Int, 474 isOptional: Boolean, 475 ): WindowManagerTraceSubject = apply { 476 addAssertion("hasRotation($rotation, display=$displayId)", isOptional) { 477 it.hasRotation(rotation, displayId) 478 } 479 } 480 481 /** {@inheritDoc} */ 482 override fun isNotPinned(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 483 isNotPinned(componentMatcher, isOptional = false) 484 485 /** See [isNotPinned] */ 486 fun isNotPinned( 487 componentMatcher: IComponentMatcher, 488 isOptional: Boolean, 489 ): WindowManagerTraceSubject = apply { 490 addAssertion("isNotPinned(${componentMatcher.toWindowIdentifier()})", isOptional) { 491 it.isNotPinned(componentMatcher) 492 } 493 } 494 495 /** {@inheritDoc} */ 496 override fun isFocusedApp(app: String): WindowManagerTraceSubject = 497 isFocusedApp(app, isOptional = false) 498 499 /** See [isFocusedApp] */ 500 fun isFocusedApp(app: String, isOptional: Boolean): WindowManagerTraceSubject = apply { 501 addAssertion("isFocusedApp($app)", isOptional) { it.isFocusedApp(app) } 502 } 503 504 /** {@inheritDoc} */ 505 override fun isNotFocusedApp(app: String): WindowManagerTraceSubject = 506 isNotFocusedApp(app, isOptional = false) 507 508 /** See [isNotFocusedApp] */ 509 fun isNotFocusedApp(app: String, isOptional: Boolean): WindowManagerTraceSubject = apply { 510 addAssertion("isNotFocusedApp($app)", isOptional) { it.isNotFocusedApp(app) } 511 } 512 513 /** {@inheritDoc} */ 514 override fun isPinned(componentMatcher: IComponentMatcher): WindowManagerTraceSubject = 515 isPinned(componentMatcher, isOptional = false) 516 517 /** See [isPinned] */ 518 fun isPinned( 519 componentMatcher: IComponentMatcher, 520 isOptional: Boolean, 521 ): WindowManagerTraceSubject = apply { 522 addAssertion("isPinned(${componentMatcher.toWindowIdentifier()})", isOptional) { 523 it.isPinned(componentMatcher) 524 } 525 } 526 527 /** {@inheritDoc} */ 528 override fun isRecentsActivityInvisible(): WindowManagerTraceSubject = 529 isRecentsActivityInvisible(isOptional = false) 530 531 /** See [isRecentsActivityInvisible] */ 532 fun isRecentsActivityInvisible(isOptional: Boolean): WindowManagerTraceSubject = apply { 533 addAssertion("isRecentsActivityInvisible", isOptional) { it.isRecentsActivityInvisible() } 534 } 535 536 /** {@inheritDoc} */ 537 override fun isRecentsActivityVisible(): WindowManagerTraceSubject = 538 isRecentsActivityVisible(isOptional = false) 539 540 /** See [isRecentsActivityVisible] */ 541 fun isRecentsActivityVisible(isOptional: Boolean): WindowManagerTraceSubject = apply { 542 addAssertion("isRecentsActivityVisible", isOptional) { it.isRecentsActivityVisible() } 543 } 544 545 override fun isValid(): WindowManagerTraceSubject = apply { 546 addAssertion("isValid") { it.isValid() } 547 } 548 549 /** {@inheritDoc} */ 550 override fun notContainsAppWindow( 551 componentMatcher: IComponentMatcher 552 ): WindowManagerTraceSubject = notContainsAppWindow(componentMatcher, isOptional = false) 553 554 /** See [notContainsAppWindow] */ 555 fun notContainsAppWindow( 556 componentMatcher: IComponentMatcher, 557 isOptional: Boolean, 558 ): WindowManagerTraceSubject = apply { 559 addAssertion("notContainsAppWindow(${componentMatcher.toWindowIdentifier()})", isOptional) { 560 it.notContainsAppWindow(componentMatcher) 561 } 562 } 563 564 /** {@inheritDoc} */ 565 override fun containsAtLeastOneDisplay(): WindowManagerTraceSubject = apply { 566 addAssertion("containAtLeastOneDisplay", isOptional = false) { 567 it.containsAtLeastOneDisplay() 568 } 569 } 570 571 /** Checks that all visible layers are shown for more than one consecutive entry */ 572 fun visibleWindowsShownMoreThanOneConsecutiveEntry( 573 ignoreWindows: List<ComponentNameMatcher> = 574 listOf( 575 ComponentNameMatcher.SPLASH_SCREEN, 576 ComponentNameMatcher.SNAPSHOT, 577 ComponentNameMatcher.SECONDARY_HOME_HANDLE, 578 ComponentNameMatcher.EDGE_BACK_GESTURE_HANDLER, 579 ) 580 ): WindowManagerTraceSubject = apply { 581 visibleEntriesShownMoreThanOneConsecutiveTime { subject -> 582 subject.wmState.windowStates 583 .filter { it.isVisible } 584 .filter { window -> 585 ignoreWindows.none { matcher -> matcher.windowMatchesAnyOf(window) } 586 } 587 .map { it.name } 588 .toSet() 589 } 590 } 591 592 /** Executes a custom [assertion] on the current subject */ 593 operator fun invoke( 594 name: String, 595 isOptional: Boolean = false, 596 assertion: AssertionPredicate<WindowManagerStateSubject>, 597 ): WindowManagerTraceSubject = apply { addAssertion(name, isOptional, assertion) } 598 599 /** Run the assertions for all trace entries within the specified time range */ 600 fun forElapsedTimeRange(startTime: Long, endTime: Long) { 601 val subjectsInRange = 602 subjects.filter { it.wmState.timestamp.elapsedNanos in startTime..endTime } 603 assertionsChecker.test(subjectsInRange) 604 } 605 606 /** 607 * User-defined entry point for the trace entry with [timestamp] 608 * 609 * @param timestamp of the entry 610 */ 611 fun getEntryByElapsedTimestamp(timestamp: Long): WindowManagerStateSubject = 612 subjects.first { it.wmState.timestamp.elapsedNanos == timestamp } 613 } 614