1 /* 2 * 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; 18 19 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 26 import static com.android.server.wm.BLASTSyncEngine.METHOD_NONE; 27 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; 28 import static com.android.server.wm.WindowContainer.POSITION_TOP; 29 import static com.android.server.wm.WindowContainer.SYNC_STATE_NONE; 30 import static com.android.server.wm.WindowContainer.SYNC_STATE_READY; 31 import static com.android.server.wm.WindowState.BLAST_TIMEOUT_DURATION; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertFalse; 35 import static org.junit.Assert.assertTrue; 36 import static org.mockito.ArgumentMatchers.any; 37 import static org.mockito.ArgumentMatchers.anyInt; 38 import static org.mockito.ArgumentMatchers.eq; 39 import static org.mockito.ArgumentMatchers.notNull; 40 import static org.mockito.Mockito.spy; 41 42 import android.platform.test.annotations.Presubmit; 43 import android.view.SurfaceControl; 44 45 import androidx.test.filters.SmallTest; 46 47 import com.android.server.testutils.StubTransaction; 48 import com.android.server.testutils.TestHandler; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.InOrder; 55 import org.mockito.Mockito; 56 57 /** 58 * Test class for {@link BLASTSyncEngine}. 59 * 60 * Build/Install/Run: 61 * atest WmTests:SyncEngineTests 62 */ 63 @SmallTest 64 @Presubmit 65 @RunWith(WindowTestRunner.class) 66 public class SyncEngineTests extends WindowTestsBase { 67 68 @Before setUp()69 public void setUp() { 70 spyOn(mWm.mWindowPlacerLocked); 71 } 72 73 @Test testTrivialSyncCallback()74 public void testTrivialSyncCallback() { 75 TestWindowContainer mockWC = new TestWindowContainer(mWm, false /* waiter */); 76 77 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 78 79 BLASTSyncEngine.TransactionReadyListener listener = mock( 80 BLASTSyncEngine.TransactionReadyListener.class); 81 82 int id = startSyncSet(bse, listener); 83 bse.addToSyncSet(id, mockWC); 84 // The traversal is not requested because ready is not set. 85 verify(mWm.mWindowPlacerLocked, times(0)).requestTraversal(); 86 87 bse.onSurfacePlacement(); 88 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 89 90 bse.setReady(id); 91 // Make sure a traversal is requested 92 verify(mWm.mWindowPlacerLocked).requestTraversal(); 93 bse.onSurfacePlacement(); 94 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 95 96 // make sure it was cleaned-up (no second callback) 97 bse.onSurfacePlacement(); 98 verify(listener, times(1)).onTransactionReady(anyInt(), any()); 99 } 100 101 @Test testWaitingSyncCallback()102 public void testWaitingSyncCallback() { 103 TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */); 104 105 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 106 107 BLASTSyncEngine.TransactionReadyListener listener = mock( 108 BLASTSyncEngine.TransactionReadyListener.class); 109 110 int id = startSyncSet(bse, listener); 111 bse.addToSyncSet(id, mockWC); 112 bse.setReady(id); 113 // Make sure traversals requested. 114 verify(mWm.mWindowPlacerLocked).requestTraversal(); 115 bse.onSurfacePlacement(); 116 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 117 118 assertTrue(mockWC.onSyncFinishedDrawing()); 119 bse.onSurfacePlacement(); 120 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 121 122 // The sync is not finished for a relaunching activity. 123 id = startSyncSet(bse, listener); 124 final ActivityRecord r = new ActivityBuilder(mAtm).setCreateTask(true).build(); 125 final WindowState w = mock(WindowState.class); 126 doReturn(true).when(w).isVisibleRequested(); 127 doReturn(true).when(w).fillsParent(); 128 doReturn(true).when(w).isSyncFinished(any()); 129 r.mChildren.add(w); 130 bse.addToSyncSet(id, r); 131 r.onSyncFinishedDrawing(); 132 assertTrue(r.isSyncFinished(r.getSyncGroup())); 133 r.startRelaunching(); 134 assertFalse(r.isSyncFinished(r.getSyncGroup())); 135 r.finishRelaunching(); 136 assertTrue(r.isSyncFinished(r.getSyncGroup())); 137 } 138 139 @Test testInvisibleSyncCallback()140 public void testInvisibleSyncCallback() { 141 TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */); 142 143 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 144 145 BLASTSyncEngine.TransactionReadyListener listener = mock( 146 BLASTSyncEngine.TransactionReadyListener.class); 147 148 int id = startSyncSet(bse, listener); 149 bse.addToSyncSet(id, mockWC); 150 bse.setReady(id); 151 // Make sure traversals requested. 152 verify(mWm.mWindowPlacerLocked).requestTraversal(); 153 bse.onSurfacePlacement(); 154 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 155 156 // Finish sync if invisible. 157 mockWC.mVisibleRequested = false; 158 bse.onSurfacePlacement(); 159 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 160 assertEquals(SYNC_STATE_NONE, mockWC.mSyncState); 161 } 162 163 @Test testWaitForChildrenCallback()164 public void testWaitForChildrenCallback() { 165 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 166 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 167 TestWindowContainer childWC2 = new TestWindowContainer(mWm, true /* waiter */); 168 parentWC.addChild(childWC, POSITION_TOP); 169 parentWC.addChild(childWC2, POSITION_TOP); 170 171 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 172 173 BLASTSyncEngine.TransactionReadyListener listener = mock( 174 BLASTSyncEngine.TransactionReadyListener.class); 175 176 int id = startSyncSet(bse, listener); 177 bse.addToSyncSet(id, parentWC); 178 bse.setReady(id); 179 bse.onSurfacePlacement(); 180 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 181 182 parentWC.onSyncFinishedDrawing(); 183 bse.onSurfacePlacement(); 184 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 185 186 childWC.onSyncFinishedDrawing(); 187 bse.onSurfacePlacement(); 188 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 189 190 childWC2.onSyncFinishedDrawing(); 191 bse.onSurfacePlacement(); 192 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 193 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 194 assertEquals(SYNC_STATE_NONE, childWC.mSyncState); 195 assertEquals(SYNC_STATE_NONE, childWC2.mSyncState); 196 } 197 198 @Test testWaitForParentCallback()199 public void testWaitForParentCallback() { 200 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 201 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 202 parentWC.addChild(childWC, POSITION_TOP); 203 204 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 205 206 BLASTSyncEngine.TransactionReadyListener listener = mock( 207 BLASTSyncEngine.TransactionReadyListener.class); 208 209 int id = startSyncSet(bse, listener); 210 bse.addToSyncSet(id, parentWC); 211 bse.setReady(id); 212 bse.onSurfacePlacement(); 213 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 214 215 childWC.onSyncFinishedDrawing(); 216 bse.onSurfacePlacement(); 217 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 218 219 parentWC.onSyncFinishedDrawing(); 220 bse.onSurfacePlacement(); 221 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 222 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 223 assertEquals(SYNC_STATE_NONE, childWC.mSyncState); 224 } 225 226 @Test testFillsParent()227 public void testFillsParent() { 228 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 229 TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */); 230 TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */); 231 topChildWC.mFillsParent = botChildWC.mFillsParent = true; 232 parentWC.addChild(topChildWC, POSITION_TOP); 233 parentWC.addChild(botChildWC, POSITION_BOTTOM); 234 235 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 236 237 BLASTSyncEngine.TransactionReadyListener listener = mock( 238 BLASTSyncEngine.TransactionReadyListener.class); 239 240 int id = startSyncSet(bse, listener); 241 bse.addToSyncSet(id, parentWC); 242 bse.setReady(id); 243 bse.onSurfacePlacement(); 244 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 245 246 parentWC.onSyncFinishedDrawing(); 247 topChildWC.onSyncFinishedDrawing(); 248 // Even though bottom isn't finished, we should see callback because it is occluded by top. 249 assertFalse(botChildWC.isSyncFinished(botChildWC.getSyncGroup())); 250 bse.onSurfacePlacement(); 251 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 252 253 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 254 assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState); 255 assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState); 256 } 257 258 @Test testReparentOut()259 public void testReparentOut() { 260 TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */); 261 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 262 TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */); 263 TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */); 264 parentWC.addChild(topChildWC, POSITION_TOP); 265 parentWC.addChild(botChildWC, POSITION_BOTTOM); 266 267 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 268 269 BLASTSyncEngine.TransactionReadyListener listener = mock( 270 BLASTSyncEngine.TransactionReadyListener.class); 271 272 int id = startSyncSet(bse, listener); 273 bse.addToSyncSet(id, parentWC); 274 bse.setReady(id); 275 bse.onSurfacePlacement(); 276 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 277 278 parentWC.onSyncFinishedDrawing(); 279 topChildWC.onSyncFinishedDrawing(); 280 bse.onSurfacePlacement(); 281 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 282 283 // reparent out cancels 284 botChildWC.reparent(nonMemberParentWC, POSITION_TOP); 285 assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState); 286 287 bse.onSurfacePlacement(); 288 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 289 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 290 assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState); 291 } 292 293 @Test testReparentIn()294 public void testReparentIn() { 295 TestWindowContainer nonMemberParentWC = new TestWindowContainer(mWm, true /* waiter */); 296 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 297 TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */); 298 TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */); 299 parentWC.addChild(topChildWC, POSITION_TOP); 300 nonMemberParentWC.addChild(botChildWC, POSITION_BOTTOM); 301 302 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 303 304 BLASTSyncEngine.TransactionReadyListener listener = mock( 305 BLASTSyncEngine.TransactionReadyListener.class); 306 307 int id = startSyncSet(bse, listener); 308 bse.addToSyncSet(id, parentWC); 309 bse.setReady(id); 310 bse.onSurfacePlacement(); 311 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 312 313 parentWC.onSyncFinishedDrawing(); 314 topChildWC.onSyncFinishedDrawing(); 315 316 // No-longer finished because new child 317 botChildWC.reparent(parentWC, POSITION_BOTTOM); 318 bse.onSurfacePlacement(); 319 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 320 321 botChildWC.onSyncFinishedDrawing(); 322 bse.onSurfacePlacement(); 323 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 324 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 325 assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState); 326 assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState); 327 328 // If the appearance of window won't change after reparenting, its sync state can be kept. 329 final WindowState w = createWindow(null, TYPE_BASE_APPLICATION, "win"); 330 parentWC.onRequestedOverrideConfigurationChanged(w.getConfiguration()); 331 w.reparent(botChildWC, POSITION_TOP); 332 parentWC.prepareSync(); 333 // Assume the window has drawn with the latest configuration. 334 makeLastConfigReportedToClient(w, true /* visible */); 335 assertTrue(w.onSyncFinishedDrawing()); 336 assertEquals(SYNC_STATE_READY, w.mSyncState); 337 w.reparent(topChildWC, POSITION_TOP); 338 assertEquals(SYNC_STATE_READY, w.mSyncState); 339 } 340 341 @Test testRemoval()342 public void testRemoval() { 343 // Need different transactions to verify stuff 344 mWm.mTransactionFactory = () -> spy(new StubTransaction()); 345 TestWindowContainer rootWC = new TestWindowContainer(mWm, false /* waiter */); 346 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 347 TestWindowContainer topChildWC = new TestWindowContainer(mWm, true /* waiter */); 348 TestWindowContainer botChildWC = new TestWindowContainer(mWm, true /* waiter */); 349 rootWC.addChild(parentWC, POSITION_TOP); 350 parentWC.addChild(topChildWC, POSITION_TOP); 351 parentWC.addChild(botChildWC, POSITION_BOTTOM); 352 353 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 354 355 BLASTSyncEngine.TransactionReadyListener listener = mock( 356 BLASTSyncEngine.TransactionReadyListener.class); 357 358 int id = startSyncSet(bse, listener); 359 bse.addToSyncSet(id, parentWC); 360 final BLASTSyncEngine.SyncGroup syncGroup = parentWC.mSyncGroup; 361 bse.setReady(id); 362 bse.onSurfacePlacement(); 363 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 364 365 parentWC.onSyncFinishedDrawing(); 366 topChildWC.removeImmediately(); 367 bse.onSurfacePlacement(); 368 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 369 370 // Removal should merge transaction into parent 371 verify(parentWC.mSyncTransaction, times(1)).merge(eq(topChildWC.mSyncTransaction)); 372 assertEquals(SYNC_STATE_NONE, topChildWC.mSyncState); 373 374 // Removal of a sync-root should merge transaction into orphan 375 parentWC.removeImmediately(); 376 final SurfaceControl.Transaction orphan = syncGroup.getOrphanTransaction(); 377 verify(orphan, times(1)).merge(eq(parentWC.mSyncTransaction)); 378 379 // Then the orphan transaction should be merged into sync 380 bse.onSurfacePlacement(); 381 final ArgumentCaptor<SurfaceControl.Transaction> merged = 382 ArgumentCaptor.forClass(SurfaceControl.Transaction.class); 383 verify(listener, times(1)).onTransactionReady(eq(id), merged.capture()); 384 final SurfaceControl.Transaction mergedTransaction = merged.getValue(); 385 verify(mergedTransaction, times(1)).merge(eq(orphan)); 386 387 assertEquals(SYNC_STATE_NONE, parentWC.mSyncState); 388 assertEquals(SYNC_STATE_NONE, botChildWC.mSyncState); 389 } 390 391 @Test testNonBlastMethod()392 public void testNonBlastMethod() { 393 mAppWindow = createWindow(null, TYPE_BASE_APPLICATION, "mAppWindow"); 394 395 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 396 397 BLASTSyncEngine.TransactionReadyListener listener = mock( 398 BLASTSyncEngine.TransactionReadyListener.class); 399 400 final int id = startSyncSet(bse, listener); 401 bse.setSyncMethod(id, METHOD_NONE); 402 bse.addToSyncSet(id, mAppWindow.mToken); 403 mAppWindow.prepareSync(); 404 assertFalse(mAppWindow.shouldSyncWithBuffers()); 405 406 mAppWindow.removeImmediately(); 407 } 408 409 @Test testQueueSyncSet()410 public void testQueueSyncSet() { 411 final TestHandler testHandler = new TestHandler(null); 412 TestWindowContainer mockWC = new TestWindowContainer(mWm, true /* waiter */); 413 TestWindowContainer mockWC2 = new TestWindowContainer(mWm, true /* waiter */); 414 415 final BLASTSyncEngine bse = createTestBLASTSyncEngine(testHandler); 416 417 BLASTSyncEngine.TransactionReadyListener listener = mock( 418 BLASTSyncEngine.TransactionReadyListener.class); 419 420 int id = startSyncSet(bse, listener); 421 bse.addToSyncSet(id, mockWC); 422 bse.setReady(id); 423 bse.onSurfacePlacement(); 424 verify(listener, times(0)).onTransactionReady(eq(id), notNull()); 425 426 final int[] nextId = new int[]{-1}; 427 bse.queueSyncSet( 428 () -> nextId[0] = startSyncSet(bse, listener), 429 () -> { 430 bse.setReady(nextId[0]); 431 bse.addToSyncSet(nextId[0], mockWC2); 432 }); 433 434 // Make sure it is queued 435 assertEquals(-1, nextId[0]); 436 437 // Finish the original sync and see that we've started a new sync-set immediately but 438 // that the readiness was posted. 439 mockWC.onSyncFinishedDrawing(); 440 verify(mWm.mWindowPlacerLocked).requestTraversal(); 441 bse.onSurfacePlacement(); 442 verify(listener, times(1)).onTransactionReady(eq(id), notNull()); 443 444 assertTrue(nextId[0] != -1); 445 assertFalse(bse.isReady(nextId[0])); 446 447 // now make sure the applySync callback was posted. 448 testHandler.flush(); 449 assertTrue(bse.isReady(nextId[0])); 450 } 451 452 @Test testStratifiedParallel()453 public void testStratifiedParallel() { 454 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 455 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 456 parentWC.addChild(childWC, POSITION_TOP); 457 childWC.mVisibleRequested = true; 458 childWC.mFillsParent = true; 459 460 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 461 462 BLASTSyncEngine.TransactionReadyListener listenerChild = mock( 463 BLASTSyncEngine.TransactionReadyListener.class); 464 BLASTSyncEngine.TransactionReadyListener listenerParent = mock( 465 BLASTSyncEngine.TransactionReadyListener.class); 466 467 // Start a sync-set for the "inner" stuff 468 int childSync = startSyncSet(bse, listenerChild); 469 bse.addToSyncSet(childSync, childWC); 470 bse.setReady(childSync); 471 472 // Start sync-set for the "outer" stuff but explicitly parallel (it should ignore child) 473 int parentSync = startSyncSet(bse, listenerParent, true /* parallel */); 474 bse.addToSyncSet(parentSync, parentWC); 475 bse.setReady(parentSync); 476 477 bse.onSurfacePlacement(); 478 // Nothing should have happened yet 479 verify(listenerChild, times(0)).onTransactionReady(anyInt(), any()); 480 verify(listenerParent, times(0)).onTransactionReady(anyInt(), any()); 481 482 // Now, make PARENT ready, since they are in parallel, this should work 483 parentWC.onSyncFinishedDrawing(); 484 bse.onSurfacePlacement(); 485 486 // Parent should become ready while child is still waiting. 487 verify(listenerParent, times(1)).onTransactionReady(eq(parentSync), notNull()); 488 verify(listenerChild, times(0)).onTransactionReady(anyInt(), any()); 489 490 // Child should still work 491 childWC.onSyncFinishedDrawing(); 492 bse.onSurfacePlacement(); 493 verify(listenerChild, times(1)).onTransactionReady(eq(childSync), notNull()); 494 } 495 496 @Test testDependencies()497 public void testDependencies() { 498 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 499 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 500 TestWindowContainer childWC2 = new TestWindowContainer(mWm, true /* waiter */); 501 parentWC.addChild(childWC, POSITION_TOP); 502 childWC.mVisibleRequested = true; 503 childWC.mFillsParent = true; 504 childWC2.mVisibleRequested = true; 505 childWC2.mFillsParent = true; 506 507 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 508 509 BLASTSyncEngine.TransactionReadyListener listener = mock( 510 BLASTSyncEngine.TransactionReadyListener.class); 511 512 // This is non-parallel, so it is waiting on the child as-well 513 int sync1 = startSyncSet(bse, listener); 514 bse.addToSyncSet(sync1, parentWC); 515 bse.setReady(sync1); 516 517 // Create one which will end-up depending on the *next* sync 518 int sync2 = startSyncSet(bse, listener, true /* parallel */); 519 520 // If another sync tries to sync on the same subtree, it must now serialize with the other. 521 int sync3 = startSyncSet(bse, listener, true /* parallel */); 522 bse.addToSyncSet(sync3, childWC); 523 bse.addToSyncSet(sync3, childWC2); 524 bse.setReady(sync3); 525 526 // This will depend on sync3. 527 int sync4 = startSyncSet(bse, listener, true /* parallel */); 528 bse.addToSyncSet(sync4, childWC2); 529 bse.setReady(sync4); 530 531 // This makes sync2 depend on sync3. Since both sync2 and sync4 depend on sync3, when sync3 532 // finishes, sync2 should run first since it was created first. 533 bse.addToSyncSet(sync2, childWC2); 534 bse.setReady(sync2); 535 536 childWC.onSyncFinishedDrawing(); 537 childWC2.onSyncFinishedDrawing(); 538 bse.onSurfacePlacement(); 539 540 // Nothing should be ready yet since everything ultimately depends on sync1. 541 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 542 543 parentWC.onSyncFinishedDrawing(); 544 bse.onSurfacePlacement(); 545 546 // They should all be ready, now, so just verify that the order is expected 547 InOrder readyOrder = Mockito.inOrder(listener); 548 // sync1 is the first one, so it should call ready first. 549 readyOrder.verify(listener).onTransactionReady(eq(sync1), any()); 550 // everything else depends on sync3, so it should call ready next. 551 readyOrder.verify(listener).onTransactionReady(eq(sync3), any()); 552 // both sync2 and sync4 depend on sync3, but sync2 started first, so it should go next. 553 readyOrder.verify(listener).onTransactionReady(eq(sync2), any()); 554 readyOrder.verify(listener).onTransactionReady(eq(sync4), any()); 555 } 556 557 @Test testStratifiedParallelParentFirst()558 public void testStratifiedParallelParentFirst() { 559 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 560 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 561 parentWC.addChild(childWC, POSITION_TOP); 562 childWC.mVisibleRequested = true; 563 childWC.mFillsParent = true; 564 565 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 566 567 BLASTSyncEngine.TransactionReadyListener listener = mock( 568 BLASTSyncEngine.TransactionReadyListener.class); 569 570 // This is parallel, so it should ignore children 571 int sync1 = startSyncSet(bse, listener, true /* parallel */); 572 bse.addToSyncSet(sync1, parentWC); 573 bse.setReady(sync1); 574 575 int sync2 = startSyncSet(bse, listener, true /* parallel */); 576 bse.addToSyncSet(sync2, childWC); 577 bse.setReady(sync2); 578 579 childWC.onSyncFinishedDrawing(); 580 bse.onSurfacePlacement(); 581 582 // Sync2 should have run in parallel 583 verify(listener, times(1)).onTransactionReady(eq(sync2), any()); 584 verify(listener, times(0)).onTransactionReady(eq(sync1), any()); 585 586 parentWC.onSyncFinishedDrawing(); 587 bse.onSurfacePlacement(); 588 589 verify(listener, times(1)).onTransactionReady(eq(sync1), any()); 590 } 591 592 @Test testDependencyCycle()593 public void testDependencyCycle() { 594 TestWindowContainer parentWC = new TestWindowContainer(mWm, true /* waiter */); 595 TestWindowContainer childWC = new TestWindowContainer(mWm, true /* waiter */); 596 TestWindowContainer childWC2 = new TestWindowContainer(mWm, true /* waiter */); 597 TestWindowContainer childWC3 = new TestWindowContainer(mWm, true /* waiter */); 598 parentWC.addChild(childWC, POSITION_TOP); 599 childWC.mVisibleRequested = true; 600 childWC.mFillsParent = true; 601 childWC2.mVisibleRequested = true; 602 childWC2.mFillsParent = true; 603 childWC3.mVisibleRequested = true; 604 childWC3.mFillsParent = true; 605 606 final BLASTSyncEngine bse = createTestBLASTSyncEngine(); 607 608 BLASTSyncEngine.TransactionReadyListener listener = mock( 609 BLASTSyncEngine.TransactionReadyListener.class); 610 611 // This is non-parallel, so it is waiting on the child as-well 612 int sync1 = startSyncSet(bse, listener); 613 bse.addToSyncSet(sync1, parentWC); 614 bse.setReady(sync1); 615 616 // Sync 2 depends on sync1 AND childWC2 617 int sync2 = startSyncSet(bse, listener, true /* parallel */); 618 bse.addToSyncSet(sync2, childWC); 619 bse.addToSyncSet(sync2, childWC2); 620 bse.setReady(sync2); 621 622 // Sync 3 depends on sync2 AND childWC3 623 int sync3 = startSyncSet(bse, listener, true /* parallel */); 624 bse.addToSyncSet(sync3, childWC2); 625 bse.addToSyncSet(sync3, childWC3); 626 bse.setReady(sync3); 627 628 // Now make sync1 depend on WC3 (which would make it depend on sync3). This would form 629 // a cycle, so it should instead move childWC3 into sync1. 630 bse.addToSyncSet(sync1, childWC3); 631 632 // Sync3 should no-longer have childWC3 as a root-member since a window can currently only 633 // be directly watched by 1 syncgroup maximum (due to implementation of isSyncFinished). 634 assertFalse(bse.getSyncSet(sync3).mRootMembers.contains(childWC3)); 635 636 childWC3.onSyncFinishedDrawing(); 637 childWC2.onSyncFinishedDrawing(); 638 parentWC.onSyncFinishedDrawing(); 639 bse.onSurfacePlacement(); 640 641 // make sure sync3 hasn't run even though all its (original) members are ready 642 verify(listener, times(0)).onTransactionReady(anyInt(), any()); 643 644 // Now finish the last container and make sure everything finishes (didn't "deadlock" due 645 // to a dependency cycle. 646 childWC.onSyncFinishedDrawing(); 647 bse.onSurfacePlacement(); 648 649 InOrder readyOrder = Mockito.inOrder(listener); 650 readyOrder.verify(listener).onTransactionReady(eq(sync1), any()); 651 readyOrder.verify(listener).onTransactionReady(eq(sync2), any()); 652 readyOrder.verify(listener).onTransactionReady(eq(sync3), any()); 653 } 654 startSyncSet(BLASTSyncEngine engine, BLASTSyncEngine.TransactionReadyListener listener)655 static int startSyncSet(BLASTSyncEngine engine, 656 BLASTSyncEngine.TransactionReadyListener listener) { 657 return engine.startSyncSet(listener, BLAST_TIMEOUT_DURATION, "Test", false /* parallel */); 658 } 659 startSyncSet(BLASTSyncEngine engine, BLASTSyncEngine.TransactionReadyListener listener, boolean parallel)660 static int startSyncSet(BLASTSyncEngine engine, 661 BLASTSyncEngine.TransactionReadyListener listener, boolean parallel) { 662 return engine.startSyncSet(listener, BLAST_TIMEOUT_DURATION, "Test", parallel); 663 } 664 665 static class TestWindowContainer extends WindowContainer { 666 final boolean mWaiter; 667 boolean mVisibleRequested = true; 668 boolean mFillsParent = false; 669 TestWindowContainer(WindowManagerService wms, boolean waiter)670 TestWindowContainer(WindowManagerService wms, boolean waiter) { 671 super(wms); 672 mWaiter = waiter; 673 mDisplayContent = wms.getDefaultDisplayContentLocked(); 674 } 675 676 @Override prepareSync()677 boolean prepareSync() { 678 if (!super.prepareSync()) { 679 return false; 680 } 681 if (mWaiter) { 682 mSyncState = SYNC_STATE_WAITING_FOR_DRAW; 683 } 684 return true; 685 } 686 687 @Override createSurfaceControl(boolean force)688 void createSurfaceControl(boolean force) { 689 // nothing 690 } 691 692 @Override isVisibleRequested()693 boolean isVisibleRequested() { 694 return mVisibleRequested; 695 } 696 697 @Override fillsParent()698 boolean fillsParent() { 699 return mFillsParent; 700 } 701 } 702 } 703