• 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 com.android.server.wm;
18 
19 import static android.os.Build.HW_TIMEOUT_MULTIPLIER;
20 import static android.window.SurfaceSyncGroup.TRANSACTION_READY_TIMEOUT;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.app.Activity;
27 import android.app.Instrumentation;
28 import android.app.KeyguardManager;
29 import android.graphics.Bitmap;
30 import android.graphics.Color;
31 import android.graphics.PixelFormat;
32 import android.graphics.Rect;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.HandlerThread;
36 import android.platform.test.annotations.Presubmit;
37 import android.view.SurfaceControl;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.ViewTreeObserver;
41 import android.view.WindowManager;
42 import android.view.cts.surfacevalidator.BitmapPixelChecker;
43 import android.widget.FrameLayout;
44 import android.window.SurfaceSyncGroup;
45 
46 import androidx.annotation.Nullable;
47 import androidx.test.InstrumentationRegistry;
48 import androidx.test.rule.ActivityTestRule;
49 
50 import com.android.server.wm.utils.CommonUtils;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 
57 import java.util.concurrent.CountDownLatch;
58 import java.util.concurrent.TimeUnit;
59 
60 @Presubmit
61 public class SurfaceSyncGroupTests {
62     private static final String TAG = "SurfaceSyncGroupTests";
63 
64     private static final long TIMEOUT_S = HW_TIMEOUT_MULTIPLIER * 5L;
65 
66     @Rule
67     public ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
68             TestActivity.class);
69 
70     private TestActivity mActivity;
71 
72     Instrumentation mInstrumentation;
73 
74     private final HandlerThread mHandlerThread = new HandlerThread("applyTransaction");
75     private Handler mHandler;
76 
77     @Before
setup()78     public void setup() {
79         mActivity = mActivityRule.getActivity();
80         mInstrumentation = InstrumentationRegistry.getInstrumentation();
81         mHandlerThread.start();
82         mHandler = mHandlerThread.getThreadHandler();
83     }
84 
85     @After
tearDown()86     public void tearDown() {
87         mHandlerThread.quitSafely();
88         CommonUtils.waitUntilActivityRemoved(mActivity);
89     }
90 
91     @Test
testOverlappingSyncsEnsureOrder_WhenTimeout()92     public void testOverlappingSyncsEnsureOrder_WhenTimeout() throws InterruptedException {
93         WindowManager.LayoutParams params = new WindowManager.LayoutParams();
94         params.format = PixelFormat.TRANSLUCENT;
95 
96         CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
97         CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
98         final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first");
99         final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
100         final SurfaceSyncGroup infiniteSsg = new SurfaceSyncGroup(TAG + "-infinite");
101 
102         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
103         t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
104         firstSsg.addTransaction(t);
105 
106         View backgroundView = mActivity.getBackgroundView();
107         firstSsg.add(backgroundView.getRootSurfaceControl(),
108                 () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
109 
110         addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
111 
112         assertTrue("Failed to draw two frames",
113                 secondDrawCompleteLatch.await(TIMEOUT_S, TimeUnit.SECONDS));
114 
115         mHandler.postDelayed(() -> {
116             // Don't add a markSyncReady for the first sync group until after it's added to another
117             // SSG to ensure the timeout is longer than the second frame's timeout. The infinite SSG
118             // will never complete to ensure it reaches the timeout, but only after the second SSG
119             // had a chance to reach its timeout.
120             infiniteSsg.add(firstSsg, null /* runnable */);
121             firstSsg.markSyncReady();
122         }, 200);
123 
124         assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
125                 bothSyncGroupsComplete.await(TIMEOUT_S, TimeUnit.SECONDS));
126 
127         validateScreenshot();
128     }
129 
130     @Test
testOverlappingSyncsEnsureOrder_WhileHoldingTransaction()131     public void testOverlappingSyncsEnsureOrder_WhileHoldingTransaction()
132             throws InterruptedException {
133         WindowManager.LayoutParams params = new WindowManager.LayoutParams();
134         params.format = PixelFormat.TRANSLUCENT;
135 
136         CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
137         CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
138 
139         final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first",
140                 transaction -> mHandler.postDelayed(() -> {
141                     try {
142                         assertTrue("Failed to draw two frames",
143                                 secondDrawCompleteLatch.await(TIMEOUT_S, TimeUnit.SECONDS));
144                     } catch (InterruptedException e) {
145                         throw new RuntimeException(e);
146                     }
147                     transaction.apply();
148                 }, TRANSACTION_READY_TIMEOUT + 200));
149         final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
150 
151         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
152         t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
153         firstSsg.addTransaction(t);
154 
155         View backgroundView = mActivity.getBackgroundView();
156         firstSsg.add(backgroundView.getRootSurfaceControl(),
157                 () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
158         firstSsg.markSyncReady();
159 
160         addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
161 
162         assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
163                 bothSyncGroupsComplete.await(TIMEOUT_S, TimeUnit.SECONDS));
164 
165         validateScreenshot();
166     }
167 
addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup, CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete)168     private void addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup,
169             CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete) {
170         View backgroundView = mActivity.getBackgroundView();
171         ViewTreeObserver viewTreeObserver = backgroundView.getViewTreeObserver();
172         viewTreeObserver.registerFrameCommitCallback(() -> mHandler.post(() -> {
173             surfaceSyncGroup.add(backgroundView.getRootSurfaceControl(),
174                     () -> mActivity.runOnUiThread(
175                             () -> backgroundView.setBackgroundColor(Color.BLUE)));
176 
177             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
178             t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
179             surfaceSyncGroup.addTransaction(t);
180             surfaceSyncGroup.markSyncReady();
181             viewTreeObserver.registerFrameCommitCallback(waitForSecondDraw::countDown);
182         }));
183     }
184 
validateScreenshot()185     private void validateScreenshot() {
186         Bitmap screenshot = mInstrumentation.getUiAutomation().takeScreenshot(
187                 mActivity.getWindow());
188         assertNotNull("Failed to generate a screenshot", screenshot);
189         Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
190         screenshot.recycle();
191 
192         BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.BLUE);
193         int halfWidth = swBitmap.getWidth() / 2;
194         int halfHeight = swBitmap.getHeight() / 2;
195         // We don't need to check all the pixels since we only care that at least some of them are
196         // blue. If the buffers were submitted out of order, all the pixels will be red.
197         Rect bounds = new Rect(halfWidth, halfHeight, halfWidth + 10, halfHeight + 10);
198         int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap, bounds);
199         assertEquals("Expected 100 received " + numMatchingPixels + " matching pixels", 100,
200                 numMatchingPixels);
201 
202         swBitmap.recycle();
203     }
204 
205     public static class TestActivity extends Activity {
206         private ViewGroup mParentView;
207 
208         @Override
onCreate(@ullable Bundle savedInstanceState)209         public void onCreate(@Nullable Bundle savedInstanceState) {
210             super.onCreate(savedInstanceState);
211 
212 
213             mParentView = new FrameLayout(this);
214             setContentView(mParentView);
215 
216             KeyguardManager km = getSystemService(KeyguardManager.class);
217             km.requestDismissKeyguard(this, null);
218         }
219 
getBackgroundView()220         public View getBackgroundView() {
221             return mParentView;
222         }
223     }
224 }
225