xref: /aosp_15_r20/cts/tests/framework/base/windowmanager/src/android/server/wm/window/SnapshotTaskTests.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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 android.server.wm.window;
18 
19 import static android.server.wm.WindowManagerTestBase.startActivity;
20 import static android.view.WindowInsets.Type.captionBar;
21 import static android.view.WindowInsets.Type.systemBars;
22 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
23 
24 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
25 
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertTrue;
29 
30 import android.app.UiAutomation;
31 import android.content.ComponentName;
32 import android.graphics.Bitmap;
33 import android.graphics.Color;
34 import android.graphics.Insets;
35 import android.graphics.Rect;
36 import android.os.Bundle;
37 import android.server.wm.ActivityManagerTestBase;
38 import android.server.wm.WindowManagerState;
39 import android.server.wm.WindowManagerTestBase;
40 import android.view.SurfaceControl;
41 import android.view.View;
42 import android.view.ViewGroup;
43 import android.view.WindowInsets;
44 import android.view.WindowInsetsController;
45 import android.view.WindowManager;
46 import android.view.cts.surfacevalidator.BitmapPixelChecker;
47 import android.window.SplashScreen;
48 
49 import androidx.annotation.Nullable;
50 import androidx.test.platform.app.InstrumentationRegistry;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 
56 import java.util.concurrent.CountDownLatch;
57 import java.util.concurrent.TimeUnit;
58 
59 /** Build/Install/Run:
60  *     atest CtsWindowManagerDeviceWindow:SnapshotTaskTests
61  */
62 public class SnapshotTaskTests extends ActivityManagerTestBase {
63     private static final String TAG = "SnapshotTaskTests";
64     private static final ComponentName TEST_ACTIVITY = new ComponentName(
65             getInstrumentation().getContext(), TestActivity.class);
66 
67     private TestActivity mActivity;
68     private WindowManager mWindowManager;
69     private UiAutomation mUiAutomation;
70 
71     private static final int MATCHING_PIXEL_MISMATCH_ALLOWED = 100;
72 
73     @Before
setup()74     public void setup() throws Exception {
75         super.setUp();
76         mActivity = startActivity(TestActivity.class);
77 
78         mWindowManager = mActivity.getSystemService(WindowManager.class);
79         mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
80         mUiAutomation.adoptShellPermissionIdentity();
81 
82         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
83         mActivity.waitUntilReady();
84     }
85 
86     @After
cleanup()87     public void cleanup() {
88         mUiAutomation.dropShellPermissionIdentity();
89     }
90 
91     @Test
testSetDisablePreviewScreenshots()92     public void testSetDisablePreviewScreenshots() throws Exception {
93         final View decor = mActivity.getWindow().getDecorView();
94         final int captionBarHeight = decor.getRootWindowInsets().getInsets(captionBar()).top;
95 
96         BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.RED);
97 
98         int retries = 0;
99         boolean matchesPixels = false;
100         while (retries < 5) {
101             Bitmap bitmap = mWindowManager.snapshotTaskForRecents(mActivity.getTaskId());
102             Rect boundToCheck =  new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
103             // Even when the activity requests immersive mode, the bars may or may not be hidden
104             // depending on the form-factor.
105             boundToCheck.inset(mActivity.getSystemBarOverlaps());
106             if (bitmap != null) {
107                 int expectedMatching =
108                         boundToCheck.width() * boundToCheck.height()
109                                 - MATCHING_PIXEL_MISMATCH_ALLOWED
110                                 - (captionBarHeight * decor.getWidth());
111                 int matchingPixels = pixelChecker.getNumMatchingPixels(bitmap, boundToCheck);
112                 matchesPixels = matchingPixels >= expectedMatching;
113             }
114             if (matchesPixels) {
115                 break;
116             }
117             retries++;
118             Thread.sleep(1000);
119         }
120 
121         assertTrue(matchesPixels);
122 
123         mActivity.setRecentsScreenshotEnabled(false);
124 
125         retries = 0;
126         WindowManagerState.Activity activityContainer = null;
127         boolean enableScreenshot = true;
128         while (retries < 3) {
129             mWmState.computeState();
130             activityContainer = mWmState.getActivity(TEST_ACTIVITY);
131             if (activityContainer != null) {
132                 enableScreenshot = activityContainer.enableRecentsScreenshot();
133             }
134             if (enableScreenshot) {
135                 break;
136             }
137             retries++;
138             Thread.sleep(500);
139         }
140         assertFalse("Recents screenshots should be disabled", enableScreenshot);
141 
142         Bitmap bitmap = mWindowManager.snapshotTaskForRecents(mActivity.getTaskId());
143         assertNotNull(bitmap);
144         Rect boundToCheck = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
145         boundToCheck.inset(mActivity.getSystemBarOverlaps());
146         int matchingPixels = pixelChecker.getNumMatchingPixels(bitmap, boundToCheck);
147         assertTrue(
148                 "Expected <=" + MATCHING_PIXEL_MISMATCH_ALLOWED + " matched " + matchingPixels,
149                 matchingPixels <= MATCHING_PIXEL_MISMATCH_ALLOWED);
150     }
151 
152     public static class TestActivity extends WindowManagerTestBase.FocusableActivity {
153         private final CountDownLatch mReadyToStart = new CountDownLatch(3);
154         private Insets mSystemBarOverlaps = Insets.of(0, 0, 0, 0);
155 
156 
157         @Override
onEnterAnimationComplete()158         public void onEnterAnimationComplete() {
159             mReadyToStart.countDown();
160         }
161 
waitUntilReady()162         public void waitUntilReady() throws InterruptedException {
163             mReadyToStart.await(5, TimeUnit.SECONDS);
164         }
165 
166         @Override
onAttachedToWindow()167         public void onAttachedToWindow() {
168             super.onAttachedToWindow();
169 
170             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
171             t.addTransactionCommittedListener(getMainExecutor(),
172                     mReadyToStart::countDown);
173             getWindow().getDecorView().getRootSurfaceControl().applyTransactionOnDraw(t);
174         }
175 
176         @Override
onCreate(@ullable Bundle savedInstanceState)177         protected void onCreate(@Nullable Bundle savedInstanceState) {
178             super.onCreate(savedInstanceState);
179             View view = new View(this);
180             view.setBackgroundColor(Color.RED);
181             WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
182                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
183             setContentView(view, layoutParams);
184 
185             WindowInsetsController windowInsetsController = getWindow().getInsetsController();
186             windowInsetsController.hide(
187                     WindowInsets.Type.navigationBars() | WindowInsets.Type.statusBars());
188             WindowManager.LayoutParams params = getWindow().getAttributes();
189             params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
190             getWindow().setAttributes(params);
191             getWindow().setDecorFitsSystemWindows(false);
192 
193             SplashScreen splashscreen = getSplashScreen();
194             splashscreen.setOnExitAnimationListener(splashView -> {
195                 splashView.remove();
196                 mReadyToStart.countDown();
197             });
198 
199             getWindow().getDecorView().setOnApplyWindowInsetsListener((v, insets) -> {
200                 mSystemBarOverlaps = insets.getInsets(systemBars());
201                 return insets;
202             });
203         }
204 
getSystemBarOverlaps()205         public Insets getSystemBarOverlaps() {
206             return mSystemBarOverlaps;
207         }
208     }
209 }
210