blob: 471084b81653ffc6aca79918e2d6dc3155fa65fc [file] [log] [blame]
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.input.cts_root
import android.cts.input.EventVerifier
import android.graphics.Bitmap
import android.graphics.Color
import android.os.SystemProperties
import android.platform.test.annotations.EnableFlags
import android.view.MotionEvent
import android.view.WindowManager
import android.virtualdevice.cts.common.FakeAssociationRule
import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.cts.input.DefaultPointerSpeedRule
import com.android.cts.input.TestPointerDevice
import com.android.cts.input.VirtualDisplayActivityScenario
import com.android.cts.input.inputeventmatchers.withMotionAction
import com.android.input.flags.Flags
import com.android.xts.root.annotations.RequireAdbRoot
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestName
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameter
import platform.test.screenshot.GoldenPathManager
import platform.test.screenshot.PathConfig
import platform.test.screenshot.ScreenshotTestRule
import platform.test.screenshot.assertAgainstGolden
import platform.test.screenshot.matchers.AlmostPerfectMatcher
import platform.test.screenshot.matchers.BitmapMatcher
import kotlin.test.assertNotNull
import org.junit.Ignore
/**
* End-to-end tests for the hiding pointer icons of screenshots of secure displays
*
* We use a secure virtual display to launch the test activity, and use virtual Input devices to
* move the pointer for it to show up. We then take a screenshot of the display to ensure the icon
* does not shows up on screenshot. We use the virtual display to be able to precisely compare the
* screenshots across devices of various form factors and sizes.
*
* Following tests must be run as root as they require CAPTURE_SECURE_VIDEO_OUTPUT permission
* override which can only be done by root.
*/
@MediumTest
@RunWith(Parameterized::class)
@RequireAdbRoot
class HidePointerIconOnSecureWindowScreenshotTest {
private lateinit var activity: CaptureEventActivity
private lateinit var verifier: EventVerifier
private lateinit var exactScreenshotMatcher: BitmapMatcher
@get:Rule
val testName = TestName()
@get:Rule
val virtualDisplayRule = VirtualDisplayActivityScenario.Rule<CaptureEventActivity>(
testName,
useSecureDisplay = true,
)
@get:Rule
val fakeAssociationRule = FakeAssociationRule()
@get:Rule
val defaultPointerSpeedRule = DefaultPointerSpeedRule()
@get:Rule
val screenshotRule = ScreenshotTestRule(GoldenPathManager(
InstrumentationRegistry.getInstrumentation().context,
ASSETS_PATH,
TEST_OUTPUT_PATH,
PathConfig()
), disableIconPool = false)
@Parameter(0)
lateinit var device: TestPointerDevice
@Before
fun setUp() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
activity = virtualDisplayRule.activity
activity.runOnUiThread {
activity.actionBar?.hide()
activity.window.decorView.rootView.setBackgroundColor(Color.WHITE)
activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
device.setUp(
context,
virtualDisplayRule.virtualDisplay.display,
fakeAssociationRule.associationInfo,
)
verifier = EventVerifier(activity::getInputEvent)
exactScreenshotMatcher =
AlmostPerfectMatcher(acceptableThresholdCount = MAX_PIXELS_DIFFERENT)
}
@After
fun tearDown() {
device.tearDown()
}
@Ignore("b/366475909")
@Test
@EnableFlags(Flags.FLAG_HIDE_POINTER_INDICATORS_FOR_SECURE_WINDOWS)
fun testHidePointerIconOnSecureWindowScreenshot() {
device.hoverMove(1, 1)
verifier.assertReceivedMotion(withMotionAction(MotionEvent.ACTION_HOVER_ENTER))
waitForPointerIconUpdate()
assertScreenshotsMatch()
}
private fun getActualScreenshot(): Bitmap {
val actualBitmap: Bitmap? = virtualDisplayRule.getScreenshot()
assertNotNull(actualBitmap, "Screenshot is null.")
return actualBitmap
}
private fun assertScreenshotsMatch() {
getActualScreenshot().assertAgainstGolden(
screenshotRule,
getParameterizedExpectedScreenshotName(),
exactScreenshotMatcher
)
}
private fun getParameterizedExpectedScreenshotName(): String {
// Replace illegal characters '[' and ']' in expected screenshot name with underscores.
return "${testName.methodName}expected".replace("""\[|\]""".toRegex(), "_")
}
// We don't have a way to synchronously know when the requested pointer icon has been drawn
// to the display, so wait some time (at least one display frame) for the icon to propagate.
private fun waitForPointerIconUpdate() = Thread.sleep(500L * HW_TIMEOUT_MULTIPLIER)
companion object {
const val MAX_PIXELS_DIFFERENT = 5
const val ASSETS_PATH = "tests/input/assets"
val TEST_OUTPUT_PATH =
"/sdcard/Download/CtsInputRootTestCases/" +
HidePointerIconOnSecureWindowScreenshotTest::class.java.simpleName
val HW_TIMEOUT_MULTIPLIER = SystemProperties.getInt("ro.hw_timeout_multiplier", 1);
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun data(): Iterable<Any> =
listOf(TestPointerDevice.MOUSE, TestPointerDevice.DRAWING_TABLET)
}
}