• 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.view.surfacecontrol.cts;
18 
19 import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule;
20 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.fail;
26 
27 import android.app.Activity;
28 import android.graphics.Color;
29 import android.os.Binder;
30 import android.os.SystemClock;
31 import android.platform.test.annotations.Presubmit;
32 import android.platform.test.annotations.RequiresFlagsEnabled;
33 import android.platform.test.flag.junit.CheckFlagsRule;
34 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
35 import android.server.wm.CtsWindowInfoUtils;
36 import android.util.Log;
37 import android.view.SurfaceControl;
38 import android.view.SurfaceControlViewHost;
39 import android.view.SurfaceView;
40 import android.view.View;
41 import android.view.WindowManager;
42 import android.window.TrustedPresentationThresholds;
43 
44 import androidx.annotation.NonNull;
45 import androidx.test.ext.junit.rules.ActivityScenarioRule;
46 
47 import com.android.window.flags.Flags;
48 
49 import junit.framework.Assert;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.rules.TestName;
56 
57 import java.util.ArrayList;
58 import java.util.Collections;
59 import java.util.List;
60 import java.util.concurrent.CountDownLatch;
61 import java.util.concurrent.TimeUnit;
62 import java.util.function.Consumer;
63 
64 @Presubmit
65 public class TrustedPresentationListenerTest {
66     private static final String TAG = "TrustedPresentationListenerTest";
67     private static final int STABILITY_REQUIREMENT_MS = 500;
68     private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 4000L;
69 
70     private static final float FRACTION_VISIBLE = 0.1f;
71 
72 
73     private TrustedPresentationThresholds mThresholds = new TrustedPresentationThresholds(
74             1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS);
75 
76     @Rule
77     public TestName mName = new TestName();
78 
79     @Rule
80     public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule(
81             TestActivity.class);
82 
83     @Rule
84     public final CheckFlagsRule mCheckFlagsRule =
85             DeviceFlagsValueProvider.createCheckFlagsRule();
86 
87     private TestActivity mActivity;
88 
89     private SurfaceControlViewHost.SurfacePackage mSurfacePackage = null;
90 
91     @Before
setup()92     public void setup() {
93         mActivityRule.getScenario().onActivity(activity -> mActivity = activity);
94         mDefaultListener = new Listener(1);
95     }
96 
97     @After
tearDown()98     public void tearDown() {
99         if (mSurfacePackage != null) {
100             new SurfaceControl.Transaction()
101                     .reparent(mSurfacePackage.getSurfaceControl(), null).apply();
102             mSurfacePackage.release();
103         }
104     }
105 
106     private class Listener implements Consumer<Boolean> {
107         CountDownLatch mReceivedResultsLatch;
108         final List<Boolean> mResults = Collections.synchronizedList(new ArrayList<>());
109 
Listener(int numExpectedResults)110         Listener(int numExpectedResults) {
111             mReceivedResultsLatch = new CountDownLatch(numExpectedResults);
112         }
113 
114         @Override
accept(Boolean inTrustedPresentationState)115         public void accept(Boolean inTrustedPresentationState) {
116             Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState);
117             mResults.add(inTrustedPresentationState);
118             mReceivedResultsLatch.countDown();
119         }
120 
waitForResults()121         void waitForResults() {
122             if (!TrustedPresentationListenerTest.wait(mReceivedResultsLatch, WAIT_TIME_MS)) {
123                 try {
124                     CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName());
125                 } catch (InterruptedException e) {
126                     Log.d(TAG, "Couldn't dump windows", e);
127                 }
128                 Assert.fail(
129                         "Timed out waiting for results mReceivedResultsLatch.count="
130                                 + mReceivedResultsLatch.getCount()
131                                 + " mResults="
132                                 + mResults);
133             }
134         }
135     }
136 
137     private Listener mDefaultListener;
138 
139     @Test
140     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testAddTrustedPresentationListenerOnWindow()141     public void testAddTrustedPresentationListenerOnWindow() {
142         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
143         windowManager.registerTrustedPresentationListener(
144                 mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, Runnable::run,
145                 mDefaultListener);
146         assertResults(mDefaultListener, List.of(true));
147     }
148 
149     @Test
150     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testRemoveTrustedPresentationListenerOnWindow()151     public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException {
152         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
153         windowManager.registerTrustedPresentationListener(
154                 mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, Runnable::run,
155                 mDefaultListener);
156         assertResults(mDefaultListener, List.of(true));
157         // reset the latch
158         mDefaultListener.mReceivedResultsLatch = new CountDownLatch(1);
159 
160         windowManager.unregisterTrustedPresentationListener(mDefaultListener);
161         // Ensure we waited the full time and never received a notify on the result from the
162         // callback.
163         assertFalse(
164                 "Should never have received a callback",
165                 wait(mDefaultListener.mReceivedResultsLatch, WAIT_TIME_MS));
166         // Ensure we waited the full time and never received a notify on the result from the
167         // callback.
168         // results shouldn't have changed.
169         assertEquals(mDefaultListener.mResults, List.of(true));
170     }
171 
172     @Test
173     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testRemovingUnknownListenerIsANoop()174     public void testRemovingUnknownListenerIsANoop() {
175         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
176         assertNotNull(windowManager);
177         windowManager.unregisterTrustedPresentationListener(mDefaultListener);
178     }
179 
180     @Test
181     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testAddDuplicateListenerUpdatesThresholds()182     public void testAddDuplicateListenerUpdatesThresholds() throws InterruptedException {
183         Binder nonExistentWindow = new Binder();
184         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
185         windowManager.registerTrustedPresentationListener(
186                 nonExistentWindow, mThresholds,
187                 Runnable::run, mDefaultListener);
188 
189         // Ensure we waited the full time and never received a notify on the result from the
190         // callback.
191         assertFalse(
192                 "Should never have received a callback",
193                 wait(mDefaultListener.mReceivedResultsLatch, WAIT_TIME_MS));
194 
195         windowManager.registerTrustedPresentationListener(
196                 mActivity.getWindow().getDecorView().getWindowToken(), mThresholds,
197                 Runnable::run, mDefaultListener);
198         assertResults(mDefaultListener, List.of(true));
199     }
200 
201     @Test
202     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testAddDuplicateThresholds()203     public void testAddDuplicateThresholds() {
204         var listener1 = new Listener(1 /*numExpectedResults*/);
205         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
206         windowManager.registerTrustedPresentationListener(
207                 mActivity.getWindow().getDecorView().getWindowToken(),
208                 mThresholds,
209                 Runnable::run,
210                 listener1);
211 
212         var listener2 = new Listener(1 /*numExpectedResults*/);
213         windowManager.registerTrustedPresentationListener(
214                 mActivity.getWindow().getDecorView().getWindowToken(),
215                 mThresholds,
216                 Runnable::run,
217                 listener2);
218 
219         assertResults(listener1, List.of(true));
220         assertResults(listener2, List.of(true));
221     }
222 
waitForViewAttach(View view)223     private void waitForViewAttach(View view) {
224         final CountDownLatch viewAttached = new CountDownLatch(1);
225         view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
226             @Override
227             public void onViewAttachedToWindow(@NonNull View v) {
228                 viewAttached.countDown();
229             }
230 
231             @Override
232             public void onViewDetachedFromWindow(@NonNull View v) {
233 
234             }
235         });
236         try {
237             viewAttached.await(2000, TimeUnit.MILLISECONDS);
238         } catch (InterruptedException e) {
239             throw new RuntimeException(e);
240         }
241         if (!wait(viewAttached, 2000 /* waitTimeMs */)) {
242             fail("Couldn't attach view=" + view);
243         }
244     }
245 
246     @Test
247     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testAddListenerToScvh()248     public void testAddListenerToScvh() {
249         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
250         var hostSurfaceView = new SurfaceView(mActivity);
251         hostSurfaceView.setZOrderOnTop(true);
252         var embeddedView = new View(mActivity);
253         embeddedView.setBackgroundColor(Color.GREEN);
254         mActivityRule.getScenario().onActivity(activity -> {
255             activity.setContentView(hostSurfaceView);
256             var scvh = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
257                     hostSurfaceView.getHostToken());
258             mSurfacePackage = scvh.getSurfacePackage();
259             scvh.setView(embeddedView, mActivity.getWindow().getDecorView().getWidth(),
260                     mActivity.getWindow().getDecorView().getHeight());
261             hostSurfaceView.setChildSurfacePackage(mSurfacePackage);
262         });
263 
264         waitForViewAttach(embeddedView);
265         windowManager.registerTrustedPresentationListener(
266                 embeddedView.getWindowToken(), mThresholds, Runnable::run, mDefaultListener);
267 
268         assertResults(mDefaultListener, List.of(true));
269     }
270 
271     @Test
272     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testTrustedPresentationThresholdGetters()273     public void testTrustedPresentationThresholdGetters() {
274         float alpha = 0.5f;
275         float fractionRendered = 0.9f;
276         int stabilityRequirementMs = 20;
277         TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds(alpha,
278                 fractionRendered, stabilityRequirementMs);
279         Assert.assertEquals(alpha, thresholds.getMinAlpha());
280         Assert.assertEquals(fractionRendered, thresholds.getMinFractionRendered());
281         Assert.assertEquals(stabilityRequirementMs, thresholds.getStabilityRequirementMillis());
282     }
283 
284     @Test
285     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testEquals()286     public void testEquals() {
287         float alpha = 0.5f;
288         float fractionRendered = 0.9f;
289         int stabilityRequirementMs = 20;
290         TrustedPresentationThresholds thresholdsA = new TrustedPresentationThresholds(alpha,
291                 fractionRendered, stabilityRequirementMs);
292         TrustedPresentationThresholds thresholdsB =
293                 new TrustedPresentationThresholds(alpha, fractionRendered, stabilityRequirementMs);
294         Assert.assertEquals(thresholdsA, thresholdsB);
295     }
296 
297     @Test
298     @RequiresFlagsEnabled(Flags.FLAG_TRUSTED_PRESENTATION_LISTENER_FOR_WINDOW)
testInvisibleWindowsDoesNotOcclude()299     public void testInvisibleWindowsDoesNotOcclude() {
300         WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
301         var hostSurfaceView = new SurfaceView(mActivity);
302         hostSurfaceView.setZOrderOnTop(true);
303         var embeddedView = new View(mActivity);
304         embeddedView.setBackgroundColor(Color.GREEN);
305         mActivityRule
306                 .getScenario()
307                 .onActivity(
308                         activity -> {
309                             activity.setContentView(hostSurfaceView);
310                             var scvh =
311                                     new SurfaceControlViewHost(
312                                             mActivity,
313                                             mActivity.getDisplay(),
314                                             hostSurfaceView.getHostToken());
315                             mSurfacePackage = scvh.getSurfacePackage();
316                             scvh.setView(
317                                     embeddedView,
318                                     mActivity.getWindow().getDecorView().getWidth(),
319                                     mActivity.getWindow().getDecorView().getHeight());
320                             hostSurfaceView.setChildSurfacePackage(mSurfacePackage);
321                         });
322 
323         waitForViewAttach(embeddedView);
324         Log.d(TAG, "Embedded window added");
325 
326         // at this point the main window should be occluded.
327 
328         // make the occluding surface invisible
329         new SurfaceControl.Transaction().setAlpha(mSurfacePackage.getSurfaceControl(), 0f).apply();
330 
331         var listener = new Listener(1 /*numExpectedResults*/);
332         windowManager.registerTrustedPresentationListener(
333                 mActivity.getWindow().getDecorView().getWindowToken(),
334                 mThresholds,
335                 Runnable::run,
336                 listener);
337         assertResults(listener, List.of(true));
338     }
339 
wait(CountDownLatch latch, long waitTimeMs)340     static boolean wait(CountDownLatch latch, long waitTimeMs) {
341         while (true) {
342             long now = SystemClock.uptimeMillis();
343             try {
344                 return latch.await(waitTimeMs, TimeUnit.MILLISECONDS);
345             } catch (InterruptedException e) {
346                 long elapsedTime = SystemClock.uptimeMillis() - now;
347                 waitTimeMs = Math.max(0, waitTimeMs - elapsedTime);
348             }
349         }
350 
351     }
352 
assertResults(Listener listener, List<Boolean> expectedResults)353     private void assertResults(Listener listener, List<Boolean> expectedResults) {
354         listener.waitForResults();
355         assertEquals(expectedResults.toArray(), listener.mResults.toArray());
356     }
357 
358     public static class TestActivity extends Activity {
359     }
360 }
361