• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.server.wm;
18 
19 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import android.app.Instrumentation;
26 import android.graphics.Bitmap;
27 import android.graphics.Color;
28 import android.graphics.Rect;
29 import android.os.RemoteException;
30 import android.platform.test.annotations.Presubmit;
31 import android.server.wm.scvh.IAttachEmbeddedWindow;
32 import android.server.wm.scvh.SurfaceSyncGroupActivity;
33 import android.util.Log;
34 import android.view.SurfaceControl;
35 import android.view.SurfaceControlViewHost.SurfacePackage;
36 import android.view.View;
37 import android.view.ViewTreeObserver;
38 import android.view.cts.surfacevalidator.BitmapPixelChecker;
39 import android.window.SurfaceSyncGroup;
40 
41 import androidx.test.platform.app.InstrumentationRegistry;
42 import androidx.test.rule.ActivityTestRule;
43 
44 import org.junit.Before;
45 import org.junit.Rule;
46 import org.junit.Test;
47 
48 import java.util.concurrent.CountDownLatch;
49 import java.util.concurrent.TimeUnit;
50 
51 @Presubmit
52 public class SurfaceSyncGroupTests {
53     private static final String TAG = "SurfaceSyncGroupTests";
54 
55     @Rule
56     public ActivityTestRule<SurfaceSyncGroupActivity> mActivityRule = new ActivityTestRule<>(
57             SurfaceSyncGroupActivity.class);
58 
59     private SurfaceSyncGroupActivity mActivity;
60 
61     Instrumentation mInstrumentation;
62 
63     @Before
setup()64     public void setup() {
65         mActivity = mActivityRule.getActivity();
66         mInstrumentation = InstrumentationRegistry.getInstrumentation();
67     }
68 
69     @Test
testProcessCrash()70     public void testProcessCrash() {
71         var data = mActivity.setupEmbeddedSCVH();
72         SurfacePackage surfacePackage = data.first;
73         IAttachEmbeddedWindow iAttachEmbeddedWindow = data.second;
74 
75         CountDownLatch finishedRunLatch = new CountDownLatch(1);
76 
77         mActivity.runOnUiThread(() -> {
78             SurfaceSyncGroup surfaceSyncGroup = new SurfaceSyncGroup(TAG);
79             surfaceSyncGroup.add(surfacePackage, () -> {
80                 try {
81                     iAttachEmbeddedWindow.sendCrash();
82                 } catch (RemoteException e) {
83                     Log.e(TAG, "Failed to send crash to embedded");
84                 }
85             });
86             surfaceSyncGroup.add(mActivity.getWindow().getRootSurfaceControl(),
87                     null /* runnable */);
88             // Add a transaction committed listener to make sure the transaction has been applied
89             // even though one of the processes involved crashed.
90             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
91             t.addTransactionCommittedListener(Runnable::run, finishedRunLatch::countDown);
92             surfaceSyncGroup.addTransaction(t);
93             surfaceSyncGroup.markSyncReady();
94         });
95 
96         try {
97             finishedRunLatch.await(5, TimeUnit.SECONDS);
98         } catch (InterruptedException e) {
99             Log.e(TAG, "Failed to wait for transaction committed callback");
100         }
101 
102         assertEquals("Failed to apply transaction for SurfaceSyncGroup", 0,
103                 finishedRunLatch.getCount());
104     }
105 
106     /**
107      * This test will ensure that if multiple SurfaceSyncGroups are crated for the same ViewRootImpl
108      * the SurfaceSyncGroups will maintain an order. The scenario that could occur is the following:
109      * 1. SSG1 is created that includes the target VRI. There could be other VRIs in SSG1
110      * 2. target VRI draws its frame and is ready, but SSG1 is still waiting on other things in the
111      * SSG
112      * 3. Another SSG2 is created for the target VRI. The second frame renders and is ready and this
113      * SSG has nothing else to wait on. SSG2 will apply at this point, even though SSG1 has not
114      * 4. Frame2 will get to SF first and possibly later Frame1 will get to SF when SSG1 completes.
115      * <p>
116      * This test forces that behavior by ensuring the first SSG does not complete until the second
117      * SSG gets its buffer and is considered complete. It waits a bit to ensure it had time to
118      * make it to SF first. Then later it marks the first SSG as ready so that buffer could get
119      * sent to SF.
120      * <p>
121      * With the fix in VRI, the second SSG will not complete until the first SSG has been submitted
122      * to SF, ensuring that the frames are submitted in order.
123      */
124     @Test
testOverlappingSyncsEnsureOrder()125     public void testOverlappingSyncsEnsureOrder() throws InterruptedException {
126         CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
127         CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
128         final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first");
129         final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
130 
131         View backgroundView = mActivity.getBackgroundView();
132         mActivity.runOnUiThread(() -> {
133             firstSsg.add(backgroundView.getRootSurfaceControl(),
134                     () -> backgroundView.setBackgroundColor(Color.RED));
135             addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
136         });
137 
138         assertTrue("Failed to draw two frames", secondDrawCompleteLatch.await(5, TimeUnit.SECONDS));
139 
140         // Add a bit of a delay to make sure the second frame could have been sent to SF
141         Thread.sleep(HW_TIMEOUT_MULTIPLIER * 32L);
142 
143         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
144         t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
145         firstSsg.addTransaction(t);
146         firstSsg.markSyncReady();
147 
148         assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
149                 bothSyncGroupsComplete.await(HW_TIMEOUT_MULTIPLIER * 5L, TimeUnit.SECONDS));
150 
151         validateScreenshot();
152     }
153 
addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup, CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete)154     private void addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup,
155             CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete) {
156         View backgroundView = mActivity.getBackgroundView();
157         ViewTreeObserver viewTreeObserver = backgroundView.getViewTreeObserver();
158         viewTreeObserver.registerFrameCommitCallback(() -> mActivity.runOnUiThread(() -> {
159             surfaceSyncGroup.add(backgroundView.getRootSurfaceControl(),
160                     () -> backgroundView.setBackgroundColor(Color.BLUE));
161             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
162             t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
163             surfaceSyncGroup.addTransaction(t);
164             surfaceSyncGroup.markSyncReady();
165 
166             viewTreeObserver.registerFrameCommitCallback(waitForSecondDraw::countDown);
167         }));
168     }
169 
validateScreenshot()170     private void validateScreenshot() {
171         Bitmap screenshot = mInstrumentation.getUiAutomation().takeScreenshot(
172                 mActivity.getWindow());
173         assertNotNull("Failed to generate a screenshot", screenshot);
174         Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
175         screenshot.recycle();
176 
177         BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.BLUE);
178         int halfWidth = swBitmap.getWidth() / 2;
179         int halfHeight = swBitmap.getHeight() / 2;
180         // We don't need to check all the pixels since we only care that at least some of them are
181         // blue. If the buffers were submitted out of order, all the pixels will be red.
182         Rect bounds = new Rect(halfWidth, halfHeight, halfWidth + 10, halfHeight + 10);
183         int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap, bounds);
184         assertEquals("Expected 100 received " + numMatchingPixels + " matching pixels", 100,
185                 numMatchingPixels);
186 
187         swBitmap.recycle();
188     }
189 }
190