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