1 /* 2 * Copyright (C) 2022 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotEquals; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.verify; 26 27 import android.platform.test.annotations.Presubmit; 28 import android.view.SurfaceControl; 29 import android.window.SurfaceSyncGroup; 30 31 import androidx.test.filters.SmallTest; 32 33 import com.android.server.testutils.StubTransaction; 34 35 import org.junit.After; 36 import org.junit.Before; 37 import org.junit.Test; 38 39 import java.util.concurrent.CountDownLatch; 40 import java.util.concurrent.Executor; 41 import java.util.concurrent.TimeUnit; 42 43 @SmallTest 44 @Presubmit 45 public class SurfaceSyncGroupTest { 46 private static final String TAG = "SurfaceSyncGroupTest"; 47 private static final int TIMEOUT_MS = 100; 48 49 private final Executor mExecutor = Runnable::run; 50 51 @Before setup()52 public void setup() { 53 SurfaceSyncGroup.setTransactionFactory(StubTransaction::new); 54 } 55 56 @After tearDown()57 public void tearDown() { 58 SurfaceSyncGroup.setTransactionFactory(SurfaceControl.Transaction::new); 59 } 60 61 @Test testSyncOne()62 public void testSyncOne() throws InterruptedException { 63 final CountDownLatch finishedLatch = new CountDownLatch(1); 64 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 65 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 66 SurfaceSyncGroup syncTarget = new SurfaceSyncGroup("FakeSyncTarget"); 67 syncGroup.add(syncTarget, null /* runnable */); 68 syncGroup.markSyncReady(); 69 70 syncTarget.markSyncReady(); 71 72 finishedLatch.await(5, TimeUnit.SECONDS); 73 assertEquals(0, finishedLatch.getCount()); 74 } 75 76 @Test testSyncMultiple()77 public void testSyncMultiple() throws InterruptedException { 78 final CountDownLatch finishedLatch = new CountDownLatch(1); 79 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 80 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 81 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 82 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 83 SurfaceSyncGroup syncTarget3 = new SurfaceSyncGroup("FakeSyncTarget3"); 84 85 syncGroup.add(syncTarget1, null /* runnable */); 86 syncGroup.add(syncTarget2, null /* runnable */); 87 syncGroup.add(syncTarget3, null /* runnable */); 88 syncGroup.markSyncReady(); 89 90 syncTarget1.markSyncReady(); 91 assertNotEquals(0, finishedLatch.getCount()); 92 93 syncTarget3.markSyncReady(); 94 assertNotEquals(0, finishedLatch.getCount()); 95 96 syncTarget2.markSyncReady(); 97 98 finishedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 99 assertEquals(0, finishedLatch.getCount()); 100 } 101 102 @Test testAddSyncWhenSyncComplete()103 public void testAddSyncWhenSyncComplete() { 104 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 105 106 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 107 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 108 109 assertTrue(syncGroup.add(syncTarget1, null /* runnable */)); 110 syncGroup.markSyncReady(); 111 // Adding to a sync that has been completed is also invalid since the sync id has been 112 // cleared. 113 assertFalse(syncGroup.add(syncTarget2, null /* runnable */)); 114 } 115 116 @Test testMultipleSyncGroups()117 public void testMultipleSyncGroups() throws InterruptedException { 118 final CountDownLatch finishedLatch1 = new CountDownLatch(1); 119 final CountDownLatch finishedLatch2 = new CountDownLatch(1); 120 SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(TAG); 121 SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(TAG); 122 123 syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown); 124 syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown); 125 126 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 127 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 128 129 assertTrue(syncGroup1.add(syncTarget1, null /* runnable */)); 130 assertTrue(syncGroup2.add(syncTarget2, null /* runnable */)); 131 syncGroup1.markSyncReady(); 132 syncGroup2.markSyncReady(); 133 134 syncTarget1.markSyncReady(); 135 136 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 137 assertEquals(0, finishedLatch1.getCount()); 138 assertNotEquals(0, finishedLatch2.getCount()); 139 140 syncTarget2.markSyncReady(); 141 142 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 143 assertEquals(0, finishedLatch2.getCount()); 144 } 145 146 @Test testAddSyncGroup()147 public void testAddSyncGroup() throws InterruptedException { 148 final CountDownLatch finishedLatch1 = new CountDownLatch(1); 149 final CountDownLatch finishedLatch2 = new CountDownLatch(1); 150 SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(TAG); 151 SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(TAG); 152 153 syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown); 154 syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown); 155 156 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 157 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 158 159 assertTrue(syncGroup1.add(syncTarget1, null /* runnable */)); 160 assertTrue(syncGroup2.add(syncTarget2, null /* runnable */)); 161 syncGroup1.markSyncReady(); 162 syncGroup2.add(syncGroup1, null /* runnable */); 163 syncGroup2.markSyncReady(); 164 165 // Finish syncTarget2 first to test that the syncGroup is not complete until the merged sync 166 // is also done. 167 syncTarget2.markSyncReady(); 168 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 169 // Sync did not complete yet 170 assertNotEquals(0, finishedLatch2.getCount()); 171 172 syncTarget1.markSyncReady(); 173 174 // The first sync will still get a callback when it's sync requirements are done. 175 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 176 assertEquals(0, finishedLatch1.getCount()); 177 178 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 179 assertEquals(0, finishedLatch2.getCount()); 180 } 181 182 @Test testAddSyncAlreadyComplete()183 public void testAddSyncAlreadyComplete() throws InterruptedException { 184 final CountDownLatch finishedLatch1 = new CountDownLatch(1); 185 final CountDownLatch finishedLatch2 = new CountDownLatch(1); 186 SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(TAG); 187 SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(TAG); 188 189 syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown); 190 syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown); 191 192 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 193 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 194 195 assertTrue(syncGroup1.add(syncTarget1, null /* runnable */)); 196 assertTrue(syncGroup2.add(syncTarget2, null /* runnable */)); 197 syncGroup1.markSyncReady(); 198 syncTarget1.markSyncReady(); 199 200 // The first sync will still get a callback when it's sync requirements are done. 201 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 202 assertEquals(0, finishedLatch1.getCount()); 203 204 syncGroup2.add(syncGroup1, null /* runnable */); 205 syncGroup2.markSyncReady(); 206 syncTarget2.markSyncReady(); 207 208 // Verify that the second sync will receive complete since the merged sync was already 209 // completed before the merge. 210 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 211 assertEquals(0, finishedLatch2.getCount()); 212 } 213 214 @Test testAddSyncAlreadyInASync_NewSyncReadyFirst()215 public void testAddSyncAlreadyInASync_NewSyncReadyFirst() throws InterruptedException { 216 final CountDownLatch finishedLatch1 = new CountDownLatch(1); 217 final CountDownLatch finishedLatch2 = new CountDownLatch(1); 218 SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(TAG); 219 SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(TAG); 220 221 syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown); 222 syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown); 223 224 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 225 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 226 SurfaceSyncGroup syncTarget3 = new SurfaceSyncGroup("FakeSyncTarget3"); 227 228 assertTrue(syncGroup1.add(syncTarget1, null /* runnable */)); 229 assertTrue(syncGroup1.add(syncTarget2, null /* runnable */)); 230 231 // Add syncTarget1 to syncGroup2 so it forces syncGroup1 into syncGroup2 232 assertTrue(syncGroup2.add(syncTarget1, null /* runnable */)); 233 assertTrue(syncGroup2.add(syncTarget3, null /* runnable */)); 234 235 syncGroup1.markSyncReady(); 236 syncGroup2.markSyncReady(); 237 238 // Make target1 and target3 ready, but not target2. SyncGroup2 should not be ready since 239 // SyncGroup2 also waits for all of SyncGroup1 to finish, which includes target2 240 syncTarget1.markSyncReady(); 241 syncTarget3.markSyncReady(); 242 243 // Neither SyncGroup will be ready. 244 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 245 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 246 247 assertEquals(1, finishedLatch1.getCount()); 248 assertEquals(1, finishedLatch2.getCount()); 249 250 syncTarget2.markSyncReady(); 251 252 // Both sync groups should be ready after target2 completed. 253 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 254 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 255 assertEquals(0, finishedLatch1.getCount()); 256 assertEquals(0, finishedLatch2.getCount()); 257 } 258 259 @Test testAddSyncAlreadyInASync_OldSyncFinishesFirst()260 public void testAddSyncAlreadyInASync_OldSyncFinishesFirst() throws InterruptedException { 261 final CountDownLatch finishedLatch1 = new CountDownLatch(1); 262 final CountDownLatch finishedLatch2 = new CountDownLatch(1); 263 SurfaceSyncGroup syncGroup1 = new SurfaceSyncGroup(TAG); 264 SurfaceSyncGroup syncGroup2 = new SurfaceSyncGroup(TAG); 265 266 syncGroup1.addSyncCompleteCallback(mExecutor, finishedLatch1::countDown); 267 syncGroup2.addSyncCompleteCallback(mExecutor, finishedLatch2::countDown); 268 269 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 270 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 271 SurfaceSyncGroup syncTarget3 = new SurfaceSyncGroup("FakeSyncTarget3"); 272 273 assertTrue(syncGroup1.add(syncTarget1, null /* runnable */)); 274 assertTrue(syncGroup1.add(syncTarget2, null /* runnable */)); 275 syncTarget2.markSyncReady(); 276 277 // Add syncTarget1 to syncGroup2 so it forces syncGroup1 into syncGroup2 278 assertTrue(syncGroup2.add(syncTarget1, null /* runnable */)); 279 assertTrue(syncGroup2.add(syncTarget3, null /* runnable */)); 280 281 syncGroup1.markSyncReady(); 282 syncGroup2.markSyncReady(); 283 284 syncTarget1.markSyncReady(); 285 286 // Only SyncGroup1 will be ready, but SyncGroup2 still needs its own targets to be ready. 287 finishedLatch1.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 288 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 289 290 assertEquals(0, finishedLatch1.getCount()); 291 assertEquals(1, finishedLatch2.getCount()); 292 293 syncTarget3.markSyncReady(); 294 295 // SyncGroup2 is finished after target3 completed. 296 finishedLatch2.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 297 assertEquals(0, finishedLatch2.getCount()); 298 } 299 300 @Test testParentSyncGroupMerge_true()301 public void testParentSyncGroupMerge_true() { 302 // Temporarily set a new transaction factory so it will return the stub transaction for 303 // the sync group. 304 SurfaceControl.Transaction parentTransaction = spy(new StubTransaction()); 305 SurfaceSyncGroup.setTransactionFactory(() -> parentTransaction); 306 307 final CountDownLatch finishedLatch = new CountDownLatch(1); 308 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 309 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 310 311 SurfaceControl.Transaction targetTransaction = spy(new StubTransaction()); 312 SurfaceSyncGroup.setTransactionFactory(() -> targetTransaction); 313 314 SurfaceSyncGroup syncTarget = new SurfaceSyncGroup("FakeSyncTarget"); 315 assertTrue( 316 syncGroup.add(syncTarget.mISurfaceSyncGroup, true /* parentSyncGroupMerge */, 317 null /* runnable */)); 318 syncTarget.markSyncReady(); 319 320 // When parentSyncGroupMerge is true, the transaction passed in merges the main SyncGroup 321 // transaction first because it knows the previous parentSyncGroup is older so it should 322 // be overwritten by anything newer. 323 verify(targetTransaction).merge(parentTransaction); 324 verify(parentTransaction).merge(targetTransaction); 325 } 326 327 @Test testParentSyncGroupMerge_false()328 public void testParentSyncGroupMerge_false() { 329 // Temporarily set a new transaction factory so it will return the stub transaction for 330 // the sync group. 331 SurfaceControl.Transaction parentTransaction = spy(new StubTransaction()); 332 SurfaceSyncGroup.setTransactionFactory(() -> parentTransaction); 333 334 final CountDownLatch finishedLatch = new CountDownLatch(1); 335 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 336 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 337 338 SurfaceControl.Transaction targetTransaction = spy(new StubTransaction()); 339 SurfaceSyncGroup.setTransactionFactory(() -> targetTransaction); 340 341 SurfaceSyncGroup syncTarget = new SurfaceSyncGroup("FakeSyncTarget"); 342 assertTrue(syncGroup.add(syncTarget, null /* runnable */)); 343 syncTarget.markSyncReady(); 344 345 // When parentSyncGroupMerge is false, the transaction passed in should not merge 346 // the main SyncGroup since we don't need to change the transaction order 347 verify(targetTransaction, never()).merge(parentTransaction); 348 verify(parentTransaction).merge(targetTransaction); 349 } 350 351 @Test testAddToSameParentNoCrash()352 public void testAddToSameParentNoCrash() { 353 final CountDownLatch finishedLatch = new CountDownLatch(1); 354 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 355 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 356 SurfaceSyncGroup syncTarget = new SurfaceSyncGroup("FakeSyncTarget"); 357 syncGroup.add(syncTarget, null /* runnable */); 358 // Add the syncTarget to the same syncGroup and ensure it doesn't crash. 359 syncGroup.add(syncTarget, null /* runnable */); 360 syncGroup.markSyncReady(); 361 362 syncTarget.markSyncReady(); 363 364 try { 365 finishedLatch.await(5, TimeUnit.SECONDS); 366 } catch (InterruptedException e) { 367 throw new RuntimeException(e); 368 } 369 370 assertEquals(0, finishedLatch.getCount()); 371 } 372 testSurfaceSyncGroupTimeout()373 public void testSurfaceSyncGroupTimeout() throws InterruptedException { 374 final CountDownLatch finishedLatch = new CountDownLatch(1); 375 SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG); 376 syncGroup.addSyncCompleteCallback(mExecutor, finishedLatch::countDown); 377 SurfaceSyncGroup syncTarget1 = new SurfaceSyncGroup("FakeSyncTarget1"); 378 SurfaceSyncGroup syncTarget2 = new SurfaceSyncGroup("FakeSyncTarget2"); 379 380 syncGroup.add(syncTarget1, null /* runnable */); 381 syncGroup.add(syncTarget2, null /* runnable */); 382 syncGroup.markSyncReady(); 383 384 syncTarget1.markSyncReady(); 385 assertNotEquals(0, finishedLatch.getCount()); 386 387 // Never finish syncTarget2 so it forces the timeout. Timeout is 1 second so wait a little 388 // over 1 second to make sure it completes. 389 finishedLatch.await(1100, TimeUnit.MILLISECONDS); 390 assertEquals(0, finishedLatch.getCount()); 391 } 392 } 393