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