diff options
Diffstat (limited to 'tests')
141 files changed, 3091 insertions, 1580 deletions
diff --git a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/sink/res/drawable-hdpi/ic_app.png diff --git a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png +++ b/tests/AccessoryDisplay/source/res/drawable-hdpi/ic_app.png diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java index 5d6a4a3541c1..1c78e5bcb536 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/OomAdjPerfTest.java @@ -90,6 +90,9 @@ public final class OomAdjPerfTest extends BasePerfTest { TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE1_NAME); TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE2_NAME); TargetPackageUtils.startStubPackage(mContext, STUB_PACKAGE3_NAME); + + Utils.wakeUp(); + Utils.runShellCommand("wm dismiss-keyguard"); } @After diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java index d7f4d9de6735..705fe296ae17 100644 --- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java +++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/util/TargetPackageUtils.java @@ -175,6 +175,7 @@ public class TargetPackageUtils { context.startService(intent); Assert.assertTrue("Timeout when waiting for starting package " + pkgName, pair.second.await(AWAIT_SERVICE_CONNECT_MS, TimeUnit.MILLISECONDS)); + Utils.runShellCommand("am unfreeze --sticky " + pkgName); } catch (InterruptedException e) { throw new RuntimeException(e); } diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java index 9bd94f2a9a1e..421ae57deae3 100644 --- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java +++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java @@ -66,4 +66,15 @@ public class Utils { ResultReceiver resultReceiver = intent.getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER); resultReceiver.send(0, null); } + + /** + * Wake up the device. + */ + public static void wakeUp() { + try { + UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).wakeUp(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } } diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml index 37321ad80b0f..37321ad80b0f 100755..100644 --- a/tests/AttestationVerificationTest/AndroidManifest.xml +++ b/tests/AttestationVerificationTest/AndroidManifest.xml diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt index dfbbda6c6f5e..afb3593e3e98 100644 --- a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt +++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt @@ -9,21 +9,28 @@ import android.security.attestationverification.AttestationVerificationManager.R import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY +import android.util.IndentingPrintWriter +import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.security.AttestationVerificationManagerService.DumpLogger import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations import java.io.ByteArrayOutputStream +import java.io.PrintWriter +import java.io.StringWriter import java.security.cert.Certificate import java.security.cert.CertificateFactory import java.security.cert.TrustAnchor import java.security.cert.X509Certificate import java.time.LocalDate +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + /** Test for Peer Device attestation verifier. */ @SmallTest @@ -31,6 +38,7 @@ import java.time.LocalDate class AttestationVerificationPeerDeviceVerifierTest { private val certificateFactory = CertificateFactory.getInstance("X.509") @Mock private lateinit var context: Context + private val dumpLogger = DumpLogger() private lateinit var trustAnchors: HashSet<TrustAnchor> @Before @@ -44,37 +52,50 @@ class AttestationVerificationPeerDeviceVerifierTest { } } + @After + fun dumpAndLog() { + val dump = dumpLogger.getDump() + Log.d(TAG, "$dump") + } + @Test fun verifyAttestation_returnsSuccessTypeChallenge() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 2, 1), - LocalDate.of(2021, 8, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), + LocalDate.of(2021, 8, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_SUCCESS) } @Test fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 2, 1), - LocalDate.of(2021, 1, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), + LocalDate.of(2021, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_SUCCESS) } @Test fun verifyAttestation_returnsSuccessTypePublicKey() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 2, 1), - LocalDate.of(2021, 8, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), + LocalDate.of(2021, 8, 1) + ) val leafCert = (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0] @@ -84,61 +105,75 @@ class AttestationVerificationPeerDeviceVerifierTest { val result = verifier.verifyAttestation( TYPE_PUBLIC_KEY, pkRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_SUCCESS) } @Test fun verifyAttestation_returnsSuccessOwnedBySystem() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 2, 1), - LocalDate.of(2021, 1, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), + LocalDate.of(2021, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray()) challengeRequirements.putBoolean("android.key_owned_by_system", true) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray() + ) + assertThat(result).isEqualTo(RESULT_SUCCESS) } @Test fun verifyAttestation_returnsFailureOwnedBySystem() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 2, 1), - LocalDate.of(2021, 1, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 2, 1), + LocalDate.of(2021, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) challengeRequirements.putBoolean("android.key_owned_by_system", true) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_FAILURE) } @Test fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2023, 3, 1), - LocalDate.of(2023, 2, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2023, 3, 1), + LocalDate.of(2023, 2, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_FAILURE) } @Test fun verifyAttestation_returnsFailureTrustedAnchorEmpty() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, HashSet(), false, LocalDate.of(2022, 1, 1), - LocalDate.of(2022, 1, 1)) + context, dumpLogger, HashSet(), false, LocalDate.of(2022, 1, 1), + LocalDate.of(2022, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_FAILURE) } @@ -151,32 +186,39 @@ class AttestationVerificationPeerDeviceVerifierTest { } val verifier = AttestationVerificationPeerDeviceVerifier( - context, badTrustAnchors, false, LocalDate.of(2022, 1, 1), - LocalDate.of(2022, 1, 1)) + context, dumpLogger, badTrustAnchors, false, LocalDate.of(2022, 1, 1), + LocalDate.of(2022, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_FAILURE) } fun verifyAttestation_returnsFailureChallenge() { val verifier = AttestationVerificationPeerDeviceVerifier( - context, trustAnchors, false, LocalDate.of(2022, 1, 1), - LocalDate.of(2022, 1, 1)) + context, dumpLogger, trustAnchors, false, LocalDate.of(2022, 1, 1), + LocalDate.of(2022, 1, 1) + ) val challengeRequirements = Bundle() challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray()) - val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements, - TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray()) + val result = verifier.verifyAttestation( + TYPE_CHALLENGE, challengeRequirements, + TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray() + ) assertThat(result).isEqualTo(RESULT_FAILURE) } private fun String.fromPEMFileToCerts(): Collection<Certificate> { return certificateFactory.generateCertificates( InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets() - .open(this)) + .open(this) + ) } private fun String.fromPEMFileToByteArray(): ByteArray { @@ -188,6 +230,12 @@ class AttestationVerificationPeerDeviceVerifierTest { return bos.toByteArray() } + private fun DumpLogger.getDump(): String { + val sw = StringWriter() + this.dumpTo(IndentingPrintWriter(PrintWriter(sw), " ")) + return sw.toString() + } + class TestActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -195,6 +243,7 @@ class AttestationVerificationPeerDeviceVerifierTest { } companion object { + private const val TAG = "AVFTest" private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem" private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME = "test_attestation_with_root_certs.pem" diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml index 27a8b2a0611b..3363af477dcb 100644 --- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/AndroidManifest.xml @@ -18,7 +18,7 @@ android:versionCode="1" android:versionName="1.0" > - <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="17" /> + <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" /> <instrumentation android:name="android.test.InstrumentationTestRunner" diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp index 5d49120ee702..3f48d70d659f 100644 --- a/tests/ChoreographerTests/Android.bp +++ b/tests/ChoreographerTests/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_android_core_graphics_stack", } android_test { diff --git a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp index 1e68c9dd459f..9994826d061a 100644 --- a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp +++ b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp @@ -19,10 +19,11 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_framework_cdm", } android_test { - name: "cdm_snippet", + name: "cdm_snippet_legacy", srcs: ["src/**/*.kt"], manifest: "AndroidManifest.xml", diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp index 03335c7cd576..37cb8500fbab 100644 --- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp +++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_framework_cdm", } python_test_host { @@ -36,7 +37,7 @@ python_test_host { tags: ["mobly"], }, data: [ - ":cdm_snippet", + ":cdm_snippet_legacy", ], version: { py2: { diff --git a/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml index 9d1813ff79bc..7c7ef6345a41 100644 --- a/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml +++ b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml @@ -24,12 +24,12 @@ <device name="device1"> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="test-file-name" value="cdm_snippet.apk" /> + <option name="test-file-name" value="cdm_snippet_legacy.apk" /> </target_preparer> </device> <device name="device2"> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="test-file-name" value="cdm_snippet.apk" /> + <option name="test-file-name" value="cdm_snippet_legacy.apk" /> </target_preparer> </device> diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp index 96e4a9ea4300..1038c9e93931 100644 --- a/tests/CtsSurfaceControlTestsStaging/Android.bp +++ b/tests/CtsSurfaceControlTestsStaging/Android.bp @@ -19,6 +19,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_android_core_graphics_stack", } android_test { diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java index caaee634c57a..4d4827676c74 100644 --- a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java +++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java @@ -30,10 +30,12 @@ import com.android.compatibility.common.util.DisplayUtil; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +@Ignore // b/330376055: Write tests for functionality for both dVRR and MRR devices. @RunWith(AndroidJUnit4.class) public class SurfaceControlTest { private static final String TAG = "SurfaceControlTest"; diff --git a/tests/DozeTest/res/drawable-hdpi/ic_app.png b/tests/DozeTest/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/DozeTest/res/drawable-hdpi/ic_app.png +++ b/tests/DozeTest/res/drawable-hdpi/ic_app.png diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp index 2cdf54248ebc..e09fbf6adc02 100644 --- a/tests/FlickerTests/ActivityEmbedding/Android.bp +++ b/tests/FlickerTests/ActivityEmbedding/Android.bp @@ -20,17 +20,65 @@ package { // all of the 'license_kinds' from "frameworks_base_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 + default_team: "trendy_team_windowing_sdk", default_applicable_licenses: ["frameworks_base_license"], } -android_test { - name: "FlickerTestsOther", +filegroup { + name: "FlickerTestsOtherCommon-src", + srcs: ["src/**/ActivityEmbeddingTestBase.kt"], +} + +filegroup { + name: "FlickerTestsOtherOpen-src", + srcs: ["src/**/open/*"], +} + +filegroup { + name: "FlickerTestsOtherRotation-src", + srcs: ["src/**/rotation/*"], +} + +java_library { + name: "FlickerTestsOtherCommon", + defaults: ["FlickerTestsDefault"], + srcs: [":FlickerTestsOtherCommon-src"], + static_libs: ["FlickerTestsBase"], +} + +java_defaults { + name: "FlickerTestsOtherDefaults", defaults: ["FlickerTestsDefault"], manifest: "AndroidManifest.xml", package_name: "com.android.server.wm.flicker", instrumentation_target_package: "com.android.server.wm.flicker", test_config_template: "AndroidTestTemplate.xml", - srcs: ["src/**/*"], - static_libs: ["FlickerTestsBase"], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsOtherCommon", + ], data: ["trace_config/*"], } + +android_test { + name: "FlickerTestsOtherOpen", + defaults: ["FlickerTestsOtherDefaults"], + srcs: [":FlickerTestsOtherOpen-src"], +} + +android_test { + name: "FlickerTestsOtherRotation", + defaults: ["FlickerTestsOtherDefaults"], + srcs: [":FlickerTestsOtherRotation-src"], +} + +android_test { + name: "FlickerTestsOther", + defaults: ["FlickerTestsOtherDefaults"], + srcs: ["src/**/*"], + exclude_srcs: [ + ":FlickerTestsOtherOpen-src", + ":FlickerTestsOtherRotation-src", + ":FlickerTestsOtherCommon-src", + ], +} diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml index 6f8f008cf85b..955b43a32827 100644 --- a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml +++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml @@ -19,7 +19,7 @@ xmlns:tools="http://schemas.android.com/tools" package="com.android.server.wm.flicker"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/> + <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="35"/> <!-- Read and write traces from external storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> @@ -46,6 +46,8 @@ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <!-- Allow the test to connect to perfetto trace processor --> <uses-permission android:name="android.permission.INTERNET"/> + <!-- Allow to query for the Launcher TestInfo on SDK 30+ --> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Allow the test to write directly to /sdcard/ and connect to trace processor --> <application android:requestLegacyExternalStorage="true" android:networkSecurityConfig="@xml/network_security_config" diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml index 439cf136c220..82de070921f0 100644 --- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml +++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt index 46ad77e1eff9..519b4296d93a 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.close +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -122,7 +122,7 @@ class CloseSecondaryActivityInSplitTest(flicker: LegacyFlickerTest) : companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. * diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt index af4f7a721464..4cd6d15b2983 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.layoutchange +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -114,11 +114,11 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) : // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } - .that(topLayerRegion.region.height) - .isEqual(bottomLayerRegion.region.height) + .that(topLayerRegion.region.bounds.height()) + .isEqual(bottomLayerRegion.region.bounds.height()) check { "width" } - .that(topLayerRegion.region.width) - .isEqual(bottomLayerRegion.region.width) + .that(topLayerRegion.region.bounds.width()) + .isEqual(bottomLayerRegion.region.bounds.width()) topLayerRegion.notOverlaps(bottomLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds) @@ -132,14 +132,17 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) : // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } - .that(topLayerRegion.region.height) - .isLower(bottomLayerRegion.region.height) + .that(topLayerRegion.region.bounds.height()) + .isLower(bottomLayerRegion.region.bounds.height()) check { "height" } - .that(topLayerRegion.region.height / 0.3f - bottomLayerRegion.region.height / 0.7f) + .that( + topLayerRegion.region.bounds.height() / 0.3f - + bottomLayerRegion.region.bounds.height() / 0.7f + ) .isLower(0.1f) check { "width" } - .that(topLayerRegion.region.width) - .isEqual(bottomLayerRegion.region.width) + .that(topLayerRegion.region.bounds.width()) + .isEqual(bottomLayerRegion.region.bounds.width()) topLayerRegion.notOverlaps(bottomLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds) @@ -148,7 +151,7 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) : companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt index e511b727d57f..5df8b57294f0 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.open +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -132,7 +132,7 @@ class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt index 000b457026be..6327d92ed570 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt @@ -90,7 +90,10 @@ class OpenActivityEmbeddingSecondaryToSplitTest(flicker: LegacyFlickerTest) : flicker.assertWm { notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) .then() - .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .isAppWindowInvisible( + ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT, + isOptional = true + ) .then() .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) } diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt index 4352177a8984..78004ccc3f97 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.open +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -143,7 +143,7 @@ class OpenThirdActivityOverSplitTest(flicker: LegacyFlickerTest) : companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. * diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt index 62cf6cd528e9..67825d2df361 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.open +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -43,6 +43,7 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest` */ +@FlakyTest(bugId = 341209752) @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -156,11 +157,11 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding it.timestamp ) check { "height" } - .that(mainActivityRegion.region.height) - .isEqual(secondaryActivityRegion.region.height) + .that(mainActivityRegion.region.bounds.height()) + .isEqual(secondaryActivityRegion.region.bounds.height()) check { "width" } - .that(mainActivityRegion.region.width) - .isEqual(secondaryActivityRegion.region.width) + .that(mainActivityRegion.region.bounds.width()) + .isEqual(secondaryActivityRegion.region.bounds.width()) mainActivityRegion .plus(secondaryActivityRegion.region) .coversExactly(startDisplayBounds) @@ -168,7 +169,6 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding } } - @FlakyTest(bugId = 290736037) /** Main activity should go from fullscreen to being a split with secondary activity. */ @Test fun mainActivityLayerGoesFromFullscreenToSplit() { @@ -192,18 +192,17 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } - .that(leftLayerRegion.region.height) - .isEqual(rightLayerRegion.region.height) + .that(leftLayerRegion.region.bounds.height()) + .isEqual(rightLayerRegion.region.bounds.height()) check { "width" } - .that(leftLayerRegion.region.width) - .isEqual(rightLayerRegion.region.width) + .that(leftLayerRegion.region.bounds.width()) + .isEqual(rightLayerRegion.region.bounds.width()) leftLayerRegion.notOverlaps(rightLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds) } } - @FlakyTest(bugId = 288591571) @Test override fun visibleLayersShownMoreThanOneConsecutiveEntry() { super.visibleLayersShownMoreThanOneConsecutiveEntry() @@ -211,7 +210,7 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt index aa8b4cebe91d..eed9225d3da0 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding.pip +import android.graphics.Rect import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -79,11 +79,11 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } - .that(leftLayerRegion.region.height) - .isEqual(rightLayerRegion.region.height) + .that(leftLayerRegion.region.bounds.height()) + .isEqual(rightLayerRegion.region.bounds.height()) check { "width" } - .that(leftLayerRegion.region.width) - .isEqual(rightLayerRegion.region.width) + .that(leftLayerRegion.region.bounds.width()) + .isEqual(rightLayerRegion.region.bounds.width()) leftLayerRegion.notOverlaps(rightLayerRegion.region) leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds) } @@ -136,9 +136,11 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : val pipWindowRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) check { "height" } - .that(pipWindowRegion.region.height) - .isLower(startDisplayBounds.height / 2) - check { "width" }.that(pipWindowRegion.region.width).isLower(startDisplayBounds.width) + .that(pipWindowRegion.region.bounds.height()) + .isLower(startDisplayBounds.height() / 2) + check { "width" } + .that(pipWindowRegion.region.bounds.width()) + .isLower(startDisplayBounds.width()) } } @@ -151,7 +153,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible } pipLayerList.zipWithNext { previous, current -> - if (startDisplayBounds.width > startDisplayBounds.height) { + if (startDisplayBounds.width() > startDisplayBounds.height()) { // Only verify when the display is landscape, because otherwise the final pip // window can be to the left of the original secondary activity. current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3) @@ -162,8 +164,12 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : } flicker.assertLayersEnd { val pipRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - check { "height" }.that(pipRegion.region.height).isLower(startDisplayBounds.height / 2) - check { "width" }.that(pipRegion.region.width).isLower(startDisplayBounds.width) + check { "height" } + .that(pipRegion.region.bounds.height()) + .isLower(startDisplayBounds.height() / 2) + check { "width" } + .that(pipRegion.region.bounds.width()) + .isLower(startDisplayBounds.width()) } } @@ -175,7 +181,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : invoke("secondaryLayerNotJumpToLeft") { val secondaryVisibleRegion = it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - if (secondaryVisibleRegion.region.isNotEmpty) { + if (!secondaryVisibleRegion.region.isEmpty) { check { "left" }.that(secondaryVisibleRegion.region.bounds.left).isGreater(0) } } @@ -199,7 +205,8 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY) val secondaryVisibleRegion = it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region) + // TODO(b/340992001): replace coverAtLeast with coverExactly + overlayVisibleRegion.coversAtLeast(secondaryVisibleRegion.region) } .then() .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY) @@ -222,7 +229,7 @@ class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) : companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. * diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt index 3d834c16163f..f5e6c7854eba 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt @@ -85,11 +85,11 @@ open class RotateSplitNoChangeTest(flicker: LegacyFlickerTest) : RotationTransit // Compare dimensions of two splits, given we're using default split attributes, // both activities take up the same visible size on the display. check { "height" } - .that(leftLayerRegion.region.height) - .isEqual(rightLayerRegion.region.height) + .that(leftLayerRegion.region.bounds.height()) + .isEqual(rightLayerRegion.region.bounds.height()) check { "width" } - .that(leftLayerRegion.region.width) - .isEqual(rightLayerRegion.region.width) + .that(leftLayerRegion.region.bounds.width()) + .isEqual(rightLayerRegion.region.bounds.width()) leftLayerRegion.notOverlaps(rightLayerRegion.region) // Layers of two activities sum to be fullscreen size on display. leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) @@ -108,11 +108,11 @@ open class RotateSplitNoChangeTest(flicker: LegacyFlickerTest) : RotationTransit val rightLayerRegion = this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) check { "height" } - .that(leftLayerRegion.region.height) - .isEqual(rightLayerRegion.region.height) + .that(leftLayerRegion.region.bounds.height()) + .isEqual(rightLayerRegion.region.bounds.height()) check { "width" } - .that(leftLayerRegion.region.width) - .isEqual(rightLayerRegion.region.width) + .that(leftLayerRegion.region.bounds.width()) + .isEqual(rightLayerRegion.region.bounds.width()) leftLayerRegion.notOverlaps(rightLayerRegion.region) leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) } diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt index 511c94849681..ee2c05e82d51 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt @@ -16,9 +16,13 @@ package com.android.server.wm.flicker.activityembedding.rotation +import android.graphics.Rect import android.platform.test.annotations.Presubmit +import android.tools.Position import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.traces.Condition +import android.tools.traces.DeviceStateDump import android.tools.traces.component.ComponentNameMatcher import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase import com.android.server.wm.flicker.helpers.setRotation @@ -30,7 +34,14 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : ActivityEmbeddin override val transition: FlickerBuilder.() -> Unit = { setup { this.setRotation(flicker.scenario.startRotation) } teardown { testApp.exit(wmHelper) } - transitions { this.setRotation(flicker.scenario.endRotation) } + transitions { + this.setRotation(flicker.scenario.endRotation) + if (!flicker.scenario.isTablet) { + wmHelper.StateSyncBuilder() + .add(navBarInPosition(flicker.scenario.isGesturalNavigation)) + .waitForAndVerify() + } + } } /** {@inheritDoc} */ @@ -76,4 +87,37 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : ActivityEmbeddin appLayerRotates_StartingPos() appLayerRotates_EndingPos() } + + private fun navBarInPosition(isGesturalNavigation: Boolean): Condition<DeviceStateDump> { + return Condition("navBarPosition") { dump -> + val display = + dump.layerState.displays.filterNot { it.isOff }.minByOrNull { it.id } + ?: error("There is no display!") + val displayArea = display.layerStackSpace + val navBarPosition = display.navBarPosition(isGesturalNavigation) + val navBarRegion = dump.layerState + .getLayerWithBuffer(ComponentNameMatcher.NAV_BAR) + ?.visibleRegion?.bounds ?: Rect() + + when (navBarPosition) { + Position.TOP -> + navBarRegion.top == displayArea.top && + navBarRegion.left == displayArea.left && + navBarRegion.right == displayArea.right + Position.BOTTOM -> + navBarRegion.bottom == displayArea.bottom && + navBarRegion.left == displayArea.left && + navBarRegion.right == displayArea.right + Position.LEFT -> + navBarRegion.left == displayArea.left && + navBarRegion.top == displayArea.top && + navBarRegion.bottom == displayArea.bottom + Position.RIGHT -> + navBarRegion.right == displayArea.right && + navBarRegion.top == displayArea.top && + navBarRegion.bottom == displayArea.bottom + else -> error("Unknown position $navBarPosition") + } + } + } } diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt index 7298e5f71b05..379b45cdf08e 100644 --- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt +++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt @@ -16,9 +16,9 @@ package com.android.server.wm.flicker.activityembedding.splitscreen +import android.graphics.Rect import android.platform.test.annotations.Presubmit import android.platform.test.annotations.RequiresDevice -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -32,6 +32,7 @@ import com.android.wm.shell.flicker.utils.SplitScreenUtils import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible +import kotlin.math.abs import org.junit.FixMethodOrder import org.junit.Ignore import org.junit.Test @@ -59,14 +60,16 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa testApp.launchViaIntent(wmHelper) testApp.launchSecondaryActivity(wmHelper) secondaryApp.launchViaIntent(wmHelper) - tapl.goHome() - wmHelper - .StateSyncBuilder() - .withAppTransitionIdle() - .withHomeActivityVisible() - .waitForAndVerify() startDisplayBounds = wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") + + // Record the displayBounds before `goHome()` in case the launcher is fixed-portrait. + tapl.goHome() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withHomeActivityVisible() + .waitForAndVerify() } transitions { SplitScreenUtils.enterSplit( @@ -135,19 +138,13 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa .plus(systemDivider.region) .coversExactly(startDisplayBounds) check { "ActivityEmbeddingSplitHeight" } - .that(leftAELayerRegion.region.height) - .isEqual(rightAELayerRegion.region.height) - check { "SystemSplitHeight" } - .that(rightAELayerRegion.region.height) - .isEqual(secondaryAppLayerRegion.region.height) - // TODO(b/292283182): Remove this special case handling. + .that(leftAELayerRegion.region.bounds.height()) + .isEqual(rightAELayerRegion.region.bounds.height()) check { "ActivityEmbeddingSplitWidth" } - .that(Math.abs(leftAELayerRegion.region.width - rightAELayerRegion.region.width)) - .isLower(2) - check { "SystemSplitWidth" } .that( - Math.abs( - secondaryAppLayerRegion.region.width - 2 * rightAELayerRegion.region.width + abs( + leftAELayerRegion.region.bounds.width() - + rightAELayerRegion.region.bounds.width() ) ) .isLower(2) @@ -163,22 +160,14 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) val rightAEWindowRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) - // There's no window for the divider bar. - val secondaryAppLayerRegion = - visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()) check { "ActivityEmbeddingSplitHeight" } - .that(leftAEWindowRegion.region.height) - .isEqual(rightAEWindowRegion.region.height) - check { "SystemSplitHeight" } - .that(rightAEWindowRegion.region.height) - .isEqual(secondaryAppLayerRegion.region.height) + .that(leftAEWindowRegion.region.bounds.height()) + .isEqual(rightAEWindowRegion.region.bounds.height()) check { "ActivityEmbeddingSplitWidth" } - .that(Math.abs(leftAEWindowRegion.region.width - rightAEWindowRegion.region.width)) - .isLower(2) - check { "SystemSplitWidth" } .that( - Math.abs( - secondaryAppLayerRegion.region.width - 2 * rightAEWindowRegion.region.width + abs( + leftAEWindowRegion.region.bounds.width() - + rightAEWindowRegion.region.bounds.width() ) ) .isLower(2) @@ -190,7 +179,7 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBa companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() /** * Creates the test configurations. * diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 1d71f95ef64f..d658d5991a57 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -63,17 +63,20 @@ java_library { ], } -android_library_import { - name: "wm-flicker-window-extensions_nodeps", - aars: ["libs/window-extensions-release.aar"], +java_library { + name: "wm-flicker-window-extensions", sdk_version: "current", + static_libs: [ + "androidx.window.extensions_extensions-nodeps", + ], + installable: false, } java_library { - name: "wm-flicker-window-extensions", + name: "wm-flicker-window-extensions-core", sdk_version: "current", static_libs: [ - "wm-flicker-window-extensions_nodeps", + "androidx.window.extensions.core_core-nodeps", ], installable: false, } diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml index 4b6224efaf60..4ffb11ab92ae 100644 --- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml +++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp index f5e962124b37..72a90650927f 100644 --- a/tests/FlickerTests/AppLaunch/Android.bp +++ b/tests/FlickerTests/AppLaunch/Android.bp @@ -30,7 +30,7 @@ filegroup { filegroup { name: "FlickerTestsAppLaunch1-src", - srcs: ["src/**/OpenApp*"], + srcs: ["src/**/OpenAppFrom*"], } java_library { diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml index 583bcb74ffc2..0fa4d07b2eca 100644 --- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml +++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index b1d78cbc034e..a71599d25632 100644 --- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -19,8 +19,9 @@ package com.android.server.wm.flicker.launch import android.app.Instrumentation import android.app.WallpaperManager import android.content.res.Resources +import android.graphics.Rect +import android.graphics.Region import android.platform.test.annotations.Presubmit -import android.tools.datatypes.Region import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -213,6 +214,12 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private fun LayersTraceSubject.visibleRegionCovers( component: IComponentMatcher, + expectedArea: Rect, + isOptional: Boolean = true + ): LayersTraceSubject = visibleRegionCovers(component, Region(expectedArea), isOptional) + + private fun LayersTraceSubject.visibleRegionCovers( + component: IComponentMatcher, expectedArea: Region, isOptional: Boolean = true ): LayersTraceSubject = diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml index d6ae2b3a5d94..4d9fefbc7d88 100644 --- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml +++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt index 8a241de32a2b..209a14b3657d 100644 --- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt +++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt @@ -17,6 +17,7 @@ package com.android.server.wm.flicker.service import android.app.Instrumentation +import android.platform.test.rule.DisableNotificationCooldownSettingRule import android.platform.test.rule.NavigationModeRule import android.platform.test.rule.PressHomeRule import android.platform.test.rule.UnlockScreenRule @@ -48,6 +49,7 @@ object Utils { clearCacheAfterParsing = false ) ) + .around(DisableNotificationCooldownSettingRule()) .around(PressHomeRule()) } } diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml index 988f76f4175c..b879c54dcab3 100644 --- a/tests/FlickerTests/IME/AndroidTestTemplate.xml +++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml @@ -8,8 +8,12 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> + <!-- enable AOD --> + <option name="set-secure-setting" key="doze_always_on" value="1" /> <!-- prevents the phone from restarting --> <option name="force-skip-system-props" value="true"/> <!-- set WM tracing verbose level to all --> @@ -78,6 +82,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/IME/OWNERS b/tests/FlickerTests/IME/OWNERS index ae1098d496df..e3a2e674ae7a 100644 --- a/tests/FlickerTests/IME/OWNERS +++ b/tests/FlickerTests/IME/OWNERS @@ -1,3 +1,3 @@ # ime # Bug component: 34867 -include /services/core/java/com/android/server/inputmethod/OWNERS +file:/services/core/java/com/android/server/inputmethod/OWNERS diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt index 7e486abbd30f..2b6ddcb43f18 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt @@ -32,6 +32,9 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +/** + * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest` + */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -83,11 +86,12 @@ class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(fl } if (imeSnapshotLayers.isNotEmpty()) { val visibleAreas = - imeSnapshotLayers - .mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion } + imeSnapshotLayers.mapNotNull { imeSnapshotLayer -> + imeSnapshotLayer.layer.visibleRegion + } val imeVisibleRegion = RegionSubject(visibleAreas, timestamp) val appVisibleRegion = it.visibleRegion(imeTestApp) - if (imeVisibleRegion.region.isNotEmpty) { + if (!imeVisibleRegion.region.isEmpty) { imeVisibleRegion.coversAtMost(appVisibleRegion.region) } } diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt index 2f3ec6301215..0344197c1425 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt @@ -33,8 +33,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing to home transitions. To run this test: `atest - * FlickerTests:CloseImeWindowToHomeTest` + * Test IME window closing to home transitions. + * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt index 8821b69cdb3e..fde1373b032b 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt @@ -42,7 +42,7 @@ import org.junit.runners.Parameterized * * More details on b/190352379 * - * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest` + * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt index d75eba68c7cc..ed6e8df3e293 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt @@ -23,6 +23,7 @@ import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.legacy.LegacyFlickerTestFactory import android.tools.traces.component.ComponentNameMatcher +import androidx.test.filters.FlakyTest import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import org.junit.FixMethodOrder @@ -42,7 +43,7 @@ import org.junit.runners.Parameterized * * More details on b/190352379 * - * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest` + * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -77,6 +78,7 @@ class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) : @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible() + @FlakyTest(bugId = 330486656) @Presubmit @Test fun imeAppLayerIsAlwaysVisible() { diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt index 41d9e30a17ee..dc2bd1bc9996 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt @@ -34,8 +34,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window closing back to app window transitions. To run this test: `atest - * FlickerTests:CloseImeWindowToAppTest` + * Test IME window closing back to app window transitions. + * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index 0e7fb7975df8..05771e88fc83 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify * there is no flickering when back to the simple activity without requesting IME to show. * - * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest` + * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index 47a7e1b65b2d..336fe6f991ca 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -36,8 +36,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest - * FlickerTests:OpenImeWindowToFixedPortraitAppTest` + * Test IME window shown on the app with fixing portrait orientation. + * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt index e8249bca4c2d..b8f11dcf8970 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt @@ -38,8 +38,9 @@ import org.junit.runners.Parameterized /** * Test IME window layer will become visible when switching from the fixed orientation activity - * (e.g. Launcher activity). To run this test: `atest - * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest` + * (e.g. Launcher activity). + * To run this test: + * `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -115,7 +116,10 @@ class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyF .isEqual(true) imeLayerSubjects.forEach { imeLayerSubject -> - imeLayerSubject.check { "alpha" }.that(imeLayerSubject.layer.color.a).isEqual(1.0f) + imeLayerSubject + .check { "alpha" } + .that(imeLayerSubject.layer.color.alpha()) + .isEqual(1.0f) } } } diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt index 617237d37368..34a708578396 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt @@ -33,7 +33,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest` + * Test IME window opening transitions. + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -50,7 +51,10 @@ class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTe testApp.launchViaIntent(wmHelper) testApp.openIME(wmHelper) this.setRotation(flicker.scenario.startRotation) - device.pressRecentApps() + if (flicker.scenario.isTablet && tapl.isTransientTaskbar()) { + tapl.launchedAppState.swipeUpToUnstashTaskbar() + } + tapl.launchedAppState.switchToOverview() wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify() } transitions { diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index 7b62c8967628..7c72c3187a7f 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt @@ -35,8 +35,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest - * FlickerTests:SwitchImeWindowsFromGestureNavTest` + * Test IME windows switching with 2-Buttons or gestural navigation. + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 53bfb4ecf66f..fe5320cd1a46 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized /** * Launch an app that automatically displays the IME * - * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest` + * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest` * * Actions: * ``` diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt new file mode 100644 index 000000000000..92b6b934874f --- /dev/null +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 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 com.android.server.wm.flicker.ime + +import android.platform.test.annotations.Presubmit +import android.platform.test.rule.UnlockScreenRule +import android.tools.Rotation +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.traces.component.ComponentNameMatcher +import androidx.test.filters.FlakyTest +import com.android.server.wm.flicker.BaseTest +import com.android.server.wm.flicker.helpers.ImeAppHelper +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test IME window closing on lock and opening on screen unlock. + * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest` + */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class ShowImeOnUnlockScreenTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { + private val testApp = ImeAppHelper(instrumentation) + private val imeOrSnapshot = ComponentNameMatcher.IME.or(ComponentNameMatcher.IME_SNAPSHOT) + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.expectedRotationCheckEnabled = false + testApp.launchViaIntent(wmHelper) + testApp.openIME(wmHelper) + } + transitions { + device.sleep() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() + UnlockScreenRule.unlockScreen(device) + wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify() + } + teardown { testApp.exit(wmHelper) } + } + + @Presubmit + @Test + fun imeAndAppAnimateTogetherWhenLockingAndUnlocking() { + flicker.assertLayers { + this.isVisible(testApp) + .isVisible(imeOrSnapshot) + .then() + .isInvisible(testApp) + .isInvisible(imeOrSnapshot) + .then() + .isVisible(testApp) + .isVisible(imeOrSnapshot) + } + } + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun navBarWindowIsAlwaysVisible() {} + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun statusBarWindowIsAlwaysVisible() {} + + /** {@inheritDoc} */ + @Test + @Ignore("Not applicable to this CUJ. Display turns off during transition") + override fun taskBarWindowIsAlwaysVisible() {} + + @FlakyTest(bugId = 338178020) + @Test + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = + LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt index 12290af8fd46..9eaf998ed63f 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt @@ -31,7 +31,10 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */ +/** + * Test IME window opening transitions. + * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest` + */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 0948351ac65b..7186a2c48c4c 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized /** * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity. - * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest` + * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt index a14dc62b0023..c96c760e2d7b 100644 --- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt +++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt @@ -37,8 +37,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window layer will be associated with the app task when going to the overview screen. To - * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest` + * Test IME window layer will be associated with the app task when going to the overview screen. + * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -191,7 +191,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl this.invoke("imeLayerIsVisibleAndAlignAppWidow") { val imeVisibleRegion = it.visibleRegion(ComponentNameMatcher.IME) val appVisibleRegion = it.visibleRegion(imeTestApp) - if (imeVisibleRegion.region.isNotEmpty) { + if (!imeVisibleRegion.region.isEmpty) { it.isVisible(ComponentNameMatcher.IME) imeVisibleRegion.coversAtMost(appVisibleRegion.region) } diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml index 403685831be7..04b312a896b9 100644 --- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml +++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt index ffaeeadb1042..8c9ab9aadb8e 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold` + * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationColdTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt index 6e67e193ed8c..e595100a2cbe 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm` + * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWarmTest` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index 8e210d455591..fbe1d34272c9 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt @@ -40,7 +40,8 @@ import org.junit.runners.Parameterized * * This test assumes the device doesn't have AOD enabled * - * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp` + * To run this test: + * `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWithOverlayAppTest` */ @RequiresDevice @RunWith(Parameterized::class) @@ -123,7 +124,9 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker @Test override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() - @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered() + @FlakyTest(bugId = 227143265) + @Test + override fun entireScreenCovered() = super.entireScreenCovered() @FlakyTest(bugId = 278227468) @Test diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt index b6d09d0bf3bb..c8ca644dde90 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * To run this test: `atest FlickerTests:OpenAppFromNotificationCold` + * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationColdTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt index 1e607bfb2f49..07fc2300286a 100644 --- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt +++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt @@ -18,6 +18,7 @@ package com.android.server.wm.flicker.notification import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.platform.test.rule.DisableNotificationCooldownSettingRule import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.FlickerTestData @@ -37,6 +38,7 @@ import com.android.server.wm.flicker.navBarWindowIsVisibleAtEnd import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd import org.junit.Assume +import org.junit.ClassRule import org.junit.FixMethodOrder import org.junit.Ignore import org.junit.Test @@ -47,7 +49,7 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm` + * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationWarmTest` */ @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @@ -208,5 +210,10 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + + /** Ensures that posted notifications will alert and HUN even just after boot. */ + @ClassRule + @JvmField + val disablenotificationCooldown = DisableNotificationCooldownSettingRule() } } diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml index 797ca4eacd5f..8acdabc2337d 100644 --- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml +++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index 8b09b590e790..9bb62e1e1794 100644 --- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -16,9 +16,9 @@ package com.android.server.wm.flicker.quickswitch +import android.graphics.Rect import android.platform.test.annotations.Presubmit import android.tools.NavBar -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -237,7 +237,7 @@ class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(f override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd() companion object { - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index c54ddcf793f6..491b9945d12d 100644 --- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.quickswitch +import android.graphics.Rect import android.tools.NavBar -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -285,7 +285,7 @@ class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTes super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index 69a84a0cbcb0..de54c95da361 100644 --- a/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -16,10 +16,10 @@ package com.android.server.wm.flicker.quickswitch +import android.graphics.Rect import android.platform.test.annotations.Presubmit import android.tools.NavBar import android.tools.Rotation -import android.tools.datatypes.Rect import android.tools.flicker.junit.FlickerParametersRunnerFactory import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest @@ -266,7 +266,7 @@ class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker companion object { /** {@inheritDoc} */ - private var startDisplayBounds = Rect.EMPTY + private var startDisplayBounds = Rect() @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md index 6b28fdf8a8ef..7429250f5cc0 100644 --- a/tests/FlickerTests/README.md +++ b/tests/FlickerTests/README.md @@ -7,82 +7,17 @@ The tests are organized in packages according to the transitions they test (e.g. ## Adding a Test -By default tests should inherit from `RotationTestBase` or `NonRotationTestBase` and must override the variable `transitionToRun` (Kotlin) or the function `getTransitionToRun()` (Java). -Only tests that are not supported by these classes should inherit directly from the `FlickerTestBase` class. +By default, tests should inherit from `TestBase` and override the variable `transition` (Kotlin) or the function `getTransition()` (Java). -### Rotation animations and transitions +Inheriting from this class ensures the common assertions will be executed, namely: -Tests that rotate the device should inherit from `RotationTestBase`. -Tests that inherit from the class automatically receive start and end rotation values. -Moreover, these tests inherit the following checks: * all regions on the screen are covered * status bar is always visible -* status bar rotates +* status bar is at the correct position at the start and end of the transition * nav bar is always visible -* nav bar is rotates +* nav bar is at the correct position at the start and end of the transition The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation. -### Non-Rotation animations and transitions +For more examples of how a test looks like check `ChangeAppRotationTest` within the `Rotation` subdirectory. -`NonRotationTestBase` was created to make it easier to write tests that do not involve rotation (e.g., `Pip`, `split screen` or `IME`). -Tests that inherit from the class are automatically executed twice: once in portrait and once in landscape mode and the assertions are checked independently. -Moreover, these tests inherit the following checks: -* all regions on the screen are covered -* status bar is always visible -* nav bar is always visible - -The default tests can be disabled by overriding the respective methods and including an `@Ignore` annotation. - -### Exceptional cases - -Tests that rotate the device should inherit from `RotationTestBase`. -This class allows the test to be freely configured and does not provide any assertions. - - -### Example - -Start by defining common or error prone transitions using `TransitionRunner`. -```kotlin -@LargeTest -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MyTest( - beginRotationName: String, - beginRotation: Int -) : NonRotationTestBase(beginRotationName, beginRotation) { - init { - mTestApp = MyAppHelper(InstrumentationRegistry.getInstrumentation()) - } - - override val transitionToRun: TransitionRunner - get() = TransitionRunner.newBuilder() - .withTag("myTest") - .recordAllRuns() - .runBefore { device.pressHome() } - .runBefore { device.waitForIdle() } - .run { testApp.open() } - .runAfter{ testApp.exit() } - .repeat(2) - .includeJankyRuns() - .build() - - @Test - fun myWMTest() { - checkResults { - WmTraceSubject.assertThat(it) - .showsAppWindow(MyTestApp) - .forAllEntries() - } - } - - @Test - fun mySFTest() { - checkResults { - LayersTraceSubject.assertThat(it) - .showsLayer(MyTestApp) - .forAllEntries() - } - } -} -``` diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml index b5ea7390e9ba..91ece214aad5 100644 --- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml +++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml @@ -8,6 +8,8 @@ <option name="isolated-storage" value="false"/> <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- disable DeprecatedTargetSdk warning --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_target_sdk_dialog 1"/> <!-- keeps the screen on during tests --> <option name="screen-always-on" value="on"/> <!-- prevents the phone from restarting --> @@ -78,6 +80,7 @@ value="trace_config.textproto" /> <option name="instrumentation-arg" key="per_run" value="true"/> + <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/> </test> <!-- Needed for pulling the collected trace config on to the host --> <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt new file mode 100644 index 000000000000..bf569bc23df6 --- /dev/null +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/OpenShowWhenLockedSeamlessAppRotationTest.kt @@ -0,0 +1,125 @@ +/* + * Copyright (C) 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 com.android.server.wm.flicker.rotation + +import android.platform.test.annotations.Presubmit +import android.tools.NavBar +import android.tools.Rotation +import android.tools.flicker.assertions.FlickerTest +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.flicker.rules.ChangeDisplayOrientationRule +import android.tools.traces.component.ComponentNameMatcher +import com.android.server.wm.flicker.BaseTest +import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.Ignore +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test opening an app over lockscreen with rotation change using seamless rotations. + */ +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenShowWhenLockedSeamlessAppRotationTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { + val testApp = SeamlessRotationAppHelper(instrumentation) + + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + device.sleep() + wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify() + device.wakeUp() + val originalRotation = device.displayRotation + ChangeDisplayOrientationRule.setRotation(Rotation.ROTATION_90) + Assume.assumeTrue("Assume that lockscreen uses fixed orientation", + originalRotation == device.displayRotation) + } + transitions { + // The activity is show-when-locked, so the requested orientation will be changed + // from NOSENSOR(keyguard) to UNSPECIFIED(activity). Then the fixed-user-rotation + // (by setRotation) will take effect to rotate the display. + testApp.launchViaIntent(wmHelper) + } + teardown { testApp.exit(wmHelper) } + } + + @Presubmit + @Test + fun notContainsRotationAnimation() { + flicker.assertLayers { + // Verifies that com.android.wm.shell.transition.ScreenRotationAnimation is not used. + notContains(ComponentNameMatcher("", "Animation leash of screenshot rotation")) + } + } + + // Ignore the assertions which are included in SeamlessAppRotationTest. + @Test + @Ignore("Uninterested") + override fun statusBarLayerPositionAtStartAndEnd() {} + + @Test + @Ignore("Uninterested") + override fun statusBarLayerIsVisibleAtStartAndEnd() {} + + @Test + @Ignore("Uninterested") + override fun statusBarWindowIsAlwaysVisible() {} + + @Test + @Ignore("Uninterested") + override fun navBarLayerPositionAtStartAndEnd() {} + + @Test + @Ignore("Uninterested") + override fun navBarLayerIsVisibleAtStartAndEnd() {} + + @Test + @Ignore("Uninterested") + override fun navBarWindowIsVisibleAtStartAndEnd() {} + + @Test + @Ignore("Uninterested") + override fun navBarWindowIsAlwaysVisible() {} + + @Test + @Ignore("Uninterested") + override fun visibleLayersShownMoreThanOneConsecutiveEntry() {} + + @Test + @Ignore("Uninterested") + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {} + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + // The rotation will be controlled by the setup of test. + return LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0), + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) + ) + } + } +} diff --git a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index c7da778b752b..c49b509a9db3 100644 --- a/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -21,6 +21,7 @@ import android.tools.device.apphelpers.StandardAppHelper import android.tools.flicker.legacy.FlickerBuilder import android.tools.flicker.legacy.LegacyFlickerTest import android.tools.flicker.subject.layers.LayerTraceEntrySubject +import android.tools.flicker.subject.layers.LayersTraceSubject import android.tools.traces.component.ComponentNameMatcher import android.tools.traces.component.IComponentMatcher import android.tools.traces.surfaceflinger.Display @@ -46,6 +47,7 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( ignoreLayers = + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf( ComponentNameMatcher.SPLASH_SCREEN, ComponentNameMatcher.SNAPSHOT, diff --git a/tests/FlickerTests/libs/window-extensions-release.aar b/tests/FlickerTests/libs/window-extensions-release.aar Binary files differdeleted file mode 100644 index 918e514f4c89..000000000000 --- a/tests/FlickerTests/libs/window-extensions-release.aar +++ /dev/null diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index 17f91ebad771..060015bcc4b2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -40,9 +40,10 @@ abstract class BaseTest constructor( protected val flicker: LegacyFlickerTest, protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(), + protected val tapl: LauncherInstrumentation = LauncherInstrumentation() ) { - protected val tapl: LauncherInstrumentation by lazy { - LauncherInstrumentation().also { it.expectedRotationCheckEnabled = true } + init { + tapl.setExpectedRotationCheckEnabled(true) } private val logTag = this::class.java.simpleName diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index 8853c1db856f..348d0af5a2d3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -279,12 +279,11 @@ fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp( subject.isVisible } val visibleAreas = - snapshotLayers - .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion } + snapshotLayers.mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion } val snapshotRegion = RegionSubject(visibleAreas, it.timestamp) val appVisibleRegion = it.visibleRegion(component) // Verify the size of snapshotRegion covers appVisibleRegion exactly in animation. - if (snapshotRegion.region.isNotEmpty && appVisibleRegion.region.isNotEmpty) { + if (!snapshotRegion.region.isEmpty && !appVisibleRegion.region.isEmpty) { snapshotRegion.coversExactly(appVisibleRegion.region) } } diff --git a/tests/FlickerTests/test-apps/app-helpers/OWNERS b/tests/FlickerTests/test-apps/app-helpers/OWNERS new file mode 100644 index 000000000000..ab6253200f73 --- /dev/null +++ b/tests/FlickerTests/test-apps/app-helpers/OWNERS @@ -0,0 +1,2 @@ +uysalorhan@google.com +pragyabajoria@google.com
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt new file mode 100644 index 000000000000..9a5e88becf1e --- /dev/null +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -0,0 +1,168 @@ +/* + * Copyright (C) 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 com.android.server.wm.flicker.helpers + +import android.graphics.Rect +import android.tools.device.apphelpers.IStandardAppHelper +import android.tools.helpers.SYSTEMUI_PACKAGE +import android.tools.traces.parsers.WindowManagerStateHelper +import android.tools.traces.wm.WindowingMode +import androidx.test.uiautomator.By +import androidx.test.uiautomator.BySelector +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiObject2 +import androidx.test.uiautomator.Until + +/** + * Wrapper class around App helper classes. This class adds functionality to the apps that the + * desktop apps would have. + */ +open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : + IStandardAppHelper by innerHelper { + + enum class Corners { + LEFT_TOP, + RIGHT_TOP, + LEFT_BOTTOM, + RIGHT_BOTTOM + } + + private val TIMEOUT_MS = 3_000L + private val CAPTION = "desktop_mode_caption" + private val CAPTION_HANDLE = "caption_handle" + private val MAXIMIZE_BUTTON = "maximize_window" + private val MAXIMIZE_BUTTON_VIEW = "maximize_button_view" + private val CLOSE_BUTTON = "close_window" + + private val caption: BySelector + get() = By.res(SYSTEMUI_PACKAGE, CAPTION) + + /** Wait for an app moved to desktop to finish its transition. */ + private fun waitForAppToMoveToDesktop(wmHelper: WindowManagerStateHelper) { + wmHelper + .StateSyncBuilder() + .withWindowSurfaceAppeared(innerHelper) + .withFreeformApp(innerHelper) + .withAppTransitionIdle() + .waitForAndVerify() + } + + /** Move an app to Desktop by dragging the app handle at the top. */ + fun enterDesktopWithDrag( + wmHelper: WindowManagerStateHelper, + device: UiDevice, + ) { + innerHelper.launchViaIntent(wmHelper) + dragToDesktop(wmHelper, device) + waitForAppToMoveToDesktop(wmHelper) + } + + private fun dragToDesktop(wmHelper: WindowManagerStateHelper, device: UiDevice) { + val windowRect = wmHelper.getWindowRegion(innerHelper).bounds + val startX = windowRect.centerX() + + // Start dragging a little under the top to prevent dragging the notification shade. + val startY = 10 + + val displayRect = + wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect + ?: throw IllegalStateException("Default display is null") + + // The position we want to drag to + val endY = displayRect.centerY() / 2 + + // drag the window to move to desktop + device.drag(startX, startY, startX, endY, 100) + } + + /** Click maximise button on the app header for the given app. */ + fun maximiseDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) { + val caption = getCaptionForTheApp(wmHelper, device) + val maximizeButton = + caption + ?.children + ?.find { it.resourceName.endsWith(MAXIMIZE_BUTTON_VIEW) } + ?.children + ?.get(0) + maximizeButton?.click() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() + } + /** Click close button on the app header for the given app. */ + fun closeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) { + val caption = getCaptionForTheApp(wmHelper, device) + val closeButton = caption?.children?.find { it.resourceName.endsWith(CLOSE_BUTTON) } + closeButton?.click() + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .withWindowSurfaceDisappeared(innerHelper) + .waitForAndVerify() + } + + private fun getCaptionForTheApp( + wmHelper: WindowManagerStateHelper, + device: UiDevice + ): UiObject2? { + if ( + wmHelper.getWindow(innerHelper)?.windowingMode != + WindowingMode.WINDOWING_MODE_FREEFORM.value + ) + error("expected a freeform window with caption but window is not in freeform mode") + val captions = + device.wait(Until.findObjects(caption), TIMEOUT_MS) + ?: error("Unable to find view $caption\n") + + return captions.find { + wmHelper.getWindowRegion(innerHelper).bounds.contains(it.visibleBounds) + } + } + + /** Resize a desktop app from its corners. */ + fun cornerResize( + wmHelper: WindowManagerStateHelper, + device: UiDevice, + corner: Corners, + horizontalChange: Int, + verticalChange: Int + ) { + val windowRect = wmHelper.getWindowRegion(innerHelper).bounds + val (startX, startY) = getStartCoordinatesForCornerResize(windowRect, corner) + + // The position we want to drag to + val endY = startY + verticalChange + val endX = startX + horizontalChange + + // drag the specified corner of the window to the end coordinate. + device.drag(startX, startY, endX, endY, 100) + wmHelper + .StateSyncBuilder() + .withAppTransitionIdle() + .waitForAndVerify() + } + + private fun getStartCoordinatesForCornerResize( + windowRect: Rect, + corner: Corners + ): Pair<Int, Int> { + return when (corner) { + Corners.LEFT_TOP -> Pair(windowRect.left, windowRect.top) + Corners.RIGHT_TOP -> Pair(windowRect.right, windowRect.top) + Corners.LEFT_BOTTOM -> Pair(windowRect.left, windowRect.bottom) + Corners.RIGHT_BOTTOM -> Pair(windowRect.right, windowRect.bottom) + } + } +} diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt index 0c60f284a35b..ef8d84fb915a 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt @@ -45,13 +45,7 @@ constructor( require(gameView != null) { "Mock game app view not found." } val bound = gameView.getVisibleBounds() - return uiDevice.swipe( - bound.centerX(), - bound.top, - bound.centerX(), - bound.centerY(), - SWIPE_STEPS - ) + return uiDevice.swipe(bound.centerX(), 0, bound.centerX(), bound.centerY(), SWIPE_STEPS) } /** diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt index b09e53b6400d..634b6eedd7e6 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation -import android.tools.datatypes.Rect -import android.tools.datatypes.Region +import android.graphics.Rect +import android.graphics.Region import android.tools.device.apphelpers.StandardAppHelper import android.tools.helpers.FIND_TIMEOUT import android.tools.helpers.SYSTEMUI_PACKAGE @@ -86,7 +86,7 @@ constructor( .add("letterboxAppRepositioned") { val letterboxAppWindow = getWindowRegion(wmHelper) val appRegionBounds = letterboxAppWindow.bounds - val appWidth = appRegionBounds.width + val appWidth = appRegionBounds.width() return@add if (right) appRegionBounds.left == displayBounds.right - appWidth && appRegionBounds.right == displayBounds.right @@ -108,7 +108,7 @@ constructor( .add("letterboxAppRepositioned") { val letterboxAppWindow = getWindowRegion(wmHelper) val appRegionBounds = letterboxAppWindow.bounds - val appHeight = appRegionBounds.height + val appHeight = appRegionBounds.height() return@add if (bottom) appRegionBounds.bottom == displayBounds.bottom && appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight) diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index db933b30a822..43fd57bf39aa 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -18,10 +18,11 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.content.Intent +import android.graphics.Rect +import android.graphics.Region import android.media.session.MediaController import android.media.session.MediaSessionManager -import android.tools.datatypes.Rect -import android.tools.datatypes.Region +import android.tools.datatypes.coversMoreThan import android.tools.device.apphelpers.StandardAppHelper import android.tools.helpers.FIND_TIMEOUT import android.tools.helpers.SYSTEMUI_PACKAGE @@ -62,7 +63,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : /** Drags the PIP window to the provided final coordinates without releasing the pointer. */ fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) { - val initWindowRect = getWindowRect(wmHelper).clone() + val initWindowRect = Rect(getWindowRect(wmHelper)) // initial pointer at the center of the window val initialCoord = @@ -101,7 +102,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : * @throws IllegalStateException if default display bounds are not available */ fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int) { - val initWindowRect = getWindowRect(wmHelper).clone() + val initWindowRect = Rect(getWindowRect(wmHelper)) // initial pointer at the center of the window val startX = initWindowRect.centerX() @@ -153,12 +154,12 @@ open class PipAppHelper(instrumentation: Instrumentation) : val windowRect = getWindowRect(wmHelper) // first pointer's initial x coordinate is halfway between the left edge and the center - val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat() + val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat() // second pointer's initial x coordinate is halfway between the right edge and the center - val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat() + val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat() // horizontal distance the window should increase by - val distIncrease = windowRect.width * percent + val distIncrease = windowRect.width() * percent // final x-coordinates val finalLeftX = initLeftX - (distIncrease / 2) @@ -183,7 +184,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : adjustedSteps ) - waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect)) + waitForPipWindowToExpandFrom(wmHelper, Region(windowRect)) } /** @@ -201,12 +202,12 @@ open class PipAppHelper(instrumentation: Instrumentation) : val windowRect = getWindowRect(wmHelper) // first pointer's initial x coordinate is halfway between the left edge and the center - val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat() + val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat() // second pointer's initial x coordinate is halfway between the right edge and the center - val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat() + val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat() // decrease by the distance specified through the percentage - val distDecrease = windowRect.width * percent + val distDecrease = windowRect.width() * percent // get the final x-coordinates and make sure they are not passing the center of the window val finalLeftX = Math.min(initLeftX + (distDecrease / 2), windowRect.centerX().toFloat()) @@ -231,7 +232,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : adjustedSteps ) - waitForPipWindowToMinimizeFrom(wmHelper, Region.from(windowRect)) + waitForPipWindowToMinimizeFrom(wmHelper, Region(windowRect)) } /** @@ -375,7 +376,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : uiDevice.click(windowRect.centerX(), windowRect.centerY()) Log.d(TAG, "Wait for app transition to end") wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() - waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect)) + waitForPipWindowToExpandFrom(wmHelper, Region(windowRect)) } private fun waitForPipWindowToExpandFrom( diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 9198ae184b18..45260bddd355 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -18,7 +18,10 @@ package="com.android.server.wm.flicker.testapp"> <uses-sdk android:minSdkVersion="29" - android:targetSdkVersion="29"/> + android:targetSdkVersion="35"/> + + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <application android:allowBackup="false" android:supportsRtl="true"> <uses-library android:name="androidx.window.extensions" android:required="false"/> @@ -71,6 +74,7 @@ android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity" android:theme="@style/CutoutShortEdges" android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" + android:showWhenLocked="true" android:label="SeamlessActivity" android:exported="true"> <intent-filter> @@ -107,7 +111,7 @@ android:immersive="true" android:resizeableActivity="true" android:screenOrientation="portrait" - android:theme="@android:style/Theme.NoTitleBar" + android:theme="@style/OptOutEdgeToEdge.NoTitleBar" android:configChanges="screenSize" android:label="PortraitImmersiveActivity" android:exported="true"> @@ -119,7 +123,7 @@ <activity android:name=".LaunchTransparentActivity" android:resizeableActivity="false" android:screenOrientation="portrait" - android:theme="@android:style/Theme" + android:theme="@style/OptOutEdgeToEdge" android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity" android:label="LaunchTransparentActivity" android:exported="true"> @@ -273,7 +277,7 @@ android:exported="true" android:label="MailActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity" - android:theme="@style/Theme.AppCompat.Light"> + android:theme="@style/OptOutEdgeToEdge.AppCompatTheme"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> @@ -282,7 +286,7 @@ <activity android:name=".GameActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity" android:immersive="true" - android:theme="@android:style/Theme.NoTitleBar" + android:theme="@style/OptOutEdgeToEdge.NoTitleBar" android:configChanges="screenSize" android:label="GameActivity" android:exported="true"> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index 86c21906163f..917aec1e809d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -14,66 +14,71 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" android:background="@android:color/holo_orange_light"> - <Button - android:id="@+id/launch_secondary_activity_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Secondary Activity" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> - <Button - android:id="@+id/launch_secondary_activity_rtl_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="RIGHT_TO_LEFT" - android:text="Launch Secondary Activity in RTL" /> + <Button + android:id="@+id/launch_secondary_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Secondary Activity" /> - <Button - android:id="@+id/launch_secondary_activity_horizontally_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchSecondaryActivity" - android:tag="BOTTOM_TO_TOP" - android:text="Launch Secondary Activity Horizontally" /> + <Button + android:id="@+id/launch_secondary_activity_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Secondary Activity in RTL" /> - <Button - android:id="@+id/launch_placeholder_split_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchPlaceholderSplit" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Placeholder Split" /> + <Button + android:id="@+id/launch_secondary_activity_horizontally_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="BOTTOM_TO_TOP" + android:text="Launch Secondary Activity Horizontally" /> - <Button - android:id="@+id/launch_always_expand_activity_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchAlwaysExpandActivity" - android:text="Launch Always Expand Activity" /> + <Button + android:id="@+id/launch_placeholder_split_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Placeholder Split" /> - <Button - android:id="@+id/launch_placeholder_split_rtl_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchPlaceholderSplit" - android:tag="RIGHT_TO_LEFT" - android:text="Launch Placeholder Split in RTL" /> + <Button + android:id="@+id/launch_always_expand_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchAlwaysExpandActivity" + android:text="Launch Always Expand Activity" /> - <Button - android:id="@+id/launch_trampoline_button" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:onClick="launchTrampolineActivity" - android:tag="LEFT_TO_RIGHT" - android:text="Launch Trampoline Activity" /> + <Button + android:id="@+id/launch_placeholder_split_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Placeholder Split in RTL" /> -</LinearLayout> + <Button + android:id="@+id/launch_trampoline_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchTrampolineActivity" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Trampoline Activity" /> + + </LinearLayout> +</ScrollView> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml index 9b742d96e35b..47d113717ae0 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml @@ -16,7 +16,19 @@ --> <resources> - <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="OptOutEdgeToEdge.AppCompatTheme" parent="@style/Theme.AppCompat.Light"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + + <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowBackground">@android:color/darker_gray</item> </style> @@ -32,7 +44,7 @@ <item name="android:windowLayoutInDisplayCutoutMode">never</item> </style> - <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="DialogTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowAnimationStyle">@null</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@null</item> @@ -43,18 +55,18 @@ <item name="android:windowSoftInputMode">stateUnchanged</item> </style> - <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item> <item name="android:backgroundDimEnabled">false</item> </style> - <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault"> + <style name="no_starting_window" parent="@style/OptOutEdgeToEdge"> <item name="android:windowDisablePreview">true</item> </style> - <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault"> + <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge"> <!-- Splashscreen Attributes --> <item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item> <!-- Here we want to match the duration of our AVD --> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java index 23fa91c37728..2df3da63436b 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java @@ -16,7 +16,6 @@ package com.android.server.wm.flicker.testapp; -import androidx.annotation.NonNull; import android.app.Activity; import android.content.Intent; import android.os.Bundle; @@ -103,18 +102,13 @@ public class ActivityEmbeddingMainActivity extends Activity { } private static SplitPairRule createSplitPairRules(@NonNull String layoutDirection) { - final Set<SplitPairFilter> pairFilters = new HashSet<>(); - final SplitPairFilter activitiesPair = new SplitPairFilter( - ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT, - ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT, - null /* secondaryActivityIntentAction */); - pairFilters.add(activitiesPair); + final Set<SplitPairFilter> pairFilters = getSplitPairFilters(); final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL) .setLayoutDirection(parseLayoutDirection(layoutDirection)) .build(); // Setting thresholds to ALWAYS_ALLOW values to make it easy for running on all devices. - final SplitPairRule rule = new SplitPairRule.Builder(pairFilters) + return new SplitPairRule.Builder(pairFilters) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) @@ -122,7 +116,22 @@ public class ActivityEmbeddingMainActivity extends Activity { .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW) .build(); - return rule; + } + + @NonNull + private static Set<SplitPairFilter> getSplitPairFilters() { + final Set<SplitPairFilter> pairFilters = new HashSet<>(); + final SplitPairFilter mainAndSecondaryActivitiesPair = new SplitPairFilter( + ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT, + ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT, + null /* secondaryActivityIntentAction */); + pairFilters.add(mainAndSecondaryActivitiesPair); + final SplitPairFilter mainAndTrampolineActivitiesPair = new SplitPairFilter( + ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT, + ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT, + null /* secondaryActivityIntentAction */); + pairFilters.add(mainAndTrampolineActivitiesPair); + return pairFilters; } private static SplitPlaceholderRule createSplitPlaceholderRules( diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java index c92b82b896f2..a86ba5f76374 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java @@ -125,7 +125,7 @@ public class BubbleHelper { .setContentTitle("BubbleChat") .setContentIntent(PendingIntent.getActivity(mContext, 0, new Intent(mContext, LaunchBubbleActivity.class), - PendingIntent.FLAG_UPDATE_CURRENT)) + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE)) .setStyle(new Notification.MessagingStyle(chatBot) .setConversationTitle("BubbleChat") .addMessage("BubbleChat", @@ -140,7 +140,7 @@ public class BubbleHelper { Intent target = new Intent(mContext, BubbleActivity.class); target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id); PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); return new Notification.BubbleMetadata.Builder() .setIntent(bubbleIntent) diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java index dea34442464d..37332c9712f5 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java @@ -17,6 +17,9 @@ package com.android.server.wm.flicker.testapp; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.Activity; import android.app.Person; import android.content.Context; @@ -24,6 +27,7 @@ import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; import android.graphics.drawable.Icon; +import android.os.Build; import android.os.Bundle; import android.view.View; @@ -36,6 +40,13 @@ public class LaunchBubbleActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) { + // POST_NOTIFICATIONS permission required for notification post sdk 33. + requestPermissions(new String[] { POST_NOTIFICATIONS }, 0); + } + addInboxShortcut(getApplicationContext()); mBubbleHelper = BubbleHelper.getInstance(this); setContentView(R.layout.activity_main); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java index a4dd5753539d..d6427abcc65a 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java @@ -16,6 +16,9 @@ package com.android.server.wm.flicker.testapp; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.Activity; import android.app.Notification; import android.app.NotificationChannel; @@ -23,6 +26,7 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.view.WindowManager; import android.widget.Button; @@ -34,6 +38,13 @@ public class NotificationActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) { + // POST_NOTIFICATIONS permission required for notification post sdk 33. + requestPermissions(new String[] { POST_NOTIFICATIONS }, 0); + } + WindowManager.LayoutParams p = getWindow().getAttributes(); p.layoutInDisplayCutoutMode = WindowManager.LayoutParams .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java index 1ab8ddbe20e2..27eb5a06451a 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java @@ -198,7 +198,7 @@ public class PipActivity extends Activity { filter.addAction(ACTION_SET_REQUESTED_ORIENTATION); filter.addAction(ACTION_ENTER_PIP); filter.addAction(ACTION_ASPECT_RATIO); - registerReceiver(mBroadcastReceiver, filter); + registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED); handleIntentExtra(getIntent()); } @@ -222,8 +222,8 @@ public class PipActivity extends Activity { private RemoteAction buildRemoteAction(Icon icon, String label, String action) { final Intent intent = new Intent(action); - final PendingIntent pendingIntent = - PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); return new RemoteAction(icon, label, label, pendingIntent); } diff --git a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java index 2ed4fec4a93c..c52be7c2b0c6 100644 --- a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java +++ b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java @@ -27,6 +27,9 @@ import android.util.Log; import androidx.test.core.app.ApplicationProvider; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.compatibility.common.util.AdoptShellPermissionsRule; + +import org.junit.Rule; import org.junit.Test; import java.io.FileOutputStream; @@ -46,6 +49,12 @@ public class Helper { private static final long BLOCK_SIZE = 4096; + @Rule + public final AdoptShellPermissionsRule mAdoptShellPermissionsRule = + new AdoptShellPermissionsRule( + InstrumentationRegistry.getInstrumentation().getUiAutomation(), + android.Manifest.permission.SETUP_FSVERITY); + @Test public void prepareTest() throws Exception { Context context = ApplicationProvider.getApplicationContext(); diff --git a/tests/HugeBackup/AndroidManifest.xml b/tests/HugeBackup/AndroidManifest.xml index 92445dddef5a..945e59bd36ed 100644 --- a/tests/HugeBackup/AndroidManifest.xml +++ b/tests/HugeBackup/AndroidManifest.xml @@ -25,7 +25,7 @@ android:versionName="1.0"> <!-- The backup/restore mechanism was introduced in API version 8 --> - <uses-sdk android:minSdkVersion="21" + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8"/> <application android:label="Huge Backup" diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index d17cd1fa2c3b..f367c38b06e9 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -18,26 +18,33 @@ android_test { "src/**/*.java", "src/**/*.kt", ], + asset_dirs: ["assets"], kotlincflags: [ "-Werror", ], platform_apis: true, certificate: "platform", static_libs: [ + "android.view.flags-aconfig-java", "androidx.test.core", "androidx.test.ext.junit", "androidx.test.ext.truth", "androidx.test.rules", "androidx.test.runner", "androidx.test.uiautomator_uiautomator", - "servicestests-utils", + "collector-device-lib", + "compatibility-device-util-axt", + "cts-input-lib", + "cts-wm-util", "flag-junit", "frameworks-base-testutils", "hamcrest-library", "kotlin-test", "mockito-target-minus-junit4", "platform-test-annotations", + "platform-screenshot-diff-core", "services.core.unboosted", + "servicestests-utils", "testables", "testng", "truth", diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml index 3b723ddf811f..a05d08ccceba 100644 --- a/tests/Input/AndroidManifest.xml +++ b/tests/Input/AndroidManifest.xml @@ -22,6 +22,8 @@ <uses-permission android:name="android.permission.MONITOR_INPUT"/> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <application android:label="InputTest" android:debuggable="true"> diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml index f602c5124e77..8db37058af2b 100644 --- a/tests/Input/AndroidTest.xml +++ b/tests/Input/AndroidTest.xml @@ -28,4 +28,10 @@ <!-- Take screenshot upon test failure --> <option name="screenshot-on-failure" value="true" /> </object> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="input_.*" /> + <!-- Pull files created by tests, like the output of screenshot tests --> + <option name="directory-keys" value="/storage/emulated/0/InputTests" /> + <option name="collect-on-run-ended-only" value="false" /> + </metrics_collector> </configuration> diff --git a/tests/Input/assets/testPointerFillStyle.png b/tests/Input/assets/testPointerFillStyle.png Binary files differnew file mode 100644 index 000000000000..b2354f8f4799 --- /dev/null +++ b/tests/Input/assets/testPointerFillStyle.png diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png Binary files differnew file mode 100644 index 000000000000..54d37c24afc6 --- /dev/null +++ b/tests/Input/assets/testPointerScale.png diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index f6f766a6a5b0..3c72498082e4 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -19,22 +19,28 @@ package com.android.server.input import android.content.Context import android.content.ContextWrapper +import android.hardware.display.DisplayManager import android.hardware.display.DisplayViewport +import android.hardware.display.VirtualDisplay import android.hardware.input.InputManager import android.hardware.input.InputManagerGlobal +import android.os.InputEventInjectionSync +import android.os.SystemClock import android.os.test.TestLooper import android.platform.test.annotations.Presubmit -import android.platform.test.annotations.RequiresFlagsDisabled import android.platform.test.flag.junit.DeviceFlagsValueProvider import android.provider.Settings +import android.view.View.OnKeyListener +import android.view.InputDevice +import android.view.KeyEvent +import android.view.SurfaceHolder +import android.view.SurfaceView import android.test.mock.MockContentResolver -import android.view.Display -import android.view.PointerIcon import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.util.test.FakeSettingsProvider import com.google.common.truth.Truth.assertThat +import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity import org.junit.After -import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule @@ -43,21 +49,16 @@ import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.Mockito.`when` -import org.mockito.Mockito.clearInvocations -import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.verifyZeroInteractions import org.mockito.junit.MockitoJUnit import org.mockito.stubbing.OngoingStubbing -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit /** * Tests for {@link InputManagerService}. @@ -141,7 +142,9 @@ class InputManagerServiceTests { fun testInputSettingsUpdatedOnSystemRunning() { verifyZeroInteractions(native) - service.systemRunning() + runWithShellPermissionIdentity { + service.systemRunning() + } verify(native).setPointerSpeed(anyInt()) verify(native).setTouchpadPointerSpeed(anyInt()) @@ -166,203 +169,6 @@ class InputManagerServiceTests { localService.setDisplayViewports(viewports) verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java)) verify(native).setPointerDisplayId(displayId) - - val x = 42f - val y = 314f - service.onPointerDisplayIdChanged(displayId, x, y) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(displayId, x, y) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun testSetVirtualMousePointerDisplayId() { - // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked - // until the native callback happens. - var countDownLatch = CountDownLatch(1) - val overrideDisplayId = 123 - Thread { - assertTrue("Setting virtual pointer display should succeed", - localService.setVirtualMousePointerDisplayId(overrideDisplayId)) - countDownLatch.countDown() - }.start() - assertFalse("Setting virtual pointer display should block", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - - val x = 42f - val y = 314f - service.onPointerDisplayIdChanged(overrideDisplayId, x, y) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, x, y) - assertTrue("Native callback unblocks calling thread", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - verify(native).setPointerDisplayId(overrideDisplayId) - - // Ensure that setting the same override again succeeds immediately. - assertTrue("Setting the same virtual mouse pointer displayId again should succeed", - localService.setVirtualMousePointerDisplayId(overrideDisplayId)) - - // Ensure that we did not query WM for the pointerDisplayId when setting the override - verify(wmCallbacks, never()).pointerDisplayId - - // Unset the virtual mouse pointer displayId, and ensure that we query WM for the new - // pointer displayId and the calling thread is blocked until the native callback happens. - countDownLatch = CountDownLatch(1) - val pointerDisplayId = 42 - `when`(wmCallbacks.pointerDisplayId).thenReturn(pointerDisplayId) - Thread { - assertTrue("Unsetting virtual mouse pointer displayId should succeed", - localService.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY)) - countDownLatch.countDown() - }.start() - assertFalse("Unsetting virtual mouse pointer displayId should block", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - - service.onPointerDisplayIdChanged(pointerDisplayId, x, y) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(pointerDisplayId, x, y) - assertTrue("Native callback unblocks calling thread", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - verify(native).setPointerDisplayId(pointerDisplayId) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun testSetVirtualMousePointerDisplayId_unsuccessfulUpdate() { - // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked - // until the native callback happens. - val countDownLatch = CountDownLatch(1) - val overrideDisplayId = 123 - Thread { - assertFalse("Setting virtual pointer display should be unsuccessful", - localService.setVirtualMousePointerDisplayId(overrideDisplayId)) - countDownLatch.countDown() - }.start() - assertFalse("Setting virtual pointer display should block", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - - val x = 42f - val y = 314f - // Assume the native callback updates the pointerDisplayId to the incorrect value. - service.onPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y) - assertTrue("Native callback unblocks calling thread", - countDownLatch.await(100, TimeUnit.MILLISECONDS)) - verify(native).setPointerDisplayId(overrideDisplayId) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun testSetVirtualMousePointerDisplayId_competingRequests() { - val firstRequestSyncLatch = CountDownLatch(1) - doAnswer { - firstRequestSyncLatch.countDown() - }.`when`(native).setPointerDisplayId(anyInt()) - - val firstRequestLatch = CountDownLatch(1) - val firstOverride = 123 - Thread { - assertFalse("Setting virtual pointer display from thread 1 should be unsuccessful", - localService.setVirtualMousePointerDisplayId(firstOverride)) - firstRequestLatch.countDown() - }.start() - assertFalse("Setting virtual pointer display should block", - firstRequestLatch.await(100, TimeUnit.MILLISECONDS)) - - assertTrue("Wait for first thread's request should succeed", - firstRequestSyncLatch.await(100, TimeUnit.MILLISECONDS)) - - val secondRequestLatch = CountDownLatch(1) - val secondOverride = 42 - Thread { - assertTrue("Setting virtual mouse pointer from thread 2 should be successful", - localService.setVirtualMousePointerDisplayId(secondOverride)) - secondRequestLatch.countDown() - }.start() - assertFalse("Setting virtual mouse pointer should block", - secondRequestLatch.await(100, TimeUnit.MILLISECONDS)) - - val x = 42f - val y = 314f - // Assume the native callback updates directly to the second request. - service.onPointerDisplayIdChanged(secondOverride, x, y) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(secondOverride, x, y) - assertTrue("Native callback unblocks first thread", - firstRequestLatch.await(100, TimeUnit.MILLISECONDS)) - assertTrue("Native callback unblocks second thread", - secondRequestLatch.await(100, TimeUnit.MILLISECONDS)) - verify(native, times(2)).setPointerDisplayId(anyInt()) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun onDisplayRemoved_resetAllAdditionalInputProperties() { - setVirtualMousePointerDisplayIdAndVerify(10) - - localService.setPointerIconVisible(false, 10) - verify(native).setPointerIconVisibility(10, false) - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - localService.setMousePointerAccelerationEnabled(false, 10) - verify(native).setMousePointerAccelerationEnabled(10, false) - - service.onDisplayRemoved(10) - verify(native).setPointerIconVisibility(10, true) - verify(native).displayRemoved(eq(10)) - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - verify(native).setMousePointerAccelerationEnabled(10, true) - verifyNoMoreInteractions(native) - - // This call should not block because the virtual mouse pointer override was never removed. - localService.setVirtualMousePointerDisplayId(10) - - verify(native).setPointerDisplayId(eq(10)) - verifyNoMoreInteractions(native) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun updateAdditionalInputPropertiesForOverrideDisplay() { - setVirtualMousePointerDisplayIdAndVerify(10) - - localService.setPointerIconVisible(false, 10) - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - verify(native).setPointerIconVisibility(10, false) - localService.setMousePointerAccelerationEnabled(false, 10) - verify(native).setMousePointerAccelerationEnabled(10, false) - - localService.setPointerIconVisible(true, 10) - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED)) - verify(native).setPointerIconVisibility(10, true) - localService.setMousePointerAccelerationEnabled(true, 10) - verify(native).setMousePointerAccelerationEnabled(10, true) - - localService.setPointerIconVisible(false, 20) - verify(native).setPointerIconVisibility(20, false) - localService.setMousePointerAccelerationEnabled(false, 20) - verify(native).setMousePointerAccelerationEnabled(20, false) - verifyNoMoreInteractions(native) - - clearInvocations(native) - setVirtualMousePointerDisplayIdAndVerify(20) - - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) - } - - @RequiresFlagsDisabled(com.android.input.flags.Flags.FLAG_ENABLE_POINTER_CHOREOGRAPHER) - @Test - fun setAdditionalInputPropertiesBeforeOverride() { - localService.setPointerIconVisible(false, 10) - localService.setMousePointerAccelerationEnabled(false, 10) - - verify(native).setPointerIconVisibility(10, false) - verify(native).setMousePointerAccelerationEnabled(10, false) - verifyNoMoreInteractions(native) - - setVirtualMousePointerDisplayIdAndVerify(10) - - verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL)) } @Test @@ -399,18 +205,172 @@ class InputManagerServiceTests { verify(native, times(2)).changeKeyboardLayoutAssociation() } - private fun setVirtualMousePointerDisplayIdAndVerify(overrideDisplayId: Int) { - val thread = Thread { localService.setVirtualMousePointerDisplayId(overrideDisplayId) } - thread.start() + private fun createVirtualDisplays(count: Int): List<VirtualDisplay> { + val displayManager: DisplayManager = context.getSystemService( + DisplayManager::class.java + ) as DisplayManager + val virtualDisplays = mutableListOf<VirtualDisplay>() + for (i in 0 until count) { + virtualDisplays.add(displayManager.createVirtualDisplay( + /* displayName= */ "testVirtualDisplay$i", + /* width= */ 100, + /* height= */ 100, + /* densityDpi= */ 100, + /* surface= */ null, + /* flags= */ 0 + )) + } + return virtualDisplays + } + + // Helper function that creates a KeyEvent with Keycode A with the given action + private fun createKeycodeAEvent(inputDevice: InputDevice, action: Int): KeyEvent { + val eventTime = SystemClock.uptimeMillis() + return KeyEvent( + /* downTime= */ eventTime, + /* eventTime= */ eventTime, + /* action= */ action, + /* code= */ KeyEvent.KEYCODE_A, + /* repeat= */ 0, + /* metaState= */ 0, + /* deviceId= */ inputDevice.id, + /* scanCode= */ 0, + /* flags= */ KeyEvent.FLAG_FROM_SYSTEM, + /* source= */ InputDevice.SOURCE_KEYBOARD + ) + } + + private fun createInputDevice(): InputDevice { + return InputDevice.Builder() + .setId(123) + .setName("abc") + .setDescriptor("def") + .setSources(InputDevice.SOURCE_KEYBOARD) + .build() + } - // Allow some time for the set override call to park while waiting for the native callback. - Thread.sleep(100 /*millis*/) - verify(native).setPointerDisplayId(overrideDisplayId) + @Test + fun addUniqueIdAssociationByDescriptor_verifyAssociations() { + // Overall goal is to have 2 displays and verify that events from the InputDevice are + // sent only to the view that is on the associated display. + // So, associate the InputDevice with display 1, then send and verify KeyEvents. + // Then remove associations, then associate the InputDevice with display 2, then send + // and verify commands. + + // Make 2 virtual displays with some mock SurfaceViews + val mockSurfaceView1 = mock(SurfaceView::class.java) + val mockSurfaceView2 = mock(SurfaceView::class.java) + val mockSurfaceHolder1 = mock(SurfaceHolder::class.java) + `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1) + val mockSurfaceHolder2 = mock(SurfaceHolder::class.java) + `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2) + + val virtualDisplays = createVirtualDisplays(2) + + // Simulate an InputDevice + val inputDevice = createInputDevice() + + // Associate input device with display + service.addUniqueIdAssociationByDescriptor( + inputDevice.descriptor, + virtualDisplays[0].display.displayId.toString() + ) + + // Simulate 2 different KeyEvents + val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN) + val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP) + + // Create a mock OnKeyListener object + val mockOnKeyListener = mock(OnKeyListener::class.java) + + // Verify that the event went to Display 1 not Display 2 + service.injectInputEvent(downEvent, InputEventInjectionSync.NONE) + + // Call the onKey method on the mock OnKeyListener object + mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent) + mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent) + + // Verify that the onKey method was called with the expected arguments + verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent) + verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent) + + // Remove association + service.removeUniqueIdAssociationByDescriptor(inputDevice.descriptor) + + // Associate with Display 2 + service.addUniqueIdAssociationByDescriptor( + inputDevice.descriptor, + virtualDisplays[1].display.displayId.toString() + ) + + // Simulate a KeyEvent + service.injectInputEvent(upEvent, InputEventInjectionSync.NONE) + + // Verify that the event went to Display 2 not Display 1 + verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent) + verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent) + } - service.onPointerDisplayIdChanged(overrideDisplayId, 0f, 0f) - testLooper.dispatchNext() - verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, 0f, 0f) - thread.join(100 /*millis*/) + @Test + fun addUniqueIdAssociationByPort_verifyAssociations() { + // Overall goal is to have 2 displays and verify that events from the InputDevice are + // sent only to the view that is on the associated display. + // So, associate the InputDevice with display 1, then send and verify KeyEvents. + // Then remove associations, then associate the InputDevice with display 2, then send + // and verify commands. + + // Make 2 virtual displays with some mock SurfaceViews + val mockSurfaceView1 = mock(SurfaceView::class.java) + val mockSurfaceView2 = mock(SurfaceView::class.java) + val mockSurfaceHolder1 = mock(SurfaceHolder::class.java) + `when`(mockSurfaceView1.holder).thenReturn(mockSurfaceHolder1) + val mockSurfaceHolder2 = mock(SurfaceHolder::class.java) + `when`(mockSurfaceView2.holder).thenReturn(mockSurfaceHolder2) + + val virtualDisplays = createVirtualDisplays(2) + + // Simulate an InputDevice + val inputDevice = createInputDevice() + + // Associate input device with display + service.addUniqueIdAssociationByPort( + inputDevice.name, + virtualDisplays[0].display.displayId.toString() + ) + + // Simulate 2 different KeyEvents + val downEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_DOWN) + val upEvent = createKeycodeAEvent(inputDevice, KeyEvent.ACTION_UP) + + // Create a mock OnKeyListener object + val mockOnKeyListener = mock(OnKeyListener::class.java) + + // Verify that the event went to Display 1 not Display 2 + service.injectInputEvent(downEvent, InputEventInjectionSync.NONE) + + // Call the onKey method on the mock OnKeyListener object + mockOnKeyListener.onKey(mockSurfaceView1, /* keyCode= */ KeyEvent.KEYCODE_A, downEvent) + mockOnKeyListener.onKey(mockSurfaceView2, /* keyCode= */ KeyEvent.KEYCODE_A, upEvent) + + // Verify that the onKey method was called with the expected arguments + verify(mockOnKeyListener).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, downEvent) + verify(mockOnKeyListener, never()).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, downEvent) + + // Remove association + service.removeUniqueIdAssociationByPort(inputDevice.name) + + // Associate with Display 2 + service.addUniqueIdAssociationByPort( + inputDevice.name, + virtualDisplays[1].display.displayId.toString() + ) + + // Simulate a KeyEvent + service.injectInputEvent(upEvent, InputEventInjectionSync.NONE) + + // Verify that the event went to Display 2 not Display 1 + verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent) + verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent) } } diff --git a/tests/Input/src/com/android/server/input/InputShellCommandTest.java b/tests/Input/src/com/android/server/input/InputShellCommandTest.java index f4845a518b20..11f46335f017 100644 --- a/tests/Input/src/com/android/server/input/InputShellCommandTest.java +++ b/tests/Input/src/com/android/server/input/InputShellCommandTest.java @@ -125,6 +125,14 @@ public class InputShellCommandTest { assertThat(mInputEventInjector.mInjectedEvents).isEmpty(); } + @Test + public void testInvalidKeyEventCommandArgsCombination() { + // --duration and --longpress must not be sent together + runCommand("keyevent --duration 1000 --longpress KEYCODE_A"); + + assertThat(mInputEventInjector.mInjectedEvents).isEmpty(); + } + private InputEvent getSingleInjectedInputEvent() { assertThat(mInputEventInjector.mInjectedEvents).hasSize(1); return mInputEventInjector.mInjectedEvents.get(0); diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index e60764f137af..93f97cb4a7ee 100644 --- a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -33,7 +33,6 @@ import android.icu.util.ULocale import android.os.Bundle import android.os.test.TestLooper import android.platform.test.annotations.Presubmit -import android.provider.Settings import android.util.proto.ProtoOutputStream import android.view.InputDevice import android.view.inputmethod.InputMethodInfo @@ -47,9 +46,7 @@ import com.android.test.input.R import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals -import org.junit.Assert.assertNull import org.junit.Assert.assertTrue -import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Rule import org.junit.Test @@ -234,631 +231,332 @@ class KeyboardLayoutManagerTests { } @Test - fun testDefaultUi_getKeyboardLayouts() { - NewSettingsApiFlag(false).use { - val keyboardLayouts = keyboardLayoutManager.keyboardLayouts - assertNotEquals( - "Default UI: Keyboard layout API should not return empty array", - 0, - keyboardLayouts.size - ) - assertTrue( - "Default UI: Keyboard layout API should provide English(US) layout", - hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) - } + fun testGetKeyboardLayouts() { + val keyboardLayouts = keyboardLayoutManager.keyboardLayouts + assertNotEquals( + "Keyboard layout API should not return empty array", + 0, + keyboardLayouts.size + ) + assertTrue( + "Keyboard layout API should provide English(US) layout", + hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) + ) } @Test - fun testNewUi_getKeyboardLayouts() { - NewSettingsApiFlag(true).use { - val keyboardLayouts = keyboardLayoutManager.keyboardLayouts - assertNotEquals( - "New UI: Keyboard layout API should not return empty array", - 0, - keyboardLayouts.size - ) - assertTrue( - "New UI: Keyboard layout API should provide English(US) layout", - hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) - } + fun testGetKeyboardLayout() { + val keyboardLayout = + keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR) + assertEquals("getKeyboardLayout API should return correct Layout from " + + "available layouts", + ENGLISH_US_LAYOUT_DESCRIPTOR, + keyboardLayout!!.descriptor + ) } @Test - fun testDefaultUi_getKeyboardLayoutsForInputDevice() { - NewSettingsApiFlag(false).use { - val keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutsForInputDevice(keyboardDevice.identifier) - assertNotEquals( - "Default UI: getKeyboardLayoutsForInputDevice API should not return empty array", - 0, - keyboardLayouts.size - ) - assertTrue( - "Default UI: getKeyboardLayoutsForInputDevice API should provide English(US) " + - "layout", - hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) + fun testGetSetKeyboardLayoutForInputDevice_withImeInfo() { + val imeSubtype = createImeSubtype() - val vendorSpecificKeyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutsForInputDevice( - vendorSpecificKeyboardDevice.identifier - ) - assertEquals( - "Default UI: getKeyboardLayoutsForInputDevice API should return only vendor " + - "specific layout", - 1, - vendorSpecificKeyboardLayouts.size - ) - assertEquals( - "Default UI: getKeyboardLayoutsForInputDevice API should return vendor specific " + - "layout", - VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR, - vendorSpecificKeyboardLayouts[0].descriptor + keyboardLayoutManager.setKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, + ENGLISH_UK_LAYOUT_DESCRIPTOR + ) + var result = + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype ) - } - } + assertEquals( + "getKeyboardLayoutForInputDevice API should return the set layout", + ENGLISH_UK_LAYOUT_DESCRIPTOR, + result.layoutDescriptor + ) - @Test - fun testNewUi_getKeyboardLayoutsForInputDevice() { - NewSettingsApiFlag(true).use { - val keyboardLayouts = keyboardLayoutManager.keyboardLayouts - assertNotEquals( - "New UI: getKeyboardLayoutsForInputDevice API should not return empty array", - 0, - keyboardLayouts.size - ) - assertTrue( - "New UI: getKeyboardLayoutsForInputDevice API should provide English(US) " + - "layout", - hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) + // This should replace previously set layout + keyboardLayoutManager.setKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + result = + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype ) - } + assertEquals( + "getKeyboardLayoutForInputDevice API should return the last set layout", + ENGLISH_US_LAYOUT_DESCRIPTOR, + result.layoutDescriptor + ) } @Test - fun testDefaultUi_getSetCurrentKeyboardLayoutForInputDevice() { - NewSettingsApiFlag(false).use { - assertNull( - "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null if " + - "nothing was set", - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - - keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier, - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - val keyboardLayout = - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - assertEquals( - "Default UI: getCurrentKeyboardLayoutForInputDevice API should return the set " + - "layout", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayout + fun testGetKeyboardLayoutListForInputDevice() { + // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts + var keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + createImeSubtypeForLanguageTag("hi-Latn") + ) + assertNotEquals( + "getKeyboardLayoutListForInputDevice API should return the list of " + + "supported layouts with matching script code", + 0, + keyboardLayouts.size + ) + assertTrue("getKeyboardLayoutListForInputDevice API should return a list " + + "containing English(US) layout for hi-Latn", + containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) + ) + assertTrue("getKeyboardLayoutListForInputDevice API should return a list " + + "containing English(No script code) layout for hi-Latn", + containsLayout( + keyboardLayouts, + createLayoutDescriptor("keyboard_layout_english_without_script_code") ) - } - } + ) - @Test - fun testNewUi_getSetCurrentKeyboardLayoutForInputDevice() { - NewSettingsApiFlag(true).use { - keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier, - ENGLISH_US_LAYOUT_DESCRIPTOR + // Check Layouts for "hi" which by default uses 'Deva' script. + keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + createImeSubtypeForLanguageTag("hi") ) - assertNull( - "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null " + - "even after setCurrentKeyboardLayoutForInputDevice", - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - } - } + assertEquals("getKeyboardLayoutListForInputDevice API should return empty " + + "list if no supported layouts available", + 0, + keyboardLayouts.size + ) - @Test - fun testDefaultUi_getEnabledKeyboardLayoutsForInputDevice() { - NewSettingsApiFlag(false).use { - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR + // If user manually selected some layout, always provide it in the layout list + val imeSubtype = createImeSubtypeForLanguageTag("hi") + keyboardLayoutManager.setKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + imeSubtype ) - - val keyboardLayouts = - keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice( - keyboardDevice.identifier - ) - assertEquals( - "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return added " + - "layout", + assertEquals("getKeyboardLayoutListForInputDevice API should return user " + + "selected layout even if the script is incompatible with IME", 1, - keyboardLayouts.size - ) - assertEquals( - "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return " + - "English(US) layout", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayouts[0] - ) - assertEquals( - "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " + - "English(US) layout (Auto select the first enabled layout)", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - - keyboardLayoutManager.removeKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR - ) - assertEquals( - "Default UI: getKeyboardLayoutsForInputDevice API should return 0 layouts", - 0, - keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice( - keyboardDevice.identifier - ).size - ) - assertNull( - "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null after " + - "the enabled layout is removed", - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - } - } - - @Test - fun testNewUi_getEnabledKeyboardLayoutsForInputDevice() { - NewSettingsApiFlag(true).use { - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR - ) - - assertEquals( - "New UI: getEnabledKeyboardLayoutsForInputDevice API should return always return " + - "an empty array", - 0, - keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice( - keyboardDevice.identifier - ).size - ) - assertNull( - "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null", - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - } - } - - @Test - fun testDefaultUi_switchKeyboardLayout() { - NewSettingsApiFlag(false).use { - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR - ) - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - assertEquals( - "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " + - "English(US) layout", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - - keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1) - - // Throws null pointer because trying to show toast using TestLooper - assertThrows(NullPointerException::class.java) { testLooper.dispatchAll() } - assertEquals("Default UI: getCurrentKeyboardLayoutForInputDevice API should return " + - "English(UK) layout", - ENGLISH_UK_LAYOUT_DESCRIPTOR, - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - } - } - - @Test - fun testNewUi_switchKeyboardLayout() { - NewSettingsApiFlag(true).use { - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR - ) - keyboardLayoutManager.addKeyboardLayoutForInputDevice( - keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - - keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1) - testLooper.dispatchAll() - - assertNull("New UI: getCurrentKeyboardLayoutForInputDevice API should always return " + - "null", - keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice( - keyboardDevice.identifier - ) - ) - } - } - - @Test - fun testDefaultUi_getKeyboardLayout() { - NewSettingsApiFlag(false).use { - val keyboardLayout = - keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR) - assertEquals("Default UI: getKeyboardLayout API should return correct Layout from " + - "available layouts", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayout!!.descriptor - ) - } - } - - @Test - fun testNewUi_getKeyboardLayout() { - NewSettingsApiFlag(true).use { - val keyboardLayout = - keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR) - assertEquals("New UI: getKeyboardLayout API should return correct Layout from " + - "available layouts", - ENGLISH_US_LAYOUT_DESCRIPTOR, - keyboardLayout!!.descriptor - ) - } - } - - @Test - fun testDefaultUi_getSetKeyboardLayoutForInputDevice_WithImeInfo() { - NewSettingsApiFlag(false).use { - val imeSubtype = createImeSubtype() - keyboardLayoutManager.setKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, - ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - assertEquals( - "Default UI: getKeyboardLayoutForInputDevice API should always return " + - "KeyboardLayoutSelectionResult.FAILED", - KeyboardLayoutSelectionResult.FAILED, - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype - ) - ) - } - } - - @Test - fun testNewUi_getSetKeyboardLayoutForInputDevice_withImeInfo() { - NewSettingsApiFlag(true).use { - val imeSubtype = createImeSubtype() - - keyboardLayoutManager.setKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, - ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - var result = - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype - ) - assertEquals( - "New UI: getKeyboardLayoutForInputDevice API should return the set layout", - ENGLISH_UK_LAYOUT_DESCRIPTOR, - result.layoutDescriptor - ) - - // This should replace previously set layout - keyboardLayoutManager.setKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - result = - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype - ) - assertEquals( - "New UI: getKeyboardLayoutForInputDevice API should return the last set layout", - ENGLISH_US_LAYOUT_DESCRIPTOR, - result.layoutDescriptor - ) - } - } - - @Test - fun testDefaultUi_getKeyboardLayoutListForInputDevice() { - NewSettingsApiFlag(false).use { - val keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtype() - ) - assertEquals("Default UI: getKeyboardLayoutListForInputDevice API should always " + - "return empty array", - 0, - keyboardLayouts.size - ) - } - } + keyboardLayouts.size + ) - @Test - fun testNewUi_getKeyboardLayoutListForInputDevice() { - NewSettingsApiFlag(true).use { - // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts - var keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + // Special case Japanese: UScript ignores provided script code for certain language tags + // Should manually match provided script codes and then rely on Uscript to derive + // script from language tags and match those. + keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("hi-Latn") - ) - assertNotEquals( - "New UI: getKeyboardLayoutListForInputDevice API should return the list of " + - "supported layouts with matching script code", - 0, - keyboardLayouts.size + createImeSubtypeForLanguageTag("ja-Latn-JP") ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " + - "containing English(US) layout for hi-Latn", - containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " + - "containing English(No script code) layout for hi-Latn", - containsLayout( - keyboardLayouts, - createLayoutDescriptor("keyboard_layout_english_without_script_code") - ) + assertNotEquals( + "getKeyboardLayoutListForInputDevice API should return the list of " + + "supported layouts with matching script code for ja-Latn-JP", + 0, + keyboardLayouts.size + ) + assertTrue("getKeyboardLayoutListForInputDevice API should return a list " + + "containing English(US) layout for ja-Latn-JP", + containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) + ) + assertTrue("getKeyboardLayoutListForInputDevice API should return a list " + + "containing English(No script code) layout for ja-Latn-JP", + containsLayout( + keyboardLayouts, + createLayoutDescriptor("keyboard_layout_english_without_script_code") ) + ) - // Check Layouts for "hi" which by default uses 'Deva' script. - keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + // If script code not explicitly provided for Japanese should rely on Uscript to find + // derived script code and hence no suitable layout will be found. + keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("hi") - ) - assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return empty " + - "list if no supported layouts available", - 0, - keyboardLayouts.size + createImeSubtypeForLanguageTag("ja-JP") ) + assertEquals( + "getKeyboardLayoutListForInputDevice API should return empty list of " + + "supported layouts with matching script code for ja-JP", + 0, + keyboardLayouts.size + ) - // If user manually selected some layout, always provide it in the layout list - val imeSubtype = createImeSubtypeForLanguageTag("hi") - keyboardLayoutManager.setKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype, - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - imeSubtype - ) - assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return user " + - "selected layout even if the script is incompatible with IME", - 1, - keyboardLayouts.size - ) - - // Special case Japanese: UScript ignores provided script code for certain language tags - // Should manually match provided script codes and then rely on Uscript to derive - // script from language tags and match those. - keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("ja-Latn-JP") - ) - assertNotEquals( - "New UI: getKeyboardLayoutListForInputDevice API should return the list of " + - "supported layouts with matching script code for ja-Latn-JP", - 0, - keyboardLayouts.size - ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " + - "containing English(US) layout for ja-Latn-JP", - containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " + - "containing English(No script code) layout for ja-Latn-JP", - containsLayout( - keyboardLayouts, - createLayoutDescriptor("keyboard_layout_english_without_script_code") - ) - ) - - // If script code not explicitly provided for Japanese should rely on Uscript to find - // derived script code and hence no suitable layout will be found. - keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("ja-JP") - ) - assertEquals( - "New UI: getKeyboardLayoutListForInputDevice API should return empty list of " + - "supported layouts with matching script code for ja-JP", - 0, - keyboardLayouts.size - ) - - // If IME doesn't have a corresponding language tag, then should show all available - // layouts no matter the script code. - keyboardLayouts = - keyboardLayoutManager.getKeyboardLayoutListForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, null - ) - assertNotEquals( - "New UI: getKeyboardLayoutListForInputDevice API should return all layouts if" + - "language tag or subtype not provided", - 0, - keyboardLayouts.size - ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Latin " + - "layouts if language tag or subtype not provided", - containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) - ) - assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Cyrillic " + - "layouts if language tag or subtype not provided", - containsLayout( - keyboardLayouts, - createLayoutDescriptor("keyboard_layout_russian") - ) - ) - } - } - - @Test - fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTag() { - NewSettingsApiFlag(true).use { - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTag("en-US"), - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTag("en-GB"), - ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTag("de"), - GERMAN_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTag("fr-FR"), - createLayoutDescriptor("keyboard_layout_french") - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTag("ru"), + // If IME doesn't have a corresponding language tag, then should show all available + // layouts no matter the script code. + keyboardLayouts = + keyboardLayoutManager.getKeyboardLayoutListForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, null + ) + assertNotEquals( + "getKeyboardLayoutListForInputDevice API should return all layouts if" + + "language tag or subtype not provided", + 0, + keyboardLayouts.size + ) + assertTrue("getKeyboardLayoutListForInputDevice API should contain Latin " + + "layouts if language tag or subtype not provided", + containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR) + ) + assertTrue("getKeyboardLayoutListForInputDevice API should contain Cyrillic " + + "layouts if language tag or subtype not provided", + containsLayout( + keyboardLayouts, createLayoutDescriptor("keyboard_layout_russian") ) - assertEquals( - "New UI: getDefaultKeyboardLayoutForInputDevice should return " + - "KeyboardLayoutSelectionResult.FAILED when no layout available", - KeyboardLayoutSelectionResult.FAILED, - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("it") - ) - ) - assertEquals( - "New UI: getDefaultKeyboardLayoutForInputDevice should return " + - "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" + - "available", - KeyboardLayoutSelectionResult.FAILED, - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTag("en-Deva") - ) - ) - } + ) } @Test - fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTagAndLayoutType() { - NewSettingsApiFlag(true).use { - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"), - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"), - createLayoutDescriptor("keyboard_layout_english_us_dvorak") - ) - // Try to match layout type even if country doesn't match - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"), - createLayoutDescriptor("keyboard_layout_english_us_dvorak") - ) - // Choose layout based on layout type priority, if layout type is not provided by IME - // (Qwerty > Dvorak > Extended) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en-US", ""), - ENGLISH_US_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"), - ENGLISH_UK_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"), - GERMAN_LAYOUT_DESCRIPTOR - ) - // Wrong layout type should match with language if provided layout type not available - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"), - GERMAN_LAYOUT_DESCRIPTOR - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"), - createLayoutDescriptor("keyboard_layout_french") - ) - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"), - createLayoutDescriptor("keyboard_layout_russian_qwerty") - ) - // If layout type is empty then prioritize KCM with empty layout type - assertCorrectLayout( - keyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("ru", ""), - createLayoutDescriptor("keyboard_layout_russian") + fun testGetDefaultKeyboardLayoutForInputDevice_withImeLanguageTag() { + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTag("en-US"), + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTag("en-GB"), + ENGLISH_UK_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTag("de"), + GERMAN_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTag("fr-FR"), + createLayoutDescriptor("keyboard_layout_french") + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTag("ru"), + createLayoutDescriptor("keyboard_layout_russian") + ) + assertEquals( + "getDefaultKeyboardLayoutForInputDevice should return " + + "KeyboardLayoutSelectionResult.FAILED when no layout available", + KeyboardLayoutSelectionResult.FAILED, + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + createImeSubtypeForLanguageTag("it") ) - assertEquals("New UI: getDefaultKeyboardLayoutForInputDevice should return " + + ) + assertEquals( + "getDefaultKeyboardLayoutForInputDevice should return " + "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" + "available", - KeyboardLayoutSelectionResult.FAILED, - keyboardLayoutManager.getKeyboardLayoutForInputDevice( - keyboardDevice.identifier, USER_ID, imeInfo, - createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "") - ) + KeyboardLayoutSelectionResult.FAILED, + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + createImeSubtypeForLanguageTag("en-Deva") ) - } + ) } @Test - fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() { - NewSettingsApiFlag(true).use { - val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty") - // Should return English dvorak even if IME current layout is French, since HW says the - // keyboard is a Dvorak keyboard - assertCorrectLayout( - englishDvorakKeyboardDevice, - frenchSubtype, - createLayoutDescriptor("keyboard_layout_english_us_dvorak") + fun testGetDefaultKeyboardLayoutForInputDevice_withImeLanguageTagAndLayoutType() { + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"), + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"), + createLayoutDescriptor("keyboard_layout_english_us_dvorak") + ) + // Try to match layout type even if country doesn't match + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"), + createLayoutDescriptor("keyboard_layout_english_us_dvorak") + ) + // Choose layout based on layout type priority, if layout type is not provided by IME + // (Qwerty > Dvorak > Extended) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-US", ""), + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"), + ENGLISH_UK_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"), + GERMAN_LAYOUT_DESCRIPTOR + ) + // Wrong layout type should match with language if provided layout type not available + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"), + GERMAN_LAYOUT_DESCRIPTOR + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"), + createLayoutDescriptor("keyboard_layout_french") + ) + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"), + createLayoutDescriptor("keyboard_layout_russian_qwerty") + ) + // If layout type is empty then prioritize KCM with empty layout type + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("ru", ""), + createLayoutDescriptor("keyboard_layout_russian") + ) + assertEquals("getDefaultKeyboardLayoutForInputDevice should return " + + "KeyboardLayoutSelectionResult.FAILED when no layout for script code is" + + "available", + KeyboardLayoutSelectionResult.FAILED, + keyboardLayoutManager.getKeyboardLayoutForInputDevice( + keyboardDevice.identifier, USER_ID, imeInfo, + createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "") ) + ) + // If prefer layout with empty country over mismatched country + assertCorrectLayout( + keyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("en-AU", "qwerty"), + ENGLISH_US_LAYOUT_DESCRIPTOR + ) + } - // Back to back changing HW keyboards with same product and vendor ID but different - // language and layout type should configure the layouts correctly. - assertCorrectLayout( - englishQwertyKeyboardDevice, - frenchSubtype, - createLayoutDescriptor("keyboard_layout_english_us") - ) + @Test + fun testGetDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() { + val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty") + // Should return English dvorak even if IME current layout is French, since HW says the + // keyboard is a Dvorak keyboard + assertCorrectLayout( + englishDvorakKeyboardDevice, + frenchSubtype, + createLayoutDescriptor("keyboard_layout_english_us_dvorak") + ) - // Fallback to IME information if the HW provided layout script is incompatible with the - // provided IME subtype - assertCorrectLayout( - englishDvorakKeyboardDevice, - createImeSubtypeForLanguageTagAndLayoutType("ru", ""), - createLayoutDescriptor("keyboard_layout_russian") - ) - } + // Back to back changing HW keyboards with same product and vendor ID but different + // language and layout type should configure the layouts correctly. + assertCorrectLayout( + englishQwertyKeyboardDevice, + frenchSubtype, + createLayoutDescriptor("keyboard_layout_english_us") + ) + + // Fallback to IME information if the HW provided layout script is incompatible with the + // provided IME subtype + assertCorrectLayout( + englishDvorakKeyboardDevice, + createImeSubtypeForLanguageTagAndLayoutType("ru", ""), + createLayoutDescriptor("keyboard_layout_russian") + ) } @Test @@ -867,27 +565,25 @@ class KeyboardLayoutManagerTests { KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) - ExtendedMockito.verify { - FrameworkStatsLog.write( - ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.eq(keyboardDevice.vendorId), - ArgumentMatchers.eq(keyboardDevice.productId), - ArgumentMatchers.eq( - createByteArray( - KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, - LAYOUT_TYPE_DEFAULT, - GERMAN_LAYOUT_NAME, - KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, - "de-Latn", - LAYOUT_TYPE_QWERTZ - ), + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq( + createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + GERMAN_LAYOUT_NAME, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD, + "de-Latn", + LAYOUT_TYPE_QWERTZ ), - ArgumentMatchers.eq(keyboardDevice.deviceBus), - ) - } + ), + ArgumentMatchers.eq(keyboardDevice.deviceBus), + ) } } @@ -897,27 +593,25 @@ class KeyboardLayoutManagerTests { KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz"))) Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id) - ExtendedMockito.verify { - FrameworkStatsLog.write( - ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId), - ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId), - ArgumentMatchers.eq( - createByteArray( - "en", - LAYOUT_TYPE_QWERTY, - ENGLISH_US_LAYOUT_NAME, - KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE, - "de-Latn", - LAYOUT_TYPE_QWERTZ - ) - ), - ArgumentMatchers.eq(keyboardDevice.deviceBus), - ) - } + keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId), + ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId), + ArgumentMatchers.eq( + createByteArray( + "en", + LAYOUT_TYPE_QWERTY, + ENGLISH_US_LAYOUT_NAME, + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE, + "de-Latn", + LAYOUT_TYPE_QWERTZ + ) + ), + ArgumentMatchers.eq(keyboardDevice.deviceBus), + ) } } @@ -925,27 +619,25 @@ class KeyboardLayoutManagerTests { fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() { val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) - ExtendedMockito.verify { - FrameworkStatsLog.write( - ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.eq(keyboardDevice.vendorId), - ArgumentMatchers.eq(keyboardDevice.productId), - ArgumentMatchers.eq( - createByteArray( - KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, - LAYOUT_TYPE_DEFAULT, - "Default", - KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT, - KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, - LAYOUT_TYPE_DEFAULT - ), + keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id) + ExtendedMockito.verify { + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.eq(keyboardDevice.vendorId), + ArgumentMatchers.eq(keyboardDevice.productId), + ArgumentMatchers.eq( + createByteArray( + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT, + "Default", + KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT, + KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG, + LAYOUT_TYPE_DEFAULT ), - ArgumentMatchers.eq(keyboardDevice.deviceBus), - ) - } + ), + ArgumentMatchers.eq(keyboardDevice.deviceBus), + ) } } @@ -953,19 +645,17 @@ class KeyboardLayoutManagerTests { fun testConfigurationNotLogged_onInputDeviceChanged() { val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype())) Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) - ExtendedMockito.verify({ - FrameworkStatsLog.write( - ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), - ArgumentMatchers.anyBoolean(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any(ByteArray::class.java), - ArgumentMatchers.anyInt(), - ) - }, Mockito.times(0)) - } + keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) + ExtendedMockito.verify({ + FrameworkStatsLog.write( + ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED), + ArgumentMatchers.anyBoolean(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any(ByteArray::class.java), + ArgumentMatchers.anyInt(), + ) + }, Mockito.times(0)) } @Test @@ -975,18 +665,16 @@ class KeyboardLayoutManagerTests { Mockito.doReturn(false).`when`(keyboardLayoutManager).isVirtualDevice( ArgumentMatchers.eq(keyboardDevice.id) ) - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) - ExtendedMockito.verify( - notificationManager, - Mockito.times(1) - ).notifyAsUser( - ArgumentMatchers.isNull(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any(), - ArgumentMatchers.any() - ) - } + keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) + ExtendedMockito.verify( + notificationManager, + Mockito.times(1) + ).notifyAsUser( + ArgumentMatchers.isNull(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) } @Test @@ -996,18 +684,16 @@ class KeyboardLayoutManagerTests { Mockito.doReturn(true).`when`(keyboardLayoutManager).isVirtualDevice( ArgumentMatchers.eq(keyboardDevice.id) ) - NewSettingsApiFlag(true).use { - keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) - ExtendedMockito.verify( - notificationManager, - Mockito.never() - ).notifyAsUser( - ArgumentMatchers.isNull(), - ArgumentMatchers.anyInt(), - ArgumentMatchers.any(), - ArgumentMatchers.any() - ) - } + keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id) + ExtendedMockito.verify( + notificationManager, + Mockito.never() + ).notifyAsUser( + ArgumentMatchers.isNull(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) } private fun assertCorrectLayout( @@ -1019,7 +705,7 @@ class KeyboardLayoutManagerTests { device.identifier, USER_ID, imeInfo, imeSubtype ) assertEquals( - "New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout", + "getDefaultKeyboardLayoutForInputDevice should return $expectedLayout", expectedLayout, result.layoutDescriptor ) @@ -1123,21 +809,4 @@ class KeyboardLayoutManagerTests { info.serviceInfo.name = RECEIVER_NAME return info } - - private inner class NewSettingsApiFlag constructor(enabled: Boolean) : AutoCloseable { - init { - Settings.Global.putString( - context.contentResolver, - "settings_new_keyboard_ui", enabled.toString() - ) - } - - override fun close() { - Settings.Global.putString( - context.contentResolver, - "settings_new_keyboard_ui", - "" - ) - } - } } diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 4893d14ad79b..d32cedb24a36 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -21,26 +21,33 @@ import androidx.test.filters.MediumTest import android.app.ActivityManager import android.app.ApplicationExitInfo +import android.content.Context import android.graphics.Rect +import android.hardware.display.DisplayManager import android.os.Build import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS import android.os.SystemClock import android.provider.Settings import android.provider.Settings.Global.HIDE_ERROR_DIALOGS +import android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry import android.testing.PollingCheck -import android.view.InputDevice -import android.view.MotionEvent import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until +import com.android.cts.input.DebugInputRule +import com.android.cts.input.UinputTouchScreen + +import java.time.Duration + import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Assert.fail import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -69,6 +76,9 @@ class AnrTest { private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * Build.HW_TIMEOUT_MULTIPLIER) + @get:Rule + val debugInputRule = DebugInputRule() + @Before fun setUp() { val contentResolver = instrumentation.targetContext.contentResolver @@ -84,12 +94,14 @@ class AnrTest { } @Test + @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Close() { triggerAnr() clickCloseAppOnAnrDialog() } @Test + @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Wait() { triggerAnr() clickWaitOnAnrDialog() @@ -105,7 +117,7 @@ class AnrTest { val closeAppButton: UiObject2? = uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) if (closeAppButton == null) { - fail("Could not find anr dialog") + fail("Could not find anr dialog/close button") return } closeAppButton.click() @@ -150,6 +162,18 @@ class AnrTest { assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason) } + private fun clickOnObject(obj: UiObject2) { + val displayManager = + instrumentation.context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager + val display = displayManager.getDisplay(obj.getDisplayId()) + val touchScreen = UinputTouchScreen(instrumentation, display) + + val rect: Rect = obj.visibleBounds + val pointer = touchScreen.touchDown(rect.centerX(), rect.centerY()) + pointer.lift() + touchScreen.close() + } + private fun triggerAnr() { startUnresponsiveActivity() val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) @@ -160,13 +184,7 @@ class AnrTest { return } - val rect: Rect = obj.visibleBounds - val downTime = SystemClock.uptimeMillis() - val downEvent = MotionEvent.obtain(downTime, downTime, - MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */) - downEvent.source = InputDevice.SOURCE_TOUCHSCREEN - - instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/) + clickOnObject(obj) SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors } @@ -175,5 +193,6 @@ class AnrTest { val flags = " -W -n " val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity" instrumentation.uiAutomation.executeShellCommand(startCmd) + waitForStableWindowGeometry(Duration.ofSeconds(5)) } } diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java index 5f1bc8748db8..87a0de63120e 100644 --- a/tests/Input/src/com/android/test/input/InputDeviceTest.java +++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java @@ -61,6 +61,7 @@ public class InputDeviceTest { assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size()); assertEquals(device.getHostUsiVersion(), outDevice.getHostUsiVersion()); assertEquals(device.getAssociatedDisplayId(), outDevice.getAssociatedDisplayId()); + assertEquals(device.isEnabled(), outDevice.isEnabled()); KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap(); KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap(); @@ -100,7 +101,9 @@ public class InputDeviceTest { .setKeyboardLanguageTag("en-US") .setKeyboardLayoutType("qwerty") .setUsiVersion(new HostUsiVersion(2, 0)) - .setShouldSmoothScroll(true); + .setShouldSmoothScroll(true) + .setAssociatedDisplayId(Display.DEFAULT_DISPLAY) + .setEnabled(false); for (int i = 0; i < 30; i++) { deviceBuilder.addMotionRange( diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt new file mode 100644 index 000000000000..d196b85a7466 --- /dev/null +++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt @@ -0,0 +1,136 @@ +/* + * 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 com.android.test.input + +import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources +import android.os.Environment +import android.view.ContextThemeWrapper +import android.view.PointerIcon +import android.view.flags.Flags.enableVectorCursorA11ySettings +import android.view.flags.Flags.enableVectorCursors +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assume.assumeTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestName +import org.junit.runner.RunWith +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.BitmapMatcher +import platform.test.screenshot.matchers.PixelPerfectMatcher + +/** + * Unit tests for PointerIcon. + * + * Run with: + * atest InputTests:com.android.test.input.PointerIconLoadingTest + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class PointerIconLoadingTest { + private lateinit var context: Context + private lateinit var exactScreenshotMatcher: BitmapMatcher + + @get:Rule + val testName = TestName() + + @get:Rule + val screenshotRule = ScreenshotTestRule(GoldenPathManager( + InstrumentationRegistry.getInstrumentation().getContext(), + ASSETS_PATH, + TEST_OUTPUT_PATH, + PathConfig() + ), disableIconPool = false) + + @Before + fun setUp() { + context = InstrumentationRegistry.getInstrumentation().targetContext + val config = + Configuration(context.resources.configuration).apply { + densityDpi = DENSITY_DPI + screenWidthDp = SCREEN_WIDTH_DP + screenHeightDp = SCREEN_HEIGHT_DP + smallestScreenWidthDp = SCREEN_WIDTH_DP + } + context = context.createConfigurationContext(config) + + exactScreenshotMatcher = PixelPerfectMatcher() + } + + @Test + fun testPointerFillStyle() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val theme: Resources.Theme = context.getResources().newTheme() + theme.setTo(context.getTheme()) + theme.applyStyle( + PointerIcon.vectorFillStyleToResource(PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_GREEN), + /* force= */ true) + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + ContextThemeWrapper(context, theme), + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + /* pointerScale= */ 1f) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + @Test + fun testPointerScale() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val pointerScale = 2f + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + context, + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + pointerScale) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + companion object { + const val DENSITY_DPI = 160 + const val SCREEN_WIDTH_DP = 480 + const val SCREEN_HEIGHT_DP = 800 + const val ASSETS_PATH = "tests/input/assets" + val TEST_OUTPUT_PATH = Environment.getExternalStorageDirectory().absolutePath + + "/InputTests/" + + PointerIconLoadingTest::class.java.simpleName + } +} diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java index c0c60eff0f9f..d03338909bb7 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java @@ -393,6 +393,7 @@ public final class ImeStressTestUtil { mEditText.setFocusableInTouchMode(false); } rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); + rootView.setFitsSystemWindows(true); setContentView(rootView); if (requestFocus) { diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png Binary files differindex 443de8edc2d3..7cdab94534b0 100644 --- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png +++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png Binary files differindex c51da052f4bf..377288d4aa2d 100644 --- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png +++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png Binary files differindex ab23401bf629..68b147338a1d 100644 --- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png +++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenPathManager.kt index 8faf22440828..9f14b136861f 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenPathManager.kt @@ -17,28 +17,25 @@ package com.android.input.screenshot import androidx.test.platform.app.InstrumentationRegistry -import platform.test.screenshot.GoldenImagePathManager +import platform.test.screenshot.GoldenPathManager import platform.test.screenshot.PathConfig -/** A [GoldenImagePathManager] that should be used for all Input screenshot tests. */ -class InputGoldenImagePathManager( - pathConfig: PathConfig, - assetsPathRelativeToBuildRoot: String -) : - GoldenImagePathManager( - appContext = InstrumentationRegistry.getInstrumentation().context, - assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot, - deviceLocalPath = - InstrumentationRegistry.getInstrumentation() - .targetContext - .filesDir - .absolutePath - .toString() + "/input_screenshots", - pathConfig = pathConfig, - ) { +/** A [GoldenPathManager] that should be used for all Input screenshot tests. */ +class InputGoldenPathManager(pathConfig: PathConfig, assetsPathRelativeToBuildRoot: String) : + GoldenPathManager( + appContext = InstrumentationRegistry.getInstrumentation().context, + assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot, + deviceLocalPath = + InstrumentationRegistry.getInstrumentation() + .targetContext + .filesDir + .absolutePath + .toString() + "/input_screenshots", + pathConfig = pathConfig, + ) { override fun toString(): String { // This string is appended to all actual/expected screenshots on the device, so make sure // it is a static value. - return "InputGoldenImagePathManager" + return "InputGoldenPathManager" } -}
\ No newline at end of file +} diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt index 75dab41d3609..2f408964fd8c 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt @@ -44,7 +44,7 @@ class InputScreenshotTestRule( private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) private val screenshotRule = ScreenshotTestRule( - InputGoldenImagePathManager( + InputGoldenPathManager( getEmulatedDevicePathConfig(emulationSpec), assetsPathRelativeToBuildRoot ) diff --git a/tests/Internal/TEST_MAPPING b/tests/Internal/TEST_MAPPING new file mode 100644 index 000000000000..20af0287da5a --- /dev/null +++ b/tests/Internal/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "InternalTests" + } + ] +}
\ No newline at end of file diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java index d9a4c261ee15..5a27593c7a36 100644 --- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java @@ -90,7 +90,7 @@ public class LegacyProtoLogImplTest { //noinspection ResultOfMethodCallIgnored mFile.delete(); mProtoLog = new LegacyProtoLogImpl(mFile, mViewerConfigFilename, - 1024 * 1024, mReader, 1024, new TreeMap<>()); + 1024 * 1024, mReader, 1024, new TreeMap<>(), () -> {}); } @After @@ -393,5 +393,10 @@ public class LegacyProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java index 548adeff07b2..1d7b6b348e10 100644 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java @@ -65,6 +65,7 @@ import java.io.IOException; import java.util.List; import java.util.Random; import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; import perfetto.protos.Protolog; import perfetto.protos.ProtologCommon; @@ -95,6 +96,7 @@ public class PerfettoProtoLogImplTest { private PerfettoProtoLogImpl mProtoLog; private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder; private File mFile; + private Runnable mCacheUpdater; private ProtoLogViewerConfigReader mReader; @@ -152,9 +154,11 @@ public class PerfettoProtoLogImplTest { Mockito.when(viewerConfigInputStreamProvider.getInputStream()) .thenAnswer(it -> new ProtoInputStream(mViewerConfigBuilder.build().toByteArray())); + mCacheUpdater = () -> {}; mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); - mProtoLog = - new PerfettoProtoLogImpl(viewerConfigInputStreamProvider, mReader, new TreeMap<>()); + mProtoLog = new PerfettoProtoLogImpl( + viewerConfigInputStreamProvider, mReader, new TreeMap<>(), + () -> mCacheUpdater.run()); } @After @@ -500,7 +504,8 @@ public class PerfettoProtoLogImplTest { PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder().enableProtoLog(true, List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true))) + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + true))) .build(); try { traceMonitor.start(); @@ -526,6 +531,142 @@ public class PerfettoProtoLogImplTest { Truth.assertThat(stacktrace).contains("stackTraceTrimmed"); } + @Test + public void cacheIsUpdatedWhenTracesStartAndStop() { + final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0); + mCacheUpdater = cacheUpdateCallCount::incrementAndGet; + + PerfettoTraceMonitor traceMonitor1 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, + false))) + .build(); + + PerfettoTraceMonitor traceMonitor2 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + false))) + .build(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0); + + try { + traceMonitor1.start(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(1); + + try { + traceMonitor2.start(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(2); + } finally { + traceMonitor2.stop(mWriter); + } + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(3); + + } finally { + traceMonitor1.stop(mWriter); + } + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(4); + } + + @Test + public void isEnabledUpdatesBasedOnRunningTraces() { + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isTrue(); + + PerfettoTraceMonitor traceMonitor1 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, + false))) + .build(); + + PerfettoTraceMonitor traceMonitor2 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + false))) + .build(); + + try { + traceMonitor1.start(); + + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + + try { + traceMonitor2.start(); + + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, + LogLevel.VERBOSE)).isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + } finally { + traceMonitor2.stop(mWriter); + } + + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + } finally { + traceMonitor1.stop(mWriter); + } + + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isFalse(); + Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + } + private enum TestProtoLogGroup implements IProtoLogGroup { TEST_GROUP(true, true, false, "TEST_TAG"); @@ -584,5 +725,10 @@ public class PerfettoProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java index 4267c2c127ae..60456f9ea10f 100644 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java @@ -174,5 +174,10 @@ public class ProtoLogImplTest { this.mLogToLogcat = logToLogcat; } + @Override + public int getId() { + return ordinal(); + } + } } diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java index a96389046d6a..be9fb1b309f6 100644 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java @@ -34,7 +34,7 @@ import perfetto.protos.DataSourceConfigOuterClass; import perfetto.protos.ProtologCommon; import perfetto.protos.ProtologConfig; -public class PerfettoDataSourceTest { +public class ProtologDataSourceTest { @Before public void before() { assumeTrue(android.tracing.Flags.perfettoProtologTracing()); @@ -67,7 +67,7 @@ public class PerfettoDataSourceTest { @Test public void allEnabledTraceMode() { - final ProtoLogDataSource ds = new ProtoLogDataSource(() -> {}, () -> {}, () -> {}); + final ProtoLogDataSource ds = new ProtoLogDataSource((c) -> {}, () -> {}, (c) -> {}); final ProtoLogDataSource.TlsState tlsState = createTlsState( DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( @@ -154,7 +154,7 @@ public class PerfettoDataSourceTest { private ProtoLogDataSource.TlsState createTlsState( DataSourceConfigOuterClass.DataSourceConfig config) { final ProtoLogDataSource ds = - Mockito.spy(new ProtoLogDataSource(() -> {}, () -> {}, () -> {})); + Mockito.spy(new ProtoLogDataSource((c) -> {}, () -> {}, (c) -> {})); ProtoInputStream configStream = new ProtoInputStream(config.toByteArray()); final ProtoLogDataSource.Instance dsInstance = Mockito.spy( diff --git a/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt index b5bd9ca746aa..8bc2f9716512 100644 --- a/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt +++ b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt @@ -21,8 +21,13 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.util.AttributeSet +import android.view.InputDevice +import android.view.InputDevice.SOURCE_MOUSE import android.view.InputDevice.SOURCE_STYLUS +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.InputDevice.SOURCE_TOUCHPAD import android.view.MotionEvent +import android.view.MotionEvent.ACTION_CANCEL import android.view.MotionEvent.ACTION_DOWN import android.view.MotionEvent.ACTION_HOVER_EXIT import android.view.MotionEvent.ACTION_UP @@ -56,8 +61,8 @@ private fun drawLine(canvas: Canvas, from: MotionEvent, to: MotionEvent, paint: } private fun drawCircle(canvas: Canvas, event: MotionEvent, paint: Paint, radius: Float) { - val x = event.getX() - val y = event.getY() + val x = event.x + val y = event.y canvas.drawCircle(x, y, radius, paint) } @@ -110,44 +115,72 @@ class DrawingView : View { private val scaleGestureDetector = ScaleGestureDetector(context, scaleGestureListener, null) private var touchPaint = Paint() + private var touchpadPaint = Paint() private var stylusPaint = Paint() + private var mousePaint = Paint() + private var drawingTabletPaint = Paint() private fun init() { touchPaint.color = Color.RED - touchPaint.setStrokeWidth(5f) + touchPaint.strokeWidth = 5f + touchpadPaint.color = Color.BLACK + touchpadPaint.strokeWidth = 5f stylusPaint.color = Color.YELLOW - stylusPaint.setStrokeWidth(5f) - + stylusPaint.strokeWidth = 5f + mousePaint.color = Color.BLUE + mousePaint.strokeWidth = 5f + drawingTabletPaint.color = Color.GREEN + drawingTabletPaint.strokeWidth = 5f setOnHoverListener { _, event -> processHoverEvent(event); true } } + private fun resolvePaint(event: MotionEvent): Paint? { + val inputDevice = InputDevice.getDevice(event.deviceId) + val isTouchpadDevice = inputDevice != null && inputDevice.supportsSource(SOURCE_TOUCHPAD) + return if (event.isFromSource(SOURCE_STYLUS or SOURCE_MOUSE)) { + // External stylus / drawing tablet + drawingTabletPaint + } else if (event.isFromSource(SOURCE_TOUCHSCREEN) && !event.isFromSource(SOURCE_STYLUS)) { + // Touchscreen event + touchPaint + } else if (event.isFromSource(SOURCE_MOUSE) && + (event.isFromSource(SOURCE_TOUCHPAD) || isTouchpadDevice)) { + // Touchpad event + touchpadPaint + } else if (event.isFromSource(SOURCE_MOUSE)) { + // Mouse event + mousePaint + } else if (event.isFromSource(SOURCE_STYLUS)) { + // Stylus event + stylusPaint + } else { + // Drop the event + null + } + } + private fun processTouchEvent(event: MotionEvent) { scaleGestureDetector.onTouchEvent(event) if (event.actionMasked == ACTION_DOWN) { touchEvents.remove(event.deviceId) myState?.state = PointerState.DOWN - } else if (event.actionMasked == ACTION_UP) { + } else if (event.actionMasked == ACTION_UP || event.actionMasked == ACTION_CANCEL) { myState?.state = PointerState.NONE } - var vec = touchEvents.getOrPut(event.deviceId) { Vector<Pair<MotionEvent, Paint>>() } - - val paint = if (event.isFromSource(SOURCE_STYLUS)) { - val size = myState?.lineSize ?: 5f - stylusPaint.setStrokeWidth(size) - Paint(stylusPaint) - } else { + val paint = resolvePaint(event) + if (paint != null) { + val vec = touchEvents.getOrPut(event.deviceId) { Vector<Pair<MotionEvent, Paint>>() } val size = myState?.lineSize ?: 5f - touchPaint.setStrokeWidth(size) - Paint(touchPaint) + paint.strokeWidth = size + vec.add(Pair(MotionEvent.obtain(event), Paint(paint))) + invalidate() } - vec.add(Pair(MotionEvent.obtain(event), paint)) - invalidate() } private fun processHoverEvent(event: MotionEvent) { hoverEvents.remove(event.deviceId) - if (event.getActionMasked() != ACTION_HOVER_EXIT) { - hoverEvents.put(event.deviceId, MotionEvent.obtain(event)) + if (event.actionMasked != ACTION_HOVER_EXIT) { + hoverEvents[event.deviceId] = MotionEvent.obtain(event) myState?.state = PointerState.HOVER } else { myState?.state = PointerState.NONE @@ -155,7 +188,7 @@ class DrawingView : View { invalidate() } - public override fun onTouchEvent(event: MotionEvent): Boolean { + override fun onTouchEvent(event: MotionEvent): Boolean { processTouchEvent(event) return true } @@ -171,12 +204,10 @@ class DrawingView : View { } // Draw hovers for ((_, event) in hoverEvents ) { - if (event.isFromSource(SOURCE_STYLUS)) { - val size = myState?.circleSize ?: 20f - drawCircle(canvas, event, stylusPaint, size) - } else { + val paint = resolvePaint(event) + if (paint != null) { val size = myState?.circleSize ?: 20f - drawCircle(canvas, event, touchPaint, size) + drawCircle(canvas, event, paint, size) } } } diff --git a/tests/OdmApps/app/AndroidManifest.xml b/tests/OdmApps/app/AndroidManifest.xml index 84a9ea84b522..84a9ea84b522 100755..100644 --- a/tests/OdmApps/app/AndroidManifest.xml +++ b/tests/OdmApps/app/AndroidManifest.xml diff --git a/tests/OdmApps/priv-app/AndroidManifest.xml b/tests/OdmApps/priv-app/AndroidManifest.xml index 031cf64ea7b1..031cf64ea7b1 100755..100644 --- a/tests/OdmApps/priv-app/AndroidManifest.xml +++ b/tests/OdmApps/priv-app/AndroidManifest.xml diff --git a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png Binary files differindex 66a198496cfb..66a198496cfb 100755..100644 --- a/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png +++ b/tests/RemoteDisplayProvider/res/drawable-hdpi/ic_app.png diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java index 0fb4f90f354f..56fb30ccca9c 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java @@ -135,7 +135,7 @@ public class EmbeddedWindowService extends Service { c.drawText("Remote", 250, 250, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(displayId, inputTransferToken, + wm.registerBatchedSurfaceControlInputReceiver(inputTransferToken, mSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-remote " + event); diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java index e700bc2f3d21..ac7dc9e2f31f 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -138,7 +138,7 @@ public class SurfaceInputTestActivity extends Activity { c.drawText("Local SC", 0, 0, paint); surface.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + wm.registerBatchedSurfaceControlInputReceiver( attachedSurfaceControl.getInputTransferToken(), mLocalSurfaceControl, Choreographer.getInstance(), event -> { Log.d(TAG, "onInputEvent-sc " + event); @@ -159,7 +159,7 @@ public class SurfaceInputTestActivity extends Activity { holder.unlockCanvasAndPost(c); WindowManager wm = getSystemService(WindowManager.class); - wm.registerBatchedSurfaceControlInputReceiver(getDisplayId(), + wm.registerBatchedSurfaceControlInputReceiver( mLocalSurfaceView.getRootSurfaceControl().getInputTransferToken(), mLocalSurfaceView.getSurfaceControl(), Choreographer.getInstance(), event -> { diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java index a62103e0030b..f88d82bf29a8 100644 --- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java @@ -16,10 +16,16 @@ package com.android.internal.telephony.tests; +import static android.telephony.NetworkRegistrationInfo.FIRST_SERVICE_TYPE; +import static android.telephony.NetworkRegistrationInfo.LAST_SERVICE_TYPE; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Context; @@ -72,6 +78,37 @@ public class TelephonyUtilsTest { // getSubscriptionUserHandle should be called if subID is active. verify(mSubscriptionManager, times(1)).getSubscriptionUserHandle(eq(activeSubId)); } + + @Test + public void testIsValidPlmn() { + assertTrue(TelephonyUtils.isValidPlmn("310260")); + assertTrue(TelephonyUtils.isValidPlmn("45006")); + assertFalse(TelephonyUtils.isValidPlmn("1234567")); + assertFalse(TelephonyUtils.isValidPlmn("1234")); + assertFalse(TelephonyUtils.isValidPlmn("")); + assertFalse(TelephonyUtils.isValidPlmn(null)); + } + + @Test + public void testIsValidService() { + assertTrue(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE)); + assertTrue(TelephonyUtils.isValidService(LAST_SERVICE_TYPE)); + assertFalse(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE - 1)); + assertFalse(TelephonyUtils.isValidService(LAST_SERVICE_TYPE + 1)); + } + + @Test + public void testIsValidCountryCode() { + assertTrue(TelephonyUtils.isValidCountryCode("US")); + assertTrue(TelephonyUtils.isValidCountryCode("cn")); + assertFalse(TelephonyUtils.isValidCountryCode("11")); + assertFalse(TelephonyUtils.isValidCountryCode("USA")); + assertFalse(TelephonyUtils.isValidCountryCode("chn")); + assertFalse(TelephonyUtils.isValidCountryCode("U")); + assertFalse(TelephonyUtils.isValidCountryCode("G7")); + assertFalse(TelephonyUtils.isValidCountryCode("")); + assertFalse(TelephonyUtils.isValidCountryCode(null)); + } } diff --git a/tests/TouchLatency/Android.bp b/tests/TouchLatency/Android.bp index 4ef1ead7d9c9..7990732d924d 100644 --- a/tests/TouchLatency/Android.bp +++ b/tests/TouchLatency/Android.bp @@ -5,6 +5,7 @@ package { // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 default_applicable_licenses: ["frameworks_base_license"], + default_team: "trendy_team_android_core_graphics_stack", } android_test { diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml index b23a87e57754..5058331187e8 100644 --- a/tests/TouchLatency/app/src/main/res/values/styles.xml +++ b/tests/TouchLatency/app/src/main/res/values/styles.xml @@ -18,6 +18,7 @@ <!-- Base application theme. --> <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar"> <!-- Customize your theme here. --> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> </resources> diff --git a/tests/UsbManagerTests/Android.bp b/tests/UsbManagerTests/Android.bp index f0bea3f3c28a..2909e66b53be 100644 --- a/tests/UsbManagerTests/Android.bp +++ b/tests/UsbManagerTests/Android.bp @@ -43,6 +43,9 @@ android_test { "libmultiplejvmtiagentsinterferenceagent", "libstaticjvmtiagent", ], + libs: [ + "android.test.mock", + ], certificate: "platform", platform_apis: true, test_suites: ["device-tests"], diff --git a/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java new file mode 100644 index 000000000000..d6f3148e64f1 --- /dev/null +++ b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 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.hardware.usb; + +import static com.google.common.truth.Truth.assertThat; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.usb.flags.Flags; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.util.XmlUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; +import org.xmlpull.v1.XmlSerializer; + +import java.io.StringReader; + +/** + * Unit tests for {@link android.hardware.usb.DeviceFilter}. + */ +@RunWith(AndroidJUnit4.class) +public class DeviceFilterTest { + + private static final int VID = 10; + private static final int PID = 11; + private static final int CLASS = 12; + private static final int SUBCLASS = 13; + private static final int PROTOCOL = 14; + private static final String MANUFACTURER = "Google"; + private static final String PRODUCT = "Test"; + private static final String SERIAL_NO = "4AL23"; + private static final String INTERFACE_NAME = "MTP"; + + private MockitoSession mStaticMockSession; + + @Before + public void setUp() throws Exception { + mStaticMockSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class) + .strictness(Strictness.WARN) + .startMocking(); + + when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(true); + } + + @After + public void tearDown() throws Exception { + mStaticMockSession.finishMocking(); + } + + @Test + public void testConstructorFromValues_interfaceNameIsInitialized() { + DeviceFilter deviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME); + } + + @Test + public void testConstructorFromUsbDevice_interfaceNameIsNull() { + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getVendorId()).thenReturn(VID); + when(usbDevice.getProductId()).thenReturn(PID); + when(usbDevice.getDeviceClass()).thenReturn(CLASS); + when(usbDevice.getDeviceSubclass()).thenReturn(SUBCLASS); + when(usbDevice.getDeviceProtocol()).thenReturn(PROTOCOL); + when(usbDevice.getManufacturerName()).thenReturn(MANUFACTURER); + when(usbDevice.getProductName()).thenReturn(PRODUCT); + when(usbDevice.getSerialNumber()).thenReturn(SERIAL_NO); + + DeviceFilter deviceFilter = new DeviceFilter(usbDevice); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(null); + } + + @Test + public void testConstructorFromDeviceFilter_interfaceNameIsInitialized() { + DeviceFilter originalDeviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + DeviceFilter deviceFilter = new DeviceFilter(originalDeviceFilter); + + verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter); + assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME); + } + + + @Test + public void testReadFromXml_interfaceNamePresent_propertyIsInitialized() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>"); + + assertThat(deviceFilter.mInterfaceName).isEqualTo("MTP"); + } + + @Test + public void testReadFromXml_interfaceNameAbsent_propertyIsNull() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />"); + + assertThat(deviceFilter.mInterfaceName).isEqualTo(null); + } + + @Test + public void testWrite_withInterfaceName() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>"); + XmlSerializer serializer = Mockito.mock(XmlSerializer.class); + + deviceFilter.write(serializer); + + verify(serializer).attribute(null, "interface-name", "MTP"); + } + + @Test + public void testWrite_withoutInterfaceName() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />"); + XmlSerializer serializer = Mockito.mock(XmlSerializer.class); + + deviceFilter.write(serializer); + + verify(serializer, times(0)).attribute(eq(null), eq("interface-name"), any()); + } + + @Test + public void testToString() { + DeviceFilter deviceFilter = new DeviceFilter( + VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER, + PRODUCT, SERIAL_NO, INTERFACE_NAME + ); + + assertThat(deviceFilter.toString()).isEqualTo( + "DeviceFilter[mVendorId=10,mProductId=11,mClass=12,mSubclass=13,mProtocol=14," + + "mManufacturerName=Google,mProductName=Test,mSerialNumber=4AL23," + + "mInterfaceName=MTP]"); + } + + @Test + public void testMatch_interfaceNameMatches_returnTrue() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "MTP", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertTrue(deviceFilter.matches(usbDevice)); + } + + @Test + public void testMatch_interfaceNameMismatch_returnFalse() throws Exception { + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "UVC", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertFalse(deviceFilter.matches(usbDevice)); + } + + @Test + public void testMatch_interfaceNameMismatchFlagDisabled_returnTrue() throws Exception { + when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(false); + DeviceFilter deviceFilter = getDeviceFilterFromXml( + "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" " + + "interface-name=\"MTP\"/>"); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + when(usbDevice.getInterfaceCount()).thenReturn(1); + when(usbDevice.getInterface(0)).thenReturn(new UsbInterface( + /* id= */ 0, + /* alternateSetting= */ 0, + /* name= */ "UVC", + /* class= */ 255, + /* subClass= */ 255, + /* protocol= */ 0)); + + assertTrue(deviceFilter.matches(usbDevice)); + } + + private void verifyDeviceFilterConfigurationExceptInterfaceName(DeviceFilter deviceFilter) { + assertThat(deviceFilter.mVendorId).isEqualTo(VID); + assertThat(deviceFilter.mProductId).isEqualTo(PID); + assertThat(deviceFilter.mClass).isEqualTo(CLASS); + assertThat(deviceFilter.mSubclass).isEqualTo(SUBCLASS); + assertThat(deviceFilter.mProtocol).isEqualTo(PROTOCOL); + assertThat(deviceFilter.mManufacturerName).isEqualTo(MANUFACTURER); + assertThat(deviceFilter.mProductName).isEqualTo(PRODUCT); + assertThat(deviceFilter.mSerialNumber).isEqualTo(SERIAL_NO); + } + + private DeviceFilter getDeviceFilterFromXml(String xml) throws Exception { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + XmlPullParser parser = factory.newPullParser(); + parser.setInput(new StringReader(xml)); + XmlUtils.nextElement(parser); + + return DeviceFilter.read(parser); + } + +} diff --git a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java index 4780d8a610e8..87b26a63acc7 100644 --- a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java +++ b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbProfileGroupSettingsManagerTest.java @@ -16,6 +16,8 @@ package com.android.server.usbtest; +import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; + import static com.android.server.usb.UsbProfileGroupSettingsManager.PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES; import static org.mockito.ArgumentMatchers.any; @@ -32,16 +34,20 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.Property; import android.content.pm.UserInfo; import android.content.res.Resources; import android.hardware.usb.UsbDevice; import android.os.UserHandle; import android.os.UserManager; +import android.provider.Settings; +import android.test.mock.MockContentResolver; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; +import com.android.internal.util.test.FakeSettingsProvider; import com.android.server.usb.UsbHandlerManager; import com.android.server.usb.UsbProfileGroupSettingsManager; import com.android.server.usb.UsbSettingsManager; @@ -69,6 +75,7 @@ import java.util.List; public class UsbProfileGroupSettingsManagerTest { private static final String TEST_PACKAGE_NAME = "testPkg"; + @Mock private Context mContext; @Mock @@ -85,43 +92,78 @@ public class UsbProfileGroupSettingsManagerTest { private UserManager mUserManager; @Mock private UsbUserSettingsManager mUsbUserSettingsManager; - @Mock private Property mProperty; - private ActivityManager.RunningAppProcessInfo mRunningAppProcessInfo; - private PackageInfo mPackageInfo; - private UsbProfileGroupSettingsManager mUsbProfileGroupSettingsManager; + @Mock + private Property mRestrictUsbOverlayActivitiesProperty; + @Mock + private UsbDevice mUsbDevice; + + private MockContentResolver mContentResolver; private MockitoSession mStaticMockSession; + private UsbProfileGroupSettingsManager mUsbProfileGroupSettingsManager; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mStaticMockSession = ExtendedMockito.mockitoSession() + .mockStatic(Flags.class) + .strictness(Strictness.WARN) + .startMocking(); - mRunningAppProcessInfo = new ActivityManager.RunningAppProcessInfo(); - mRunningAppProcessInfo.pkgList = new String[]{TEST_PACKAGE_NAME}; - mPackageInfo = new PackageInfo(); - mPackageInfo.packageName = TEST_PACKAGE_NAME; - mPackageInfo.applicationInfo = Mockito.mock(ApplicationInfo.class); + when(mUsbSettingsManager.getSettingsForUser(anyInt())).thenReturn(mUsbUserSettingsManager); + when(mUserManager.getEnabledProfiles(anyInt())) + .thenReturn(List.of(Mockito.mock(UserInfo.class))); + + mContentResolver = new MockContentResolver(); + mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); when(mContext.getResources()).thenReturn(Mockito.mock(Resources.class)); + when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); + when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); when(mContext.createPackageContextAsUser(anyString(), anyInt(), any(UserHandle.class))) .thenReturn(mContext); - when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - mUsbProfileGroupSettingsManager = new UsbProfileGroupSettingsManager(mContext, mUserHandle, - mUsbSettingsManager, mUsbHandlerManager); + mUsbProfileGroupSettingsManager = new UsbProfileGroupSettingsManager( + mContext, mUserHandle, mUsbSettingsManager, mUsbHandlerManager); - mStaticMockSession = ExtendedMockito.mockitoSession() - .mockStatic(Flags.class) - .strictness(Strictness.WARN) - .startMocking(); + setupDefaultConfiguration(); + } + /** + * Setups the following configuration + * + * <ul> + * <li>Flag is enabled + * <li>Device setup has completed + * <li>There is a foreground activity with MANAGE_USB permission + * <li>The foreground activity has PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES enabled + * </ul> + */ + private void setupDefaultConfiguration() throws NameNotFoundException { + when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true); + + Settings.Secure.putInt(mContentResolver, USER_SETUP_COMPLETE, 1); + + ActivityManager.RunningAppProcessInfo mRunningAppProcessInfo = + new ActivityManager.RunningAppProcessInfo(); + mRunningAppProcessInfo.pkgList = new String[] { TEST_PACKAGE_NAME }; + when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo)); + + PackageInfo mPackageInfo = new PackageInfo(); + mPackageInfo.packageName = TEST_PACKAGE_NAME; + mPackageInfo.applicationInfo = Mockito.mock(ApplicationInfo.class); when(mPackageManager.getPackageInfo(TEST_PACKAGE_NAME, 0)).thenReturn(mPackageInfo); - when(mPackageManager.getProperty(eq(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES), - eq(TEST_PACKAGE_NAME))).thenReturn(mProperty); - when(mUserManager.getEnabledProfiles(anyInt())) - .thenReturn(List.of(Mockito.mock(UserInfo.class))); - when(mUsbSettingsManager.getSettingsForUser(anyInt())).thenReturn(mUsbUserSettingsManager); + when(mPackageManager.getPackagesHoldingPermissions( + new String[] { android.Manifest.permission.MANAGE_USB }, + PackageManager.MATCH_SYSTEM_ONLY)) + .thenReturn(List.of(mPackageInfo)); + + when(mRestrictUsbOverlayActivitiesProperty.getBoolean()).thenReturn(true); + when(mPackageManager.getProperty( + eq(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES), eq(TEST_PACKAGE_NAME))) + .thenReturn(mRestrictUsbOverlayActivitiesProperty); } @After @@ -130,66 +172,59 @@ public class UsbProfileGroupSettingsManagerTest { } @Test - public void testDeviceAttached_flagTrueWithoutForegroundActivity_resolveActivityCalled() { - when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true); + public void testDeviceAttached_foregroundActivityWithManifestField_resolveActivityNotCalled() { + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); + + verify(mUsbUserSettingsManager, times(0)).queryIntentActivities(any(Intent.class)); + } + + @Test + public void testDeviceAttached_noForegroundActivity_resolveActivityCalled() { when(mActivityManager.getRunningAppProcesses()).thenReturn(new ArrayList<>()); - when(mPackageManager.getPackagesHoldingPermissions( - new String[]{android.Manifest.permission.MANAGE_USB}, - PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo)); - UsbDevice device = Mockito.mock(UsbDevice.class); - mUsbProfileGroupSettingsManager.deviceAttached(device); + + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); + verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class)); } @Test public void testDeviceAttached_noForegroundActivityWithUsbPermission_resolveActivityCalled() { - when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true); - when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo)); when(mPackageManager.getPackagesHoldingPermissions( - new String[]{android.Manifest.permission.MANAGE_USB}, - PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(new ArrayList<>()); - UsbDevice device = Mockito.mock(UsbDevice.class); - mUsbProfileGroupSettingsManager.deviceAttached(device); + new String[] { android.Manifest.permission.MANAGE_USB }, + PackageManager.MATCH_SYSTEM_ONLY)) + .thenReturn(new ArrayList<>()); + + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); + verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class)); } @Test - public void testDeviceAttached_foregroundActivityWithManifestField_resolveActivityNotCalled() { - when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true); - when(mProperty.getBoolean()).thenReturn(true); - when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo)); - when(mPackageManager.getPackagesHoldingPermissions( - new String[]{android.Manifest.permission.MANAGE_USB}, - PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo)); - UsbDevice device = Mockito.mock(UsbDevice.class); - mUsbProfileGroupSettingsManager.deviceAttached(device); - verify(mUsbUserSettingsManager, times(0)) - .queryIntentActivities(any(Intent.class)); - } + public void testDeviceAttached_restricUsbOverlayPropertyDisabled_resolveActivityCalled() { + when(mRestrictUsbOverlayActivitiesProperty.getBoolean()).thenReturn(false); + + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); - @Test - public void testDeviceAttached_foregroundActivityWithoutManifestField_resolveActivityCalled() { - when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(true); - when(mProperty.getBoolean()).thenReturn(false); - when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo)); - when(mPackageManager.getPackagesHoldingPermissions( - new String[]{android.Manifest.permission.MANAGE_USB}, - PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo)); - UsbDevice device = Mockito.mock(UsbDevice.class); - mUsbProfileGroupSettingsManager.deviceAttached(device); verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class)); } @Test - public void testDeviceAttached_flagFalseForegroundActivity_resolveActivityCalled() { + public void testDeviceAttached_flagFalse_resolveActivityCalled() { when(Flags.allowRestrictionOfOverlayActivities()).thenReturn(false); - when(mProperty.getBoolean()).thenReturn(true); - when(mActivityManager.getRunningAppProcesses()).thenReturn(List.of(mRunningAppProcessInfo)); - when(mPackageManager.getPackagesHoldingPermissions( - new String[]{android.Manifest.permission.MANAGE_USB}, - PackageManager.MATCH_SYSTEM_ONLY)).thenReturn(List.of(mPackageInfo)); - UsbDevice device = Mockito.mock(UsbDevice.class); - mUsbProfileGroupSettingsManager.deviceAttached(device); + + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); + verify(mUsbUserSettingsManager).queryIntentActivities(any(Intent.class)); } + + @Test + public void + testDeviceAttached_setupNotCompleteAndNoBlockingActivities_resolveActivityNotCalled() { + when(mRestrictUsbOverlayActivitiesProperty.getBoolean()).thenReturn(false); + Settings.Secure.putInt(mContentResolver, USER_SETUP_COMPLETE, 0); + + mUsbProfileGroupSettingsManager.deviceAttached(mUsbDevice); + + verify(mUsbUserSettingsManager, times(0)).queryIntentActivities(any(Intent.class)); + } } diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp index c4ebdecae735..c012cce494e2 100644 --- a/tests/UsbTests/Android.bp +++ b/tests/UsbTests/Android.bp @@ -37,6 +37,8 @@ android_test { "services.usb", "truth", "UsbManagerTestLib", + "android.hardware.usb.flags-aconfig-java", + "flag-junit", ], jni_libs: [ // Required for ExtendedMockito diff --git a/tests/UsbTests/TEST_MAPPING b/tests/UsbTests/TEST_MAPPING new file mode 100644 index 000000000000..70134b818722 --- /dev/null +++ b/tests/UsbTests/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "UsbTests" + } + ] +}
\ No newline at end of file diff --git a/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java new file mode 100644 index 000000000000..56845aeb6a2c --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbServiceTest.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 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 com.android.server.usb; + +import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.usb.IUsbOperationInternal; +import android.hardware.usb.flags.Flags; +import android.os.RemoteException; +import android.os.UserManager; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link com.android.server.usb.UsbService} + */ +@RunWith(AndroidJUnit4.class) +public class UsbServiceTest { + + @Mock + private Context mContext; + @Mock + private UsbPortManager mUsbPortManager; + @Mock + private UsbAlsaManager mUsbAlsaManager; + @Mock + private UserManager mUserManager; + @Mock + private UsbSettingsManager mUsbSettingsManager; + @Mock + private IUsbOperationInternal mCallback; + + private static final String TEST_PORT_ID = "123"; + + private static final int TEST_TRANSACTION_ID = 1; + + private static final int TEST_FIRST_CALLER_ID = 1000; + + private static final int TEST_SECOND_CALLER_ID = 2000; + + private UsbService mUsbService; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + + @Before + public void setUp() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_USB_DATA_SIGNAL_STAKING); + MockitoAnnotations.initMocks(this); + + when(mUsbPortManager.enableUsbData(eq(TEST_PORT_ID), anyBoolean(), eq(TEST_TRANSACTION_ID), + eq(mCallback), any())).thenReturn(true); + + mUsbService = new UsbService(mContext, mUsbPortManager, mUsbAlsaManager, + mUserManager, mUsbSettingsManager); + } + + private void assertToggleUsbSuccessfully(int uid, boolean enable) { + assertTrue(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable, + TEST_TRANSACTION_ID, mCallback, uid)); + + verify(mUsbPortManager).enableUsbData(TEST_PORT_ID, + enable, TEST_TRANSACTION_ID, mCallback, null); + verifyZeroInteractions(mCallback); + + clearInvocations(mUsbPortManager); + clearInvocations(mCallback); + } + + private void assertToggleUsbFailed(int uid, boolean enable) throws Exception { + assertFalse(mUsbService.enableUsbDataInternal(TEST_PORT_ID, enable, + TEST_TRANSACTION_ID, mCallback, uid)); + + verifyZeroInteractions(mUsbPortManager); + verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + + clearInvocations(mUsbPortManager); + clearInvocations(mCallback); + } + + /** + * Verify enableUsbData successfully disables USB port without error + */ + @Test + public void disableUsb_successfullyDisable() { + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, false); + } + + /** + * Verify enableUsbData successfully enables USB port without error given no other stakers + */ + @Test + public void enableUsbWhenNoOtherStakers_successfullyEnable() { + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, true); + } + + /** + * Verify enableUsbData does not enable USB port if other stakers are present + */ + @Test + public void enableUsbPortWithOtherStakers_failsToEnable() throws Exception { + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, false); + + assertToggleUsbFailed(TEST_SECOND_CALLER_ID, true); + } + + /** + * Verify enableUsbData successfully enables USB port when the last staker is removed + */ + @Test + public void enableUsbByTheOnlyStaker_successfullyEnable() { + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, false); + + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, true); + } + + /** + * Verify enableUsbDataWhileDockedInternal does not enable USB port if other stakers are present + */ + @Test + public void enableUsbWhileDockedWhenThereAreOtherStakers_failsToEnable() + throws RemoteException { + assertToggleUsbSuccessfully(TEST_FIRST_CALLER_ID, false); + + mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID, + mCallback, TEST_SECOND_CALLER_ID); + + verifyZeroInteractions(mUsbPortManager); + verify(mCallback).onOperationComplete(USB_OPERATION_ERROR_INTERNAL); + } + + /** + * Verify enableUsbDataWhileDockedInternal does enable USB port if other stakers are + * not present + */ + @Test + public void enableUsbWhileDockedWhenThereAreNoStakers_SuccessfullyEnable() { + mUsbService.enableUsbDataWhileDockedInternal(TEST_PORT_ID, TEST_TRANSACTION_ID, + mCallback, TEST_SECOND_CALLER_ID); + + verify(mUsbPortManager).enableUsbDataWhileDocked(TEST_PORT_ID, TEST_TRANSACTION_ID, + mCallback, null); + verifyZeroInteractions(mCallback); + } +} diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml index 61dd9d4cd021..dbe9d363e285 100644 --- a/tests/WindowInsetsTests/AndroidManifest.xml +++ b/tests/WindowInsetsTests/AndroidManifest.xml @@ -18,7 +18,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.test.windowinsetstests"> - <application android:label="@string/application_title"> + <application android:label="@string/application_title" + android:theme="@style/base"> <activity android:name=".WindowInsetsTestsMainActivity" android:exported="true"> <intent-filter> @@ -29,11 +30,9 @@ <activity android:name=".ChatActivity" android:label="@string/chat_activity_title" - android:theme="@style/chat" android:windowSoftInputMode="adjustResize" /> <activity android:name=".ControllerActivity" - android:label="@string/controller_activity_title" - android:theme="@style/controller" /> + android:label="@string/controller_activity_title" /> </application> </manifest> diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml index 5550eab61a33..7013059e1334 100644 --- a/tests/WindowInsetsTests/res/layout/controller_activity.xml +++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml @@ -15,92 +15,110 @@ --> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent"> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + /> - <LinearLayout - android:id="@+id/content" + <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical"> - - <Spinner - android:id="@+id/spinnerBehavior" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="20dp" /> - - <ToggleButton - android:id="@+id/toggleButtonStatus" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:checked="true" - android:text="Status Bars Toggle Button" - android:textOff="Status Bars Invisible" - android:textOn="Status Bars Visible" /> - - <SeekBar - android:id="@+id/seekBarStatus" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="20dp" - android:max="10000" - android:progress="10000" /> - - <ToggleButton - android:id="@+id/toggleButtonNavigation" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:checked="true" - android:text="Navigation Bars Toggle Button" - android:textOff="Navigation Bars Invisible" - android:textOn="Navigation Bars Visible" /> + android:paddingStart="8dp" + android:paddingEnd="8dp"> - <SeekBar - android:id="@+id/seekBarNavigation" + <LinearLayout + android:id="@+id/content" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="20dp" - android:max="10000" - android:progress="10000" /> - - <ToggleButton - android:id="@+id/toggleButtonIme" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:checked="true" - android:text="IME Toggle Button" - android:textOff="IME Invisible" - android:textOn="IME Visible" /> - - <SeekBar - android:id="@+id/seekBarIme" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:layout_marginBottom="20dp" - android:max="10000" - android:progress="0" /> - - <TextView - android:id="@+id/textViewControllableInsets" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="5dp" /> - - <EditText - android:id="@+id/editText" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ems="10" - android:hint="For testing IME..." - android:inputType="text" - android:text="" /> - - </LinearLayout> - -</ScrollView>
\ No newline at end of file + android:orientation="vertical"> + + <Spinner + android:id="@+id/spinnerBehavior" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="20dp" /> + + <ToggleButton + android:id="@+id/toggleButtonStatus" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:checked="true" + android:text="@string/status_bars_toggle_button" + android:textOff="@string/status_bars_invisible" + android:textOn="@string/status_bars_visible" /> + + <SeekBar + android:id="@+id/seekBarStatus" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="20dp" + android:max="10000" + android:progress="10000" /> + + <ToggleButton + android:id="@+id/toggleButtonNavigation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:checked="true" + android:text="@string/navigation_bars_toggle_button" + android:textOff="@string/navigation_bars_invisible" + android:textOn="@string/navigation_bars_visible" /> + + <SeekBar + android:id="@+id/seekBarNavigation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="20dp" + android:max="10000" + android:progress="10000" /> + + <ToggleButton + android:id="@+id/toggleButtonIme" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:checked="true" + android:text="@string/ime_toggle_button" + android:textOff="@string/ime_invisible" + android:textOn="@string/ime_visible" /> + + <SeekBar + android:id="@+id/seekBarIme" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="20dp" + android:max="10000" + android:progress="0" /> + + <TextView + android:id="@+id/textViewControllableInsets" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="5dp" /> + + <EditText + android:id="@+id/editText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ems="10" + android:autofillHints="@string/for_testing_ime" + android:hint="@string/for_testing_ime" + android:inputType="text" + android:text="" /> + + </LinearLayout> + + </ScrollView> + +</LinearLayout> diff --git a/tests/WindowInsetsTests/res/layout/main_activity.xml b/tests/WindowInsetsTests/res/layout/main_activity.xml index 621ed89204d1..d6d4ff9ca0a9 100644 --- a/tests/WindowInsetsTests/res/layout/main_activity.xml +++ b/tests/WindowInsetsTests/res/layout/main_activity.xml @@ -16,22 +16,38 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - <Button - android:id="@+id/chat_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/chat_activity_title" - android:textAllCaps="false"/> + <androidx.appcompat.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="?attr/actionBarSize" + /> - <Button - android:id="@+id/controller_button" - android:layout_width="wrap_content" + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/controller_activity_title" - android:textAllCaps="false"/> + android:paddingStart="8dp" + android:paddingEnd="8dp"> + + <Button + android:id="@+id/chat_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/chat_activity_title" + android:textAllCaps="false"/> + + <Button + android:id="@+id/controller_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/controller_activity_title" + android:textAllCaps="false"/> + + </LinearLayout> </LinearLayout> diff --git a/tests/WindowInsetsTests/res/values-night/styles.xml b/tests/WindowInsetsTests/res/values-night/styles.xml new file mode 100644 index 000000000000..323c5fd9698e --- /dev/null +++ b/tests/WindowInsetsTests/res/values-night/styles.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 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. + --> + +<resources> + + <style name="base" parent="@style/Theme.MaterialComponents"> + <item name="windowActionBar">false</item> + <item name="windowNoTitle">true</item> + + <item name="colorPrimary">@color/primaryColor</item> + <item name="colorPrimaryDark">@color/primaryDarkColor</item> + <item name="colorSecondary">?attr/colorPrimary</item> + <item name="colorOnSecondary">@color/primaryTextColor</item> + + <!-- Window decor --> + <item name="android:windowLightStatusBar">false</item> + <item name="android:windowLightNavigationBar">false</item> + + </style> + + <color name="primaryColor">#639ff9</color> + <color name="primaryLightColor">#6f6bff</color> + <color name="primaryDarkColor">#0016bb</color> + <color name="primaryTextColor">#ffffff</color> + + <color name="bubble">#333333</color> + <color name="bubble_self">#185abc</color> + +</resources> diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml index 516d4584426e..7b70852e6082 100644 --- a/tests/WindowInsetsTests/res/values/strings.xml +++ b/tests/WindowInsetsTests/res/values/strings.xml @@ -19,6 +19,16 @@ <string name="application_title">Window Insets Tests</string> <string name="chat_activity_title">New Insets Chat</string> <string name="controller_activity_title">Window Insets Controller</string> + <string name="status_bars_toggle_button">Status Bars Toggle Button</string> + <string name="status_bars_invisible">Status Bars Invisible</string> + <string name="status_bars_visible">Status Bars Visible</string> + <string name="navigation_bars_toggle_button">Navigation Bars Toggle Button</string> + <string name="navigation_bars_invisible">Navigation Bars Invisible</string> + <string name="navigation_bars_visible">Navigation Bars Visible</string> + <string name="ime_toggle_button">IME Bars Toggle Button</string> + <string name="ime_invisible">IME Bars Invisible</string> + <string name="ime_visible">IME Bars Visible</string> + <string name="for_testing_ime">For testing IME…</string> <!-- The item positions should match the flag values respectively. --> <string-array name="behaviors"> diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml index a84ffbed600d..4ce6323d8189 100644 --- a/tests/WindowInsetsTests/res/values/styles.xml +++ b/tests/WindowInsetsTests/res/values/styles.xml @@ -17,7 +17,7 @@ <resources> - <style name="chat" parent="@style/Theme.MaterialComponents.Light"> + <style name="base" parent="@style/Theme.MaterialComponents.Light"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> @@ -27,10 +27,8 @@ <item name="colorOnSecondary">@color/primaryTextColor</item> <!-- Window decor --> - <item name="android:statusBarColor">#ffffff</item> <item name="android:windowLightStatusBar">true</item> <item name="android:windowLightNavigationBar">true</item> - <item name="android:navigationBarColor">#ffffff</item> </style> @@ -63,11 +61,4 @@ <dimen name="bubble_padding">8dp</dimen> <dimen name="bubble_padding_side">16dp</dimen> - <style name="controller" parent="android:Theme.Material"> - <item name="android:colorPrimaryDark">#111111</item> - <item name="android:navigationBarColor">#111111</item> - <item name="android:colorPrimary">#222222</item> - <item name="android:colorAccent">#33ccff</item> - </style> - -</resources>
\ No newline at end of file +</resources> diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java index 167d560633ab..1dd87dfd3977 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java @@ -16,12 +16,18 @@ package com.google.android.test.windowinsetstests; -import android.app.Activity; +import static android.view.WindowInsets.Type.displayCutout; +import static android.view.WindowInsets.Type.ime; +import static android.view.WindowInsets.Type.navigationBars; +import static android.view.WindowInsets.Type.statusBars; +import static android.view.WindowInsets.Type.systemBars; +import static android.view.WindowInsets.Type.systemGestures; + import android.graphics.Insets; import android.os.Bundle; import android.view.View; import android.view.WindowInsets; -import android.view.WindowInsets.Type; +import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimationControlListener; import android.view.WindowInsetsAnimationController; import android.widget.AdapterView; @@ -32,7 +38,9 @@ import android.widget.Spinner; import android.widget.TextView; import android.widget.ToggleButton; -public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener { +import androidx.appcompat.app.AppCompatActivity; + +public class ControllerActivity extends AppCompatActivity { private ToggleButton mToggleStatus; private SeekBar mSeekStatus; @@ -48,6 +56,29 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.controller_activity); + setSupportActionBar(findViewById(R.id.toolbar)); + getWindow().setDecorFitsSystemWindows(false); + findViewById(R.id.root).setOnApplyWindowInsetsListener( + (v, insets) -> { + final int visibleTypes = systemBars() | displayCutout(); + final Insets i = insets.getInsets(visibleTypes); + v.setPadding(i.left, i.top, i.right, i.bottom); + + // Make the content view not obscured by gesture insets to prevent triggering + // system gestures while controlling seek bars. + final Insets gi = Insets.subtract( + insets.getInsets(systemGestures() | visibleTypes), i); + findViewById(R.id.content).setPadding(gi.left, gi.top, gi.right, gi.bottom); + + mNotFromUser[0] = true; + updateWidgets(insets, statusBars(), mToggleStatus, mSeekStatus); + updateWidgets(insets, navigationBars(), mToggleNavigation, mSeekNavigation); + updateWidgets(insets, ime(), mToggleIme, mSeekIme); + mLastInsets = insets; + mNotFromUser[0] = false; + + return WindowInsets.CONSUMED; + }); final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior); ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this, R.array.behaviors, android.R.layout.simple_spinner_item); @@ -66,23 +97,21 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn }); mToggleStatus = findViewById(R.id.toggleButtonStatus); mToggleStatus.setTag(mNotFromUser); - mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars())); + mToggleStatus.setOnCheckedChangeListener(new ToggleListener(statusBars())); mSeekStatus = findViewById(R.id.seekBarStatus); - mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars())); + mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(statusBars())); mToggleNavigation = findViewById(R.id.toggleButtonNavigation); mToggleNavigation.setTag(mNotFromUser); - mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars())); + mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(navigationBars())); mSeekNavigation = findViewById(R.id.seekBarNavigation); - mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars())); + mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(navigationBars())); mToggleIme = findViewById(R.id.toggleButtonIme); mToggleIme.setTag(mNotFromUser); - mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime())); + mToggleIme.setOnCheckedChangeListener(new ToggleListener(ime())); mSeekIme = findViewById(R.id.seekBarIme); - mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime())); + mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(ime())); mTextControllableInsets = findViewById(R.id.textViewControllableInsets); - final View contentView = findViewById(R.id.content); - contentView.setOnApplyWindowInsetsListener(this); - contentView.getWindowInsetsController().addOnControllableInsetsChangedListener( + mTextControllableInsets.getWindowInsetsController().addOnControllableInsetsChangedListener( (c, types) -> mTextControllableInsets.setText( "ControllableInsetsTypes:\n" + insetsTypesToString(types))); } @@ -91,22 +120,6 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn return types == 0 ? "none" : WindowInsets.Type.toString(types); } - @Override - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - mNotFromUser[0] = true; - updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus); - updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation); - updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme); - mLastInsets = insets; - mNotFromUser[0] = false; - - // Prevent triggering system gestures while controlling seek bars. - final Insets gestureInsets = insets.getInsets(Type.systemGestures()); - v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0); - - return v.onApplyWindowInsets(insets); - } - private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) { final boolean isVisible = insets.isVisible(types); final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible; @@ -121,7 +134,7 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn private static class ToggleListener implements CompoundButton.OnCheckedChangeListener { - private final @Type.InsetsType int mTypes; + private final @InsetsType int mTypes; ToggleListener(int types) { mTypes = types; @@ -143,7 +156,7 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener { - private final @Type.InsetsType int mTypes; + private final @InsetsType int mTypes; private WindowInsetsAnimationController mController; diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java index 8b77a78ff51e..278ad845d2bb 100644 --- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java +++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java @@ -16,16 +16,30 @@ package com.google.android.test.windowinsetstests; -import android.app.Activity; +import static android.view.WindowInsets.Type.displayCutout; +import static android.view.WindowInsets.Type.systemBars; + import android.content.Intent; +import android.graphics.Insets; import android.os.Bundle; +import android.view.WindowInsets; + +import androidx.appcompat.app.AppCompatActivity; -public class WindowInsetsTestsMainActivity extends Activity { +public class WindowInsetsTestsMainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); + setSupportActionBar(findViewById(R.id.toolbar)); + + findViewById(R.id.root).setOnApplyWindowInsetsListener( + (v, insets) -> { + final Insets i = insets.getInsets(systemBars() | displayCutout()); + v.setPadding(i.left, i.top, i.right, i.bottom); + return WindowInsets.CONSUMED; + }); findViewById(R.id.chat_button).setOnClickListener( v -> startActivity(new Intent(this, ChatActivity.class))); diff --git a/tests/graphics/SilkFX/AndroidManifest.xml b/tests/graphics/SilkFX/AndroidManifest.xml index c293589bdbaf..25092b52e2b6 100644 --- a/tests/graphics/SilkFX/AndroidManifest.xml +++ b/tests/graphics/SilkFX/AndroidManifest.xml @@ -23,12 +23,13 @@ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application android:label="SilkFX" - android:theme="@android:style/Theme.Material"> + android:theme="@style/Theme.UsefulDefault"> <activity android:name=".Main" android:label="SilkFX Demos" android:banner="@drawable/background1" - android:exported="true"> + android:exported="true" + android:theme="@style/Theme.UsefulDefault"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> diff --git a/tests/graphics/SilkFX/res/values/style.xml b/tests/graphics/SilkFX/res/values/style.xml index 66edbb5c9382..4dd626dfb8f5 100644 --- a/tests/graphics/SilkFX/res/values/style.xml +++ b/tests/graphics/SilkFX/res/values/style.xml @@ -23,9 +23,14 @@ <item name="android:windowElevation">0dp</item> <item name="buttonStyle">@style/AppTheme.Button</item> <item name="colorAccent">#bbffffff</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> <style name="AppTheme.Button" parent="Widget.AppCompat.Button"> <item name="android:textColor">#ffffffff</item> </style> + <style name="Theme.UsefulDefault" parent="android:Theme.Material"> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> + </style> + </resources> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp index 8d05a974dc40..4c531b8f9ee0 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp @@ -20,17 +20,29 @@ package { android_test { name: "ConcurrentMultiSessionImeTest", srcs: ["src/**/*.java"], + resource_dirs: ["res"], libs: ["android.test.runner"], static_libs: [ "androidx.test.ext.junit", + "androidx.test.rules", + "compatibility-device-util-axt", "platform-test-annotations", "platform-test-rules", "truth", + + // beadstead + "Nene", + "Harrier", + "TestApp", ], test_suites: [ "general-tests", ], - sdk_version: "current", + sdk_version: "test_current", + + data: [ + ":CtsMockInputMethod", + ], // Store test artifacts in separated directories for easier debugging. per_testcase_directory: true, diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml index 0defe5b3f2ff..2e336ca4f845 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml @@ -17,6 +17,17 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.inputmethod.multisessiontest"> + <application> + <uses-library android:name="android.test.runner" /> + <activity android:name=".MainActivity" + android:theme="@android:style/Theme.Material.NoActionBar" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml index fd598c568974..d5ed203488d5 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml @@ -17,13 +17,28 @@ <configuration description="Config for Concurrent Multi-Session IME tests"> <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController" type="module_controller"> - <option name="required-feature" value="android.software.input_methods" /> + <!-- TODO(b/323372972): require this feature once the bug is fixed. --> + <!-- option name="required-feature" value="android.software.input_methods" --> <!-- Currently enabled to automotive only --> <option name="required-feature" value="android.hardware.type.automotive" /> </object> <option name="test-suite-tag" value="apct" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="force-install-mode" value="FULL" /> + <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" /> + <option name="test-file-name" value="CtsMockInputMethod.apk" /> + </target_preparer> + + <!-- RunOnSecondaryUserTargetPreparer must run after SuiteApkInstaller. --> + <target_preparer class="com.android.tradefed.targetprep.RunOnSecondaryUserTargetPreparer"> + <option name="start-background-user" value="true" /> + <option name="test-package-name" value="com.android.server.inputmethod.multisessiontest" /> + <option name="test-package-name" value="com.android.cts.mockime" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" /> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> @@ -31,12 +46,6 @@ value="settings delete secure show_ime_with_hard_keyboard" /> </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="force-install-mode" value="FULL" /> - <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.server.inputmethod.multisessiontest" /> </test> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml new file mode 100644 index 000000000000..e16d28615c4d --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/res/layout/main_activity.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + <EditText + android:id="@+id/edit_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:hint="Input text here"/> +</FrameLayout> diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java index b66ceba458ac..fff1dd1a7cb1 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java @@ -16,34 +16,92 @@ package com.android.server.inputmethod.multisessiontest; -import static com.google.common.truth.Truth.assertThat; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import android.content.Context; -import android.content.pm.PackageManager; +import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.getResponderUserId; +import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.launchActivityAsUserSync; +import static com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityUtils.sendBundleAndWaitForReply; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_REQUEST_CODE; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_RESULT_CODE; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_HIDDEN; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REQUEST_IME_STATUS; -import androidx.test.platform.app.InstrumentationRegistry; +import static com.google.common.truth.Truth.assertWithMessage; +import android.content.ComponentName; +import android.os.Bundle; + +import androidx.test.core.app.ActivityScenario; + +import com.android.bedstead.harrier.BedsteadJUnit4; +import com.android.bedstead.harrier.DeviceState; + +import org.junit.After; import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -@RunWith(JUnit4.class) +@RunWith(BedsteadJUnit4.class) +@Ignore("b/345557347") public final class ConcurrentMultiUserTest { + @ClassRule + @Rule + public static final DeviceState sDeviceState = new DeviceState(); + + private static final ComponentName TEST_ACTIVITY = new ComponentName( + getInstrumentation().getTargetContext().getPackageName(), + MainActivity.class.getName()); + + private ActivityScenario<MainActivity> mActivityScenario; + private MainActivity mActivity; + private int mPeerUserId; + @Before - public void doBeforeEachTest() { - // No op + public void setUp() { + // Launch passenger activity. + mPeerUserId = getResponderUserId(); + launchActivityAsUserSync(TEST_ACTIVITY, mPeerUserId); + + // Launch driver activity. + mActivityScenario = ActivityScenario.launch(MainActivity.class); + mActivityScenario.onActivity(activity -> mActivity = activity); + } + + @After + public void tearDown() { + if (mActivityScenario != null) { + mActivityScenario.close(); + } } @Test - public void behaviorBeingTested_expectedResult() { - // Sample test - Context context = - InstrumentationRegistry.getInstrumentation().getTargetContext(); - assertThat(context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE)).isTrue(); - assertThat(context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_INPUT_METHODS)).isTrue(); + public void driverShowImeNotAffectPassenger() { + assertDriverImeHidden(); + assertPassengerImeHidden(); + + showDriverImeAndAssert(); + assertPassengerImeHidden(); + } + + private void assertDriverImeHidden() { + assertWithMessage("Driver IME should be hidden") + .that(mActivity.isMyImeVisible()).isFalse(); + } + + private void assertPassengerImeHidden() { + final Bundle bundleToSend = new Bundle(); + bundleToSend.putInt(KEY_REQUEST_CODE, REQUEST_IME_STATUS); + Bundle receivedBundle = sendBundleAndWaitForReply(TEST_ACTIVITY.getPackageName(), + mPeerUserId, bundleToSend); + assertWithMessage("Passenger IME should be hidden") + .that(receivedBundle.getInt(KEY_RESULT_CODE)).isEqualTo(REPLY_IME_HIDDEN); + } + + private void showDriverImeAndAssert() { + mActivity.showMyImeAndWait(); } } diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java new file mode 100644 index 000000000000..f1260008ca59 --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/MainActivity.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 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 com.android.server.inputmethod.multisessiontest; + +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_REQUEST_CODE; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.KEY_RESULT_CODE; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_HIDDEN; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REPLY_IME_SHOWN; +import static com.android.server.inputmethod.multisessiontest.TestRequestConstants.REQUEST_IME_STATUS; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Process; +import android.util.Log; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.android.compatibility.common.util.PollingCheck; +import com.android.compatibility.common.util.concurrentuser.ConcurrentUserActivityBase; + +/** + * An {@link Activity} to test multiple concurrent session IME. + */ +public final class MainActivity extends ConcurrentUserActivityBase { + private static final String TAG = ConcurrentMultiUserTest.class.getSimpleName(); + private static final long WAIT_IME_TIMEOUT_MS = 3000; + + private EditText mEditor; + private InputMethodManager mImm; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.v(TAG, "Create MainActivity as user " + + Process.myUserHandle().getIdentifier() + " on display " + + getDisplay().getDisplayId()); + setContentView(R.layout.main_activity); + mImm = getSystemService(InputMethodManager.class); + mEditor = requireViewById(R.id.edit_text); + } + + @Override + protected Bundle onBundleReceived(Bundle receivedBundle) { + final int requestCode = receivedBundle.getInt(KEY_REQUEST_CODE); + Log.v(TAG, "onBundleReceived() with request code:" + requestCode); + final Bundle replyBundle = new Bundle(); + switch (requestCode) { + case REQUEST_IME_STATUS: + replyBundle.putInt(KEY_RESULT_CODE, + isMyImeVisible() ? REPLY_IME_SHOWN : REPLY_IME_HIDDEN); + break; + default: + throw new RuntimeException("Received undefined request code:" + requestCode); + } + return replyBundle; + } + + boolean isMyImeVisible() { + final WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(mEditor); + return insets == null ? false : insets.isVisible(WindowInsetsCompat.Type.ime()); + } + + void showMyImeAndWait() { + Log.v(TAG, "showSoftInput"); + runOnUiThread(() -> { + // requestFocus() must run on UI thread. + if (!mEditor.requestFocus()) { + Log.e(TAG, "Failed to focus on mEditor"); + return; + } + if (!mImm.showSoftInput(mEditor, /* flags= */ 0)) { + Log.e(TAG, String.format("Failed to show my IME as user %d, " + + "mEditor:focused=%b,hasWindowFocus=%b", getUserId(), + mEditor.isFocused(), mEditor.hasWindowFocus())); + } + }); + PollingCheck.waitFor(WAIT_IME_TIMEOUT_MS, () -> isMyImeVisible(), + String.format("My IME (user %d) didn't show up", getUserId())); + } +} diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java new file mode 100644 index 000000000000..1501bfb69c92 --- /dev/null +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/TestRequestConstants.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 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 com.android.server.inputmethod.multisessiontest; + +final class TestRequestConstants { + private TestRequestConstants() { + } + + public static final String KEY_REQUEST_CODE = "key_request_code"; + public static final String KEY_RESULT_CODE = "key_result_code"; + + public static final int REQUEST_IME_STATUS = 1; + public static final int REPLY_IME_SHOWN = 2; + public static final int REPLY_IME_HIDDEN = 3; +} diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java index 0ec106e329f6..384a21e7c91a 100644 --- a/tests/testables/src/android/testing/TestableResources.java +++ b/tests/testables/src/android/testing/TestableResources.java @@ -26,6 +26,8 @@ import android.util.SparseArray; import org.mockito.invocation.InvocationOnMock; +import java.util.Arrays; + /** * Provides a version of Resources that defaults to all existing resources, but can have ids * changed to return specific values. @@ -103,6 +105,15 @@ public class TestableResources { if (index >= 0) { Object value = mOverrides.valueAt(index); if (value == null) throw new Resources.NotFoundException(); + // Support for Resources.getString(resId, Object... formatArgs) + if (value instanceof String + && invocationOnMock.getMethod().getName().equals("getString") + && invocationOnMock.getArguments().length > 1) { + value = String.format(mResources.getConfiguration().getLocales().get(0), + (String) value, + Arrays.copyOfRange(invocationOnMock.getArguments(), 1, + invocationOnMock.getArguments().length)); + } return value; } } catch (Resources.NotFoundException e) { diff --git a/tests/testables/src/android/testing/ViewUtils.java b/tests/testables/src/android/testing/ViewUtils.java index 80c2e8ddd907..0fad79d40c7a 100644 --- a/tests/testables/src/android/testing/ViewUtils.java +++ b/tests/testables/src/android/testing/ViewUtils.java @@ -31,13 +31,20 @@ public class ViewUtils { * This is currently done by adding the view to a window. */ public static void attachView(View view) { + attachView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + + /** + * Causes the view (and its children) to have {@link View#onAttachedToWindow()} called. + * + * This is currently done by adding the view to a window. + */ + public static void attachView(View view, int width, int height) { // Make sure hardware acceleration isn't turned on. view.getContext().getApplicationInfo().flags &= ~(ApplicationInfo.FLAG_HARDWARE_ACCELERATED); - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - LayoutParams.TYPE_APPLICATION_OVERLAY, - 0, PixelFormat.TRANSLUCENT); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, + LayoutParams.TYPE_APPLICATION_OVERLAY, 0, PixelFormat.TRANSLUCENT); view.getContext().getSystemService(WindowManager.class).addView(view, lp); } diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 7c5dcf8b95f7..e8be33cba3a1 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -51,6 +51,7 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.CutoutSpecificationTest", "android.view.DisplayCutoutTest", "android.view.DisplayShapeTest", + "android.view.ImeBackAnimationControllerTest", "android.view.InsetsAnimationControlImplTest", "android.view.InsetsControllerTest", "android.view.InsetsFlagsTest", |