1 /* 2 * Copyright 2024 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.input.cts_root 18 19 import android.cts.input.EventVerifier 20 import android.graphics.Bitmap 21 import android.graphics.Color 22 import android.os.SystemProperties 23 import android.view.MotionEvent 24 import android.view.WindowManager 25 import android.virtualdevice.cts.common.VirtualDeviceRule 26 import androidx.test.filters.MediumTest 27 import androidx.test.platform.app.InstrumentationRegistry 28 import com.android.cts.input.CaptureEventActivity 29 import com.android.cts.input.DefaultPointerSpeedRule 30 import com.android.cts.input.TestPointerDevice 31 import com.android.cts.input.VirtualDisplayActivityScenario 32 import com.android.cts.input.inputeventmatchers.withMotionAction 33 import com.android.xts.root.annotations.RequireAdbRoot 34 import org.junit.After 35 import org.junit.Before 36 import org.junit.Ignore 37 import org.junit.Rule 38 import org.junit.Test 39 import org.junit.rules.TestName 40 import org.junit.runner.RunWith 41 import org.junit.runners.Parameterized 42 import org.junit.runners.Parameterized.Parameter 43 import platform.test.screenshot.GoldenPathManager 44 import platform.test.screenshot.PathConfig 45 import platform.test.screenshot.ScreenshotTestRule 46 import platform.test.screenshot.assertAgainstGolden 47 import platform.test.screenshot.matchers.AlmostPerfectMatcher 48 import platform.test.screenshot.matchers.BitmapMatcher 49 import kotlin.test.assertNotNull 50 51 /** 52 * End-to-end tests for the hiding pointer icons of screenshots of secure displays 53 * 54 * We use a secure virtual display to launch the test activity, and use virtual Input devices to 55 * move the pointer for it to show up. We then take a screenshot of the display to ensure the icon 56 * does not shows up on screenshot. We use the virtual display to be able to precisely compare the 57 * screenshots across devices of various form factors and sizes. 58 * 59 * Following tests must be run as root as they require CAPTURE_SECURE_VIDEO_OUTPUT permission 60 * override which can only be done by root. 61 */ 62 @MediumTest 63 @RunWith(Parameterized::class) 64 @RequireAdbRoot 65 class HidePointerIconOnSecureWindowScreenshotTest { 66 private lateinit var activity: CaptureEventActivity 67 private lateinit var verifier: EventVerifier 68 private lateinit var exactScreenshotMatcher: BitmapMatcher 69 70 @get:Rule 71 val testName = TestName() 72 @get:Rule 73 val virtualDeviceRule = VirtualDeviceRule.createDefault()!! 74 // TODO(b/366492484): Remove reliance on VDM. 75 @get:Rule 76 val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>( 77 testName, 78 useSecureDisplay = true, 79 virtualDeviceRule = virtualDeviceRule 80 ) 81 @get:Rule 82 val defaultPointerSpeedRule = DefaultPointerSpeedRule() 83 @get:Rule 84 val screenshotRule = ScreenshotTestRule(GoldenPathManager( 85 InstrumentationRegistry.getInstrumentation().context, 86 ASSETS_PATH, 87 TEST_OUTPUT_PATH, 88 PathConfig() 89 ), disableIconPool = false) 90 91 @Parameter(0) 92 lateinit var device: TestPointerDevice 93 94 @Before setUpnull95 fun setUp() { 96 activity = virtualDisplayRule.activity 97 activity.runOnUiThread { 98 activity.actionBar?.hide() 99 activity.window.decorView.rootView.setBackgroundColor(Color.WHITE) 100 activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) 101 } 102 103 device.setUp( 104 virtualDeviceRule.defaultVirtualDevice, 105 virtualDisplayRule.virtualDisplay.display, 106 ) 107 108 verifier = EventVerifier(activity::getInputEvent) 109 110 exactScreenshotMatcher = 111 AlmostPerfectMatcher(acceptableThresholdCount = MAX_PIXELS_DIFFERENT) 112 } 113 114 @After tearDownnull115 fun tearDown() { 116 device.tearDown() 117 } 118 119 @Ignore("b/366475909") 120 @Test testHidePointerIconOnSecureWindowScreenshotnull121 fun testHidePointerIconOnSecureWindowScreenshot() { 122 device.hoverMove(1, 1) 123 verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_HOVER_ENTER)) 124 waitForPointerIconUpdate() 125 126 assertScreenshotsMatch() 127 } 128 getActualScreenshotnull129 private fun getActualScreenshot(): Bitmap { 130 val actualBitmap: Bitmap? = virtualDisplayRule.getScreenshot() 131 assertNotNull(actualBitmap, "Screenshot is null.") 132 return actualBitmap 133 } 134 assertScreenshotsMatchnull135 private fun assertScreenshotsMatch() { 136 getActualScreenshot().assertAgainstGolden( 137 screenshotRule, 138 getParameterizedExpectedScreenshotName(), 139 exactScreenshotMatcher 140 ) 141 } 142 getParameterizedExpectedScreenshotNamenull143 private fun getParameterizedExpectedScreenshotName(): String { 144 // Replace illegal characters '[' and ']' in expected screenshot name with underscores. 145 return "${testName.methodName}expected".replace("""\[|\]""".toRegex(), "_") 146 } 147 148 // We don't have a way to synchronously know when the requested pointer icon has been drawn 149 // to the display, so wait some time (at least one display frame) for the icon to propagate. waitForPointerIconUpdatenull150 private fun waitForPointerIconUpdate() = Thread.sleep(500L * HW_TIMEOUT_MULTIPLIER) 151 152 companion object { 153 const val MAX_PIXELS_DIFFERENT = 5 154 const val ASSETS_PATH = "tests/input/assets" 155 val TEST_OUTPUT_PATH = 156 "/sdcard/Download/CtsInputRootTestCases/" + 157 HidePointerIconOnSecureWindowScreenshotTest::class.java.simpleName 158 val HW_TIMEOUT_MULTIPLIER = SystemProperties.getInt("ro.hw_timeout_multiplier", 1); 159 160 @JvmStatic 161 @Parameterized.Parameters(name = "{0}") 162 fun data(): Iterable<Any> = 163 listOf(TestPointerDevice.MOUSE, TestPointerDevice.DRAWING_TABLET) 164 } 165 } 166