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