diff options
Diffstat (limited to 'tests')
51 files changed, 2128 insertions, 169 deletions
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java index 08430f2f2744..4143f595f9a0 100644 --- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java +++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java @@ -159,7 +159,7 @@ public class BatteryUsageStatsPerfTest { private static BatteryUsageStats buildBatteryUsageStats() { final BatteryUsageStats.Builder builder = - new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, 0) + new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false, false, false, 0) .setBatteryCapacity(4000) .setDischargePercentage(20) .setDischargedPowerRange(1000, 2000) diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp index e09fbf6adc02..c681ce96a269 100644 --- a/tests/FlickerTests/ActivityEmbedding/Android.bp +++ b/tests/FlickerTests/ActivityEmbedding/Android.bp @@ -24,6 +24,9 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +//////////////////////////////////////////////////////////////////////////////// +// Begin to cleanup after CL merges + filegroup { name: "FlickerTestsOtherCommon-src", srcs: ["src/**/ActivityEmbeddingTestBase.kt"], @@ -82,3 +85,123 @@ android_test { ":FlickerTestsOtherCommon-src", ], } + +// End to cleanup after CL merges +//////////////////////////////////////////////////////////////////////////////// + +android_test { + name: "FlickerTestsActivityEmbedding", + 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", + "FlickerTestsOtherCommon", + ], + data: ["trace_config/*"], +} + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsActivityEmbedding module + +test_module_config { + name: "FlickerTestsActivityEmbedding-CatchAll", + base: "FlickerTestsActivityEmbedding", + exclude_filters: [ + "com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest", + "com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest", + "com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest", + "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest", + "com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest", + "com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest", + "com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest", + "com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest", + "com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest", + "com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest", + "com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Close-CloseSecondaryActivityInSplitTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.close.CloseSecondaryActivityInSplitTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-LayoutChange-HorizontalSplitChangeRatioTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.layoutchange.HorizontalSplitChangeRatioTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Open-MainActivityStartsSecondaryWithAlwaysExpandTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.open.MainActivityStartsSecondaryWithAlwaysExpandTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingPlaceholderSplitTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingPlaceholderSplitTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Open-OpenActivityEmbeddingSecondaryToSplitTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenActivityEmbeddingSecondaryToSplitTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Open-OpenThirdActivityOverSplitTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenThirdActivityOverSplitTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Open-OpenTrampolineActivityTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.open.OpenTrampolineActivityTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Pip-SecondaryActivityEnterPipTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.pip.SecondaryActivityEnterPipTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Rotation-RotateSplitNoChangeTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.rotation.RotateSplitNoChangeTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-Rtl-RTLStartSecondaryWithPlaceholderTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.rtl.RTLStartSecondaryWithPlaceholderTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsActivityEmbedding-SplitScreen-EnterSystemSplitTest", + base: "FlickerTestsActivityEmbedding", + include_filters: ["com.android.server.wm.flicker.activityembedding.splitscreen.EnterSystemSplitTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsActivityEmbedding module +//////////////////////////////////////////////////////////////////////////////// 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 ee2c05e82d51..06326f8cc8d2 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 @@ -36,7 +36,7 @@ abstract class RotationTransition(flicker: LegacyFlickerTest) : ActivityEmbeddin teardown { testApp.exit(wmHelper) } transitions { this.setRotation(flicker.scenario.endRotation) - if (!flicker.scenario.isTablet) { + if (!usesTaskbar) { wmHelper.StateSyncBuilder() .add(navBarInPosition(flicker.scenario.isGesturalNavigation)) .waitForAndVerify() diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp index d14a178fe316..8b45740aad7b 100644 --- a/tests/FlickerTests/AppClose/Android.bp +++ b/tests/FlickerTests/AppClose/Android.bp @@ -33,3 +33,34 @@ android_test { static_libs: ["FlickerTestsBase"], data: ["trace_config/*"], } + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsAppClose module + +test_module_config { + name: "FlickerTestsAppClose-CatchAll", + base: "FlickerTestsAppClose", + exclude_filters: [ + "com.android.server.wm.flicker.close.CloseAppBackButtonTest", + "com.android.server.wm.flicker.close.CloseAppHomeButtonTest", + "com.android.server.wm.flicker.close.", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppClose-CloseAppBackButtonTest", + base: "FlickerTestsAppClose", + include_filters: ["com.android.server.wm.flicker.close.CloseAppBackButtonTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppClose-CloseAppHomeButtonTest", + base: "FlickerTestsAppClose", + include_filters: ["com.android.server.wm.flicker.close.CloseAppHomeButtonTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsAppClose module +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp index 72a90650927f..b61739f100ab 100644 --- a/tests/FlickerTests/AppLaunch/Android.bp +++ b/tests/FlickerTests/AppLaunch/Android.bp @@ -15,6 +15,7 @@ // package { + default_team: "trendy_team_windowing_animations_transitions", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_base_license" @@ -23,6 +24,9 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +//////////////////////////////////////////////////////////////////////////////// +// Begin to cleanup after CL merges + filegroup { name: "FlickerTestsAppLaunchCommon-src", srcs: ["src/**/common/*"], @@ -69,3 +73,122 @@ android_test { ], data: ["trace_config/*"], } + +// End to cleanup after CL merges +//////////////////////////////////////////////////////////////////////////////// + +android_test { + name: "FlickerTestsAppLaunch", + defaults: ["FlickerTestsDefault"], + manifest: "AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", + srcs: ["src/**/*"], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsAppLaunchCommon", + ], + data: ["trace_config/*"], +} + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsAppLaunch module + +test_module_config { + name: "FlickerTestsAppLaunch-CatchAll", + base: "FlickerTestsAppLaunch", + exclude_filters: [ + "com.android.server.wm.flicker.launch.TaskTransitionTest", + "com.android.server.wm.flicker.launch.ActivityTransitionTest", + "com.android.server.wm.flicker.launch.OpenAppFromIconColdTest", + "com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest", + "com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest", + "com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest", + "com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest", + "com.android.server.wm.flicker.launch.OpenAppFromOverviewTest", + "com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest", + "com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition", + "com.android.server.wm.flicker.launch.OverrideTaskTransitionTest", + "com.android.server.wm.flicker.launch.TaskTransitionTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-ActivityTransitionTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.ActivityTransitionTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromIconColdTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIconColdTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromIntentColdAfterCameraTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdAfterCameraTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromIntentColdTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentColdTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromIntentWarmTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromIntentWarmTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromLockscreenViaIntentTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromLockscreenViaIntentTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenAppFromOverviewTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenAppFromOverviewTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenCameraFromHomeOnDoubleClickPowerButtonTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenCameraFromHomeOnDoubleClickPowerButtonTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OpenTransferSplashscreenAppFromLauncherTransition", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OpenTransferSplashscreenAppFromLauncherTransition"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-OverrideTaskTransitionTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.OverrideTaskTransitionTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppLaunch-TaskTransitionTest", + base: "FlickerTestsAppLaunch", + include_filters: ["com.android.server.wm.flicker.launch.TaskTransitionTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsAppLaunch module +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/FlickerTests/AppLaunch/OWNERS b/tests/FlickerTests/AppLaunch/OWNERS index 2c414a27cacb..d16b57dcb84c 100644 --- a/tests/FlickerTests/AppLaunch/OWNERS +++ b/tests/FlickerTests/AppLaunch/OWNERS @@ -1,4 +1,2 @@ -# System UI > ... > Overview (recent apps) > UI -# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview* # window manager > animations/transitions # Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192 diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt index 44ae27c2ee4b..adeba72c9c96 100644 --- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt @@ -75,7 +75,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @FlakyTest(bugId = 288341660) @Test fun navBarLayerVisibilityChanges() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.assertLayers { this.isInvisible(ComponentNameMatcher.NAV_BAR) .then() @@ -97,7 +97,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @FlakyTest(bugId = 293581770) @Test fun navBarWindowsVisibilityChanges() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.assertWm { this.isNonAppWindowInvisible(ComponentNameMatcher.NAV_BAR) .then() @@ -112,7 +112,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @Presubmit @Test fun taskBarLayerIsVisibleAtEnd() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) } } @@ -170,7 +170,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @Presubmit @Test fun navBarLayerIsVisibleAtEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) } } @@ -184,7 +184,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @Presubmit @Test override fun appLayerBecomesVisible() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) super.appLayerBecomesVisible() } @@ -192,7 +192,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : @FlakyTest(bugId = 227143265) @Test fun appLayerBecomesVisibleTablet() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) super.appLayerBecomesVisible() } diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt index 8a3304b0343d..b497e3048759 100644 --- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt @@ -28,6 +28,7 @@ abstract class OpenAppFromIconTransition(flicker: LegacyFlickerTest) : get() = { super.transition(this) setup { + // By default, launcher doesn't rotate on phones, but rotates on tablets if (flicker.scenario.isTablet) { tapl.setExpectedRotation(flicker.scenario.startRotation.value) } else { diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt index f8fd35860f6f..a6e31d49a0e8 100644 --- a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt +++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt @@ -103,7 +103,7 @@ abstract class OpenAppFromLockscreenTransition(flicker: LegacyFlickerTest) : @Presubmit @Test open fun navBarLayerPositionAtEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarLayerPositionAtEnd() } diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp index 78d93e1cb32a..f80e6b4b2f5e 100644 --- a/tests/FlickerTests/IME/Android.bp +++ b/tests/FlickerTests/IME/Android.bp @@ -24,6 +24,9 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +//////////////////////////////////////////////////////////////////////////////// +// Begin to cleanup after CL merges + filegroup { name: "FlickerTestsImeCommon-src", srcs: ["src/**/common/*"], @@ -39,6 +42,9 @@ filegroup { srcs: ["src/**/ShowImeOnAppStart*"], } +// End to cleanup after CL merges +//////////////////////////////////////////////////////////////////////////////// + android_test { name: "FlickerTestsIme", defaults: ["FlickerTestsDefault"], @@ -53,6 +59,9 @@ android_test { data: ["trace_config/*"], } +//////////////////////////////////////////////////////////////////////////////// +// Begin to cleanup after CL merges + java_library { name: "FlickerTestsImeCommon", defaults: ["FlickerTestsDefault"], @@ -107,3 +116,140 @@ android_test { ], data: ["trace_config/*"], } + +// End to cleanup after CL merges +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsIme module + +test_module_config { + name: "FlickerTestsIme-CatchAll", + base: "FlickerTestsIme", + exclude_filters: [ + "com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest", + "com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest", + "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest", + "com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest", + "com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest", + "com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest", + "com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest", + "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest", + "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest", + "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest", + "com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest", + "com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest", + "com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest", + "com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest", + "com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeOnDismissPopupDialogTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnDismissPopupDialogTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeOnGoHomeTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeOnGoHomeTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeShownOnAppStartOnGoHomeTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartOnGoHomeTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeShownOnAppStartToAppOnPressBackTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeShownOnAppStartToAppOnPressBackTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeToAppOnPressBackTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeToAppOnPressBackTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-CloseImeToHomeOnFinishActivityTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.CloseImeToHomeOnFinishActivityTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-OpenImeWindowToFixedPortraitAppTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.OpenImeWindowToFixedPortraitAppTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromOverviewTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromOverviewTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeOnAppStartWhenLaunchingAppTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnAppStartWhenLaunchingAppTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeOnUnlockScreenTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeOnUnlockScreenTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeWhenFocusingOnInputFieldTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhenFocusingOnInputFieldTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeWhileDismissingThemedPopupDialogTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileDismissingThemedPopupDialogTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsIme-ShowImeWhileEnteringOverviewTest", + base: "FlickerTestsIme", + include_filters: ["com.android.server.wm.flicker.ime.ShowImeWhileEnteringOverviewTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsIme module +//////////////////////////////////////////////////////////////////////////////// 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 dc2bd1bc9996..522c68bba0d1 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 @@ -72,7 +72,7 @@ class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicke @Presubmit @Test override fun navBarLayerPositionAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart) flicker.navBarLayerPositionAtStartAndEnd() } @@ -80,7 +80,7 @@ class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicke @Presubmit @Test fun navBarLayerPositionAtStartAndEndLandscapeOrSeascapeAtStart() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart) flicker.navBarLayerPositionAtStartAndEnd() } 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 c96c760e2d7b..eb63e4985a9f 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 @@ -28,6 +28,7 @@ import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd +import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Ignore @@ -93,7 +94,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl @Presubmit @Test fun navBarLayerIsVisibleAtStartAndEnd3Button() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) Assume.assumeFalse(flicker.scenario.isGesturalNavigation) flicker.navBarLayerIsVisibleAtStartAndEnd() } @@ -105,7 +106,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl @Presubmit @Test fun navBarLayerIsInvisibleInLandscapeGestural() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart) Assume.assumeTrue(flicker.scenario.isGesturalNavigation) flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) } @@ -114,7 +115,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl /** * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions - * this is fixed and the nav bar shows as invisible + * this is fixed and the status bar shows as invisible */ @Presubmit @Test @@ -128,7 +129,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl /** * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions - * this is fixed and the nav bar shows as invisible + * this is fixed and the status bar shows as invisible */ @Presubmit @Test @@ -149,6 +150,10 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl @Ignore("Visibility changes depending on orientation and navigation mode") override fun navBarLayerPositionAtStartAndEnd() {} + @Test + @Ignore("Visibility changes depending on orientation and navigation mode") + override fun taskBarLayerIsVisibleAtStartAndEnd() {} + /** {@inheritDoc} */ @Test @Ignore("Visibility changes depending on orientation and navigation mode") @@ -161,7 +166,10 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl @Presubmit @Test - override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd() + fun taskBarLayerIsVisibleAtStartAndEndForTablets() { + Assume.assumeTrue(flicker.scenario.isTablet) + flicker.taskBarLayerIsVisibleAtStartAndEnd() + } @Presubmit @Test @@ -174,7 +182,7 @@ class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(fl @Test fun statusBarLayerIsInvisibleInLandscape() { Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart) - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) } flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) } } diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp index 4648383b2771..06daaafacbd8 100644 --- a/tests/FlickerTests/Notification/Android.bp +++ b/tests/FlickerTests/Notification/Android.bp @@ -32,3 +32,57 @@ android_test { static_libs: ["FlickerTestsBase"], data: ["trace_config/*"], } + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsNotification module + +test_module_config { + name: "FlickerTestsNotification-CatchAll", + base: "FlickerTestsNotification", + exclude_filters: [ + "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest", + "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest", + "com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest", + "com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest", + "com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationColdTest", + base: "FlickerTestsNotification", + include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationColdTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWarmTest", + base: "FlickerTestsNotification", + include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWarmTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsNotification-OpenAppFromLockscreenNotificationWithOverlayAppTest", + base: "FlickerTestsNotification", + include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromLockscreenNotificationWithOverlayAppTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsNotification-OpenAppFromNotificationColdTest", + base: "FlickerTestsNotification", + include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationColdTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsNotification-OpenAppFromNotificationWarmTest", + base: "FlickerTestsNotification", + include_filters: ["com.android.server.wm.flicker.notification.OpenAppFromNotificationWarmTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsNotification module +//////////////////////////////////////////////////////////////////////////////// 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 07fc2300286a..ad70757a9a4d 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 @@ -151,7 +151,7 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : @Presubmit @Test open fun taskBarWindowIsVisibleAtEnd() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) flicker.taskBarWindowIsVisibleAtEnd() } @@ -163,7 +163,7 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : @Presubmit @Test open fun taskBarLayerIsVisibleAtEnd() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) flicker.taskBarLayerIsVisibleAtEnd() } @@ -171,7 +171,7 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : @Presubmit @Test open fun navBarLayerPositionAtEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarLayerPositionAtEnd() } @@ -179,14 +179,14 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) : @Presubmit @Test open fun navBarLayerIsVisibleAtEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarLayerIsVisibleAtEnd() } @Presubmit @Test open fun navBarWindowIsVisibleAtEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarWindowIsVisibleAtEnd() } diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp index 8755d0e3b304..4d5dba3d9221 100644 --- a/tests/FlickerTests/QuickSwitch/Android.bp +++ b/tests/FlickerTests/QuickSwitch/Android.bp @@ -32,3 +32,41 @@ android_test { static_libs: ["FlickerTestsBase"], data: ["trace_config/*"], } + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsQuickswitch module + +test_module_config { + name: "FlickerTestsQuickswitch-CatchAll", + base: "FlickerTestsQuickswitch", + exclude_filters: [ + "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest", + "com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest", + "com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsBackTest", + base: "FlickerTestsQuickswitch", + include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsBackTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsQuickswitch-QuickSwitchBetweenTwoAppsForwardTest", + base: "FlickerTestsQuickswitch", + include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchBetweenTwoAppsForwardTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsQuickswitch-QuickSwitchFromLauncherTest", + base: "FlickerTestsQuickswitch", + include_filters: ["com.android.server.wm.flicker.quickswitch.QuickSwitchFromLauncherTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsQuickswitch module +//////////////////////////////////////////////////////////////////////////////// diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp index aceb8bad256f..0884ef9734b0 100644 --- a/tests/FlickerTests/Rotation/Android.bp +++ b/tests/FlickerTests/Rotation/Android.bp @@ -37,3 +37,41 @@ android_test { static_libs: ["FlickerTestsBase"], data: ["trace_config/*"], } + +//////////////////////////////////////////////////////////////////////////////// +// Begin breakdowns for FlickerTestsRotation module + +test_module_config { + name: "FlickerTestsAppRotation-CatchAll", + base: "FlickerTestsRotation", + exclude_filters: [ + "com.android.server.wm.flicker.rotation.ChangeAppRotationTest", + "com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest", + "com.android.server.wm.flicker.rotation.SeamlessAppRotationTest", + ], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppRotation-ChangeAppRotationTest", + base: "FlickerTestsRotation", + include_filters: ["com.android.server.wm.flicker.rotation.ChangeAppRotationTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppRotation-OpenShowWhenLockedSeamlessAppRotationTest", + base: "FlickerTestsRotation", + include_filters: ["com.android.server.wm.flicker.rotation.OpenShowWhenLockedSeamlessAppRotationTest"], + test_suites: ["device-tests"], +} + +test_module_config { + name: "FlickerTestsAppRotation-SeamlessAppRotationTest", + base: "FlickerTestsRotation", + include_filters: ["com.android.server.wm.flicker.rotation.SeamlessAppRotationTest"], + test_suites: ["device-tests"], +} + +// End breakdowns for FlickerTestsRotation module +//////////////////////////////////////////////////////////////////////////////// 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 060015bcc4b2..851ce022bd81 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -26,6 +26,7 @@ import android.tools.traces.component.ComponentNameMatcher import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.wm.shell.Flags import org.junit.Assume import org.junit.AssumptionViolatedException import org.junit.Test @@ -48,6 +49,9 @@ constructor( private val logTag = this::class.java.simpleName + protected val usesTaskbar: Boolean + get() = flicker.scenario.isTablet || Flags.enableTaskbarOnPhones() + /** Specification of the test transition to execute */ abstract val transition: FlickerBuilder.() -> Unit @@ -87,7 +91,7 @@ constructor( @Presubmit @Test open fun navBarLayerIsVisibleAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarLayerIsVisibleAtStartAndEnd() } @@ -100,7 +104,7 @@ constructor( @Presubmit @Test open fun navBarLayerPositionAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarLayerPositionAtStartAndEnd() } @@ -112,7 +116,7 @@ constructor( @Presubmit @Test open fun navBarWindowIsAlwaysVisible() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart) flicker.navBarWindowIsAlwaysVisible() } @@ -126,32 +130,28 @@ constructor( @Presubmit @Test open fun navBarWindowIsVisibleAtStartAndEnd() { - Assume.assumeFalse(flicker.scenario.isTablet) + Assume.assumeFalse(usesTaskbar) flicker.navBarWindowIsVisibleAtStartAndEnd() } /** * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the * transition - * - * Note: Large screen only */ @Presubmit @Test open fun taskBarLayerIsVisibleAtStartAndEnd() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) flicker.taskBarLayerIsVisibleAtStartAndEnd() } /** * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition - * - * Note: Large screen only */ @Presubmit @Test open fun taskBarWindowIsAlwaysVisible() { - Assume.assumeTrue(flicker.scenario.isTablet) + Assume.assumeTrue(usesTaskbar) flicker.taskBarWindowIsAlwaysVisible() } 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 index 5121f6677cea..8811e00f9661 100644 --- 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 @@ -17,6 +17,7 @@ package com.android.server.wm.flicker.helpers import android.graphics.Rect +import android.platform.uiautomator_helpers.DeviceHelpers import android.tools.device.apphelpers.IStandardAppHelper import android.tools.helpers.SYSTEMUI_PACKAGE import android.tools.traces.parsers.WindowManagerStateHelper @@ -26,6 +27,7 @@ import androidx.test.uiautomator.BySelector import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until +import java.time.Duration /** * Wrapper class around App helper classes. This class adds functionality to the apps that the @@ -41,16 +43,6 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : 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 @@ -123,7 +115,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : ) error("expected a freeform window with caption but window is not in freeform mode") val captions = - device.wait(Until.findObjects(caption), TIMEOUT_MS) + device.wait(Until.findObjects(caption), TIMEOUT.toMillis()) ?: error("Unable to find view $caption\n") return captions.find { @@ -200,6 +192,33 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : device.drag(startX, startY, endX, endY, 100) } + fun enterDesktopModeFromAppHandleMenu( + wmHelper: WindowManagerStateHelper, + device: UiDevice) { + val windowRect = wmHelper.getWindowRegion(innerHelper).bounds + val startX = windowRect.centerX() + // Click a little under the top to prevent opening the notification shade. + val startY = 10 + + // Click on the app handle coordinates. + device.click(startX, startY) + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() + + val pill = getAppHandlePillForWindow() + val desktopModeButton = + pill + ?.children + ?.find { it.resourceName.endsWith(DESKTOP_MODE_BUTTON) } + + desktopModeButton?.click() + wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() + } + + private fun getAppHandlePillForWindow(): UiObject2? { + val pillContainer: BySelector = By.res(SYSTEMUI_PACKAGE, PILL_CONTAINER) + return DeviceHelpers.waitForObj(pillContainer, TIMEOUT) + } + /** Wait for transition to full screen to finish. */ private fun waitForTransitionToFullscreen(wmHelper: WindowManagerStateHelper) { wmHelper @@ -208,4 +227,15 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : .withAppTransitionIdle() .waitForAndVerify() } + + private companion object { + val TIMEOUT = Duration.ofSeconds(3) + val CAPTION = "desktop_mode_caption" + val MAXIMIZE_BUTTON_VIEW = "maximize_button_view" + val CLOSE_BUTTON = "close_window" + val PILL_CONTAINER = "windowing_pill" + val DESKTOP_MODE_BUTTON = "desktop_button" + val caption: BySelector + get() = By.res(SYSTEMUI_PACKAGE, CAPTION) + } } diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml index 36cbf1a8fe84..365a0ea017f6 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml @@ -27,15 +27,6 @@ where things are arranged differently and to circle back up to the top once we reach the bottom. --> - <!-- View used for testing sourceRectHint. --> - <View - android:id="@+id/source_rect" - android:layout_width="320dp" - android:layout_height="180dp" - android:visibility="gone" - android:background="@android:color/holo_green_light" - /> - <Button android:id="@+id/enter_pip" android:layout_width="wrap_content" @@ -122,12 +113,11 @@ android:onClick="onRatioSelected"/> </RadioGroup> - <Button + <CheckBox android:id="@+id/set_source_rect_hint" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Set SourceRectHint" - android:onClick="setSourceRectHint"/> + android:text="Set SourceRectHint"/> <TextView android:layout_width="wrap_content" 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 27eb5a06451a..13d7f7f0d521 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 @@ -43,10 +43,10 @@ import android.media.MediaMetadata; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Bundle; +import android.util.DisplayMetrics; import android.util.Log; import android.util.Rational; import android.view.View; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.widget.CheckBox; @@ -70,7 +70,7 @@ public class PipActivity extends Activity { */ private static final String TITLE_STATE_PAUSED = "TestApp media is paused"; - private static final Rational RATIO_DEFAULT = null; + private static final Rational RATIO_DEFAULT = new Rational(16, 9); private static final Rational RATIO_SQUARE = new Rational(1, 1); private static final Rational RATIO_WIDE = new Rational(2, 1); private static final Rational RATIO_TALL = new Rational(1, 2); @@ -88,8 +88,7 @@ public class PipActivity extends Activity { "com.android.wm.shell.flicker.testapp.ASPECT_RATIO"; private final PictureInPictureParams.Builder mPipParamsBuilder = - new PictureInPictureParams.Builder() - .setAspectRatio(RATIO_DEFAULT); + new PictureInPictureParams.Builder(); private MediaSession mMediaSession; private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder() .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP) @@ -139,6 +138,9 @@ public class PipActivity extends Activity { } }; + private Rational mAspectRatio = RATIO_DEFAULT; + private boolean mEnableSourceRectHint; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -156,6 +158,14 @@ public class PipActivity extends Activity { findViewById(R.id.media_session_stop) .setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED)); + final CheckBox setSourceRectHintCheckBox = findViewById(R.id.set_source_rect_hint); + setSourceRectHintCheckBox.setOnCheckedChangeListener((v, isChecked) -> { + if (mEnableSourceRectHint != isChecked) { + mEnableSourceRectHint = isChecked; + updateSourceRectHint(); + } + }); + mMediaSession = new MediaSession(this, "WMShell_TestApp"); mMediaSession.setPlaybackState(mPlaybackStateBuilder.build()); mMediaSession.setCallback(new MediaSession.Callback() { @@ -250,47 +260,64 @@ public class PipActivity extends Activity { } } + private void updateSourceRectHint() { + if (!mEnableSourceRectHint) return; + // Similar to PipUtils#getEnterPipWithOverlaySrcRectHint, crop the display bounds + // as source rect hint based on the current aspect ratio. + final DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + final Rect displayBounds = new Rect(0, 0, + displayMetrics.widthPixels, displayMetrics.heightPixels); + final Rect sourceRectHint = getEnterPipWithOverlaySrcRectHint( + displayBounds, mAspectRatio.floatValue()); + mPipParamsBuilder + .setAspectRatio(mAspectRatio) + .setSourceRectHint(sourceRectHint); + setPictureInPictureParams(mPipParamsBuilder.build()); + } + /** - * Adds a temporary view used for testing sourceRectHint. - * + * Crop a Rect matches the aspect ratio and pivots at the center point. + * This is a counterpart of {@link PipUtils#getEnterPipWithOverlaySrcRectHint} */ - public void setSourceRectHint(View v) { - View rectView = findViewById(R.id.source_rect); - if (rectView != null) { - rectView.setVisibility(View.VISIBLE); - rectView.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - Rect boundingRect = new Rect(); - rectView.getGlobalVisibleRect(boundingRect); - mPipParamsBuilder.setSourceRectHint(boundingRect); - setPictureInPictureParams(mPipParamsBuilder.build()); - rectView.getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - }); - rectView.invalidate(); // changing the visibility, invalidating to redraw the view + private Rect getEnterPipWithOverlaySrcRectHint(Rect appBounds, float aspectRatio) { + final float appBoundsAspectRatio = appBounds.width() / (float) appBounds.height(); + final int width, height; + int left = appBounds.left; + int top = appBounds.top; + if (appBoundsAspectRatio < aspectRatio) { + width = appBounds.width(); + height = (int) (width / aspectRatio); + top = appBounds.top + (appBounds.height() - height) / 2; + } else { + height = appBounds.height(); + width = (int) (height * aspectRatio); + left = appBounds.left + (appBounds.width() - width) / 2; } + return new Rect(left, top, left + width, top + height); } public void onRatioSelected(View v) { switch (v.getId()) { case R.id.ratio_default: - mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT); + mAspectRatio = RATIO_DEFAULT; break; case R.id.ratio_square: - mPipParamsBuilder.setAspectRatio(RATIO_SQUARE); + mAspectRatio = RATIO_SQUARE; break; case R.id.ratio_wide: - mPipParamsBuilder.setAspectRatio(RATIO_WIDE); + mAspectRatio = RATIO_WIDE; break; case R.id.ratio_tall: - mPipParamsBuilder.setAspectRatio(RATIO_TALL); + mAspectRatio = RATIO_TALL; break; } + setPictureInPictureParams(mPipParamsBuilder.setAspectRatio(mAspectRatio).build()); + if (mEnableSourceRectHint) { + updateSourceRectHint(); + } } private void updateMediaSessionState(int newState) { diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index f367c38b06e9..06c2651b604d 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -48,6 +48,7 @@ android_test { "testables", "testng", "truth", + "ui-trace-collector", ], libs: [ "android.test.mock", diff --git a/tests/Input/AndroidTest.xml b/tests/Input/AndroidTest.xml index 4a99bd4f1801..bc9322fbd3dc 100644 --- a/tests/Input/AndroidTest.xml +++ b/tests/Input/AndroidTest.xml @@ -22,6 +22,10 @@ <option name="shell-timeout" value="660s" /> <option name="test-timeout" value="600s" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="device-listeners" value="android.tools.collectors.DefaultUITraceListener"/> + <!-- DefaultUITraceListener args --> + <option name="instrumentation-arg" key="skip_test_success_metrics" value="true"/> + <option name="instrumentation-arg" key="per_class" value="true"/> </test> <object class="com.android.tradefed.testtype.suite.module.TestFailureModuleController" type="module_controller"> @@ -32,6 +36,8 @@ <option name="pull-pattern-keys" value="input_.*" /> <!-- Pull files created by tests, like the output of screenshot tests --> <option name="directory-keys" value="/sdcard/Download/InputTests" /> + <!-- Pull perfetto traces from DefaultUITraceListener --> + <option name="pull-pattern-keys" value="perfetto_file_path*" /> <option name="collect-on-run-ended-only" value="false" /> </metrics_collector> </configuration> diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt new file mode 100644 index 000000000000..14aac6637d4f --- /dev/null +++ b/tests/Input/src/android/hardware/input/KeyGestureEventListenerTest.kt @@ -0,0 +1,202 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input + +import android.content.Context +import android.content.ContextWrapper +import android.os.Handler +import android.os.HandlerExecutor +import android.os.test.TestLooper +import android.platform.test.annotations.Presubmit +import android.platform.test.flag.junit.SetFlagsRule +import android.view.KeyEvent +import androidx.test.core.app.ApplicationProvider +import com.android.server.testutils.any +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnitRunner +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.fail + +/** + * Tests for [InputManager.KeyGestureEventListener]. + * + * Build/Install/Run: + * atest InputTests:KeyGestureEventListenerTest + */ +@Presubmit +@RunWith(MockitoJUnitRunner::class) +class KeyGestureEventListenerTest { + + companion object { + const val DEVICE_ID = 1 + val HOME_GESTURE_EVENT = KeyGestureEvent( + DEVICE_ID, + intArrayOf(KeyEvent.KEYCODE_H), + KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_HOME + ) + } + + @get:Rule + val rule = SetFlagsRule() + + private val testLooper = TestLooper() + private val executor = HandlerExecutor(Handler(testLooper.looper)) + private var registeredListener: IKeyGestureEventListener? = null + private lateinit var context: Context + private lateinit var inputManager: InputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession + + @Mock + private lateinit var iInputManagerMock: IInputManager + + @Before + fun setUp() { + context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) + inputManager = InputManager(context) + `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) + .thenReturn(inputManager) + + // Handle key gesture event listener registration. + doAnswer { + val listener = it.getArgument(0) as IKeyGestureEventListener + if (registeredListener != null && + registeredListener!!.asBinder() != listener.asBinder()) { + // There can only be one registered key gesture event listener per process. + fail("Trying to register a new listener when one already exists") + } + registeredListener = listener + null + }.`when`(iInputManagerMock).registerKeyGestureEventListener(any()) + + // Handle key gesture event listener being unregistered. + doAnswer { + val listener = it.getArgument(0) as IKeyGestureEventListener + if (registeredListener == null || + registeredListener!!.asBinder() != listener.asBinder()) { + fail("Trying to unregister a listener that is not registered") + } + registeredListener = null + null + }.`when`(iInputManagerMock).unregisterKeyGestureEventListener(any()) + } + + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + + private fun notifyKeyGestureEvent(event: KeyGestureEvent) { + registeredListener!!.onKeyGestureEvent( + event.deviceId, + event.keycodes, + event.modifierState, + event.keyGestureType + ) + } + + @Test + fun testListenerHasCorrectGestureNotified() { + var callbackCount = 0 + + // Add a key gesture event listener + inputManager.registerKeyGestureEventListener(executor) { + event: KeyGestureEvent -> + assertEquals(HOME_GESTURE_EVENT, event) + callbackCount++ + } + + // Notifying key gesture event will notify the listener. + notifyKeyGestureEvent(HOME_GESTURE_EVENT) + testLooper.dispatchNext() + assertEquals(1, callbackCount) + } + + @Test + fun testAddingListenersRegistersInternalCallbackListener() { + // Set up two callbacks. + val callback1 = InputManager.KeyGestureEventListener { _ -> } + val callback2 = InputManager.KeyGestureEventListener { _ -> } + + assertNull(registeredListener) + + // Adding the listener should register the callback with InputManagerService. + inputManager.registerKeyGestureEventListener(executor, callback1) + assertNotNull(registeredListener) + + // Adding another listener should not register new internal listener. + val currListener = registeredListener + inputManager.registerKeyGestureEventListener(executor, callback2) + assertEquals(currListener, registeredListener) + } + + @Test + fun testRemovingListenersUnregistersInternalCallbackListener() { + // Set up two callbacks. + val callback1 = InputManager.KeyGestureEventListener { _ -> } + val callback2 = InputManager.KeyGestureEventListener { _ -> } + + inputManager.registerKeyGestureEventListener(executor, callback1) + inputManager.registerKeyGestureEventListener(executor, callback2) + + // Only removing all listeners should remove the internal callback + inputManager.unregisterKeyGestureEventListener(callback1) + assertNotNull(registeredListener) + inputManager.unregisterKeyGestureEventListener(callback2) + assertNull(registeredListener) + } + + @Test + fun testMultipleListeners() { + // Set up two callbacks. + var callbackCount1 = 0 + var callbackCount2 = 0 + val callback1 = InputManager.KeyGestureEventListener { _ -> callbackCount1++ } + val callback2 = InputManager.KeyGestureEventListener { _ -> callbackCount2++ } + + // Add both key gesture event listeners + inputManager.registerKeyGestureEventListener(executor, callback1) + inputManager.registerKeyGestureEventListener(executor, callback2) + + // Notifying key gesture event, should notify both the callbacks. + notifyKeyGestureEvent(HOME_GESTURE_EVENT) + testLooper.dispatchAll() + assertEquals(1, callbackCount1) + assertEquals(1, callbackCount2) + + inputManager.unregisterKeyGestureEventListener(callback2) + // Notifying key gesture event, should still trigger callback1 but not + // callback2. + notifyKeyGestureEvent(HOME_GESTURE_EVENT) + testLooper.dispatchAll() + assertEquals(2, callbackCount1) + assertEquals(1, callbackCount2) + } +} diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 3c72498082e4..8829f74f5092 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -151,6 +151,7 @@ class InputManagerServiceTests { verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean()) verify(native).setTouchpadTapToClickEnabled(anyBoolean()) verify(native).setTouchpadTapDraggingEnabled(anyBoolean()) + verify(native).setShouldNotifyTouchpadHardwareState(anyBoolean()) verify(native).setTouchpadRightClickZoneEnabled(anyBoolean()) verify(native).setShowTouches(anyBoolean()) verify(native).setMotionClassifierEnabled(anyBoolean()) diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt new file mode 100644 index 000000000000..3f611e0ead53 --- /dev/null +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -0,0 +1,96 @@ +/* + * 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.server.input + +import android.content.Context +import android.content.ContextWrapper +import android.hardware.input.IKeyGestureEventListener +import android.hardware.input.KeyGestureEvent +import android.platform.test.annotations.Presubmit +import android.view.KeyEvent +import androidx.test.core.app.ApplicationProvider +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mockito +import org.mockito.junit.MockitoJUnit + +/** + * Tests for {@link KeyGestureController}. + * + * Build/Install/Run: + * atest InputTests:KeyGestureControllerTests + */ +@Presubmit +class KeyGestureControllerTests { + + companion object { + val DEVICE_ID = 1 + val HOME_GESTURE_EVENT = KeyGestureEvent( + DEVICE_ID, + intArrayOf(KeyEvent.KEYCODE_H), + KeyEvent.META_META_ON or KeyEvent.META_META_LEFT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_HOME + ) + } + + @get:Rule + val rule = MockitoJUnit.rule()!! + + private lateinit var keyGestureController: KeyGestureController + private lateinit var context: Context + private var lastEvent: KeyGestureEvent? = null + + @Before + fun setup() { + context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + keyGestureController = KeyGestureController() + } + + @Test + fun testKeyGestureEvent_registerUnregisterListener() { + val listener = KeyGestureEventListener() + + // Register key gesture event listener + keyGestureController.registerKeyGestureEventListener(listener, 0) + keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT) + assertEquals( + "Listener should get callback on key gesture event", + HOME_GESTURE_EVENT, + lastEvent!! + ) + + // Unregister listener + lastEvent = null + keyGestureController.unregisterKeyGestureEventListener(listener, 0) + keyGestureController.onKeyGestureEvent(HOME_GESTURE_EVENT) + assertNull("Listener should not get callback after being unregistered", lastEvent) + } + + inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() { + override fun onKeyGestureEvent( + deviceId: Int, + keycodes: IntArray, + modifierState: Int, + gestureType: Int + ) { + lastEvent = KeyGestureEvent(deviceId, keycodes, modifierState, gestureType) + } + } +}
\ No newline at end of file diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index 8d1fc508ffe7..d32cedb24a36 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -40,7 +40,7 @@ import androidx.test.uiautomator.Until import com.android.cts.input.DebugInputRule import com.android.cts.input.UinputTouchScreen -import java.util.concurrent.TimeUnit +import java.time.Duration import org.junit.After import org.junit.Assert.assertEquals @@ -193,6 +193,6 @@ class AnrTest { val flags = " -W -n " val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity" instrumentation.uiAutomation.executeShellCommand(startCmd) - waitForStableWindowGeometry(5L, TimeUnit.SECONDS) + waitForStableWindowGeometry(Duration.ofSeconds(5)) } } diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt index c1a86b3a2dac..015e188fc98e 100644 --- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt +++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt @@ -18,12 +18,20 @@ package com.android.test.input import android.view.InputDevice.SOURCE_MOUSE import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.InputDevice.SOURCE_STYLUS +import android.view.InputDevice.SOURCE_TOUCHPAD + import android.view.InputEventAssigner import android.view.KeyEvent import android.view.MotionEvent import org.junit.Assert.assertEquals import org.junit.Test +sealed class StreamEvent +private data object Vsync : StreamEvent() +data class MotionEventData(val action: Int, val source: Int, val id: Int, val expectedId: Int) : + StreamEvent() + /** * Create a MotionEvent with the provided action, eventTime, and source */ @@ -49,64 +57,164 @@ private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent { return KeyEvent(eventTime, eventTime, action, code, repeat) } +/** + * Check that the correct eventIds are assigned in a stream. The stream consists of motion + * events or vsync (processed frame) + * Each streamEvent should have unique ids when writing tests + * The test passes even if two events get assigned the same eventId, since the mapping is + * streamEventId -> motionEventId and streamEvents have unique ids + */ +private fun checkEventStream(vararg streamEvents: StreamEvent) { + val assigner = InputEventAssigner() + var eventTime = 10L + // Maps MotionEventData.id to MotionEvent.id + // We can't control the event id of the generated motion events but for testing it's easier + // to label the events with a custom id for readability + val eventIdMap: HashMap<Int, Int> = HashMap() + for (streamEvent in streamEvents) { + when (streamEvent) { + is MotionEventData -> { + val event = createMotionEvent(streamEvent.action, eventTime, streamEvent.source) + eventIdMap[streamEvent.id] = event.id + val eventId = assigner.processEvent(event) + assertEquals(eventIdMap[streamEvent.expectedId], eventId) + } + is Vsync -> assigner.notifyFrameProcessed() + } + eventTime += 1 + } +} + class InputEventAssignerTest { companion object { private const val TAG = "InputEventAssignerTest" } /** - * A single MOVE event should be assigned to the next available frame. + * A single event should be assigned to the next available frame. */ @Test - fun testTouchGesture() { - val assigner = InputEventAssigner() - val event = createMotionEvent(MotionEvent.ACTION_MOVE, 10, SOURCE_TOUCHSCREEN) - val eventId = assigner.processEvent(event) - assertEquals(event.id, eventId) + fun testTouchMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_TOUCHSCREEN, id = 1, expectedId = 1) + ) + } + + @Test + fun testMouseMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_MOUSE, id = 1, expectedId = 1) + ) + } + + @Test + fun testMouseScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 1, expectedId = 1) + ) + } + + @Test + fun testStylusMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) + } + + @Test + fun testStylusHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_HOVER_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) + } + + @Test + fun testTouchpadMove() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_MOVE, SOURCE_STYLUS, id = 1, expectedId = 1) + ) } /** - * DOWN event should be used until a vsync comes in. After vsync, the latest event should be - * produced. + * Test that before a VSYNC the event id generated by input event assigner for move events is + * the id of the down event. Move events coming after a VSYNC should be assigned their own event + * id */ + private fun testDownAndMove(source: Int) { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, source, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_MOVE, source, id = 2, expectedId = 1), + Vsync, + MotionEventData(MotionEvent.ACTION_MOVE, source, id = 4, expectedId = 4) + ) + } + @Test - fun testTouchDownWithMove() { - val assigner = InputEventAssigner() - val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_TOUCHSCREEN) - val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_TOUCHSCREEN) - val move2 = createMotionEvent(MotionEvent.ACTION_MOVE, 13, SOURCE_TOUCHSCREEN) - val move3 = createMotionEvent(MotionEvent.ACTION_MOVE, 14, SOURCE_TOUCHSCREEN) - val move4 = createMotionEvent(MotionEvent.ACTION_MOVE, 15, SOURCE_TOUCHSCREEN) - var eventId = assigner.processEvent(down) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move1) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move2) - // Even though we already had 2 move events, there was no choreographer callback yet. - // Therefore, we should still get the id of the down event - assertEquals(down.id, eventId) + fun testTouchDownAndMove() { + testDownAndMove(SOURCE_TOUCHSCREEN) + } - // Now send CALLBACK_INPUT to the assigner. It should provide the latest motion event - assigner.notifyFrameProcessed() - eventId = assigner.processEvent(move3) - assertEquals(move3.id, eventId) - eventId = assigner.processEvent(move4) - assertEquals(move4.id, eventId) + @Test + fun testMouseDownAndMove() { + testDownAndMove(SOURCE_MOUSE) + } + + @Test + fun testStylusDownAndMove() { + testDownAndMove(SOURCE_STYLUS) + } + + @Test + fun testTouchpadDownAndMove() { + testDownAndMove(SOURCE_TOUCHPAD) } /** - * Similar to the above test, but with SOURCE_MOUSE. Since we don't have down latency - * concept for non-touchscreens, the latest input event will be used. + * After an up event, motion events should be assigned their own event id */ @Test - fun testMouseDownWithMove() { - val assigner = InputEventAssigner() - val down = createMotionEvent(MotionEvent.ACTION_DOWN, 10, SOURCE_MOUSE) - val move1 = createMotionEvent(MotionEvent.ACTION_MOVE, 12, SOURCE_MOUSE) - var eventId = assigner.processEvent(down) - assertEquals(down.id, eventId) - eventId = assigner.processEvent(move1) - assertEquals(move1.id, eventId) + fun testMouseDownUpAndScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_UP, SOURCE_MOUSE, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3) + ) + } + + /** + * After an up event, motion events should be assigned their own event id + */ + @Test + fun testStylusDownUpAndHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_UP, SOURCE_STYLUS, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3) + ) + } + + /** + * After a cancel event, motion events should be assigned their own event id + */ + @Test + fun testMouseDownCancelAndScroll() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_MOUSE, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_MOUSE, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_SCROLL, SOURCE_MOUSE, id = 3, expectedId = 3) + ) + } + + /** + * After a cancel event, motion events should be assigned their own event id + */ + @Test + fun testStylusDownCancelAndHover() { + checkEventStream( + MotionEventData(MotionEvent.ACTION_DOWN, SOURCE_STYLUS, id = 1, expectedId = 1), + MotionEventData(MotionEvent.ACTION_CANCEL, SOURCE_STYLUS, id = 2, expectedId = 2), + MotionEventData(MotionEvent.ACTION_HOVER_ENTER, SOURCE_STYLUS, id = 3, expectedId = 3) + ) } /** diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java index e60d8efdbfa4..a2c3572eca9b 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java @@ -37,14 +37,14 @@ import android.content.res.Configuration; import android.os.SystemClock; import android.platform.test.annotations.RootPermissionTest; import android.platform.test.rule.UnlockScreenRule; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.Until; import android.view.WindowManager; import android.widget.EditText; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; import org.junit.Rule; import org.junit.Test; diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java index 2ac25f2696d3..b994bfb00007 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java @@ -33,10 +33,10 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.content.Intent; import android.platform.test.annotations.RootPermissionTest; import android.platform.test.rule.UnlockScreenRule; -import android.support.test.uiautomator.UiDevice; import android.widget.EditText; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; import org.junit.Rule; import org.junit.Test; diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java index 5368025ff898..2128cbf90542 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java @@ -48,12 +48,12 @@ import android.os.Build; import android.os.SystemClock; import android.platform.test.annotations.RootPermissionTest; import android.platform.test.rule.UnlockScreenRule; -import android.support.test.uiautomator.UiDevice; import android.util.Log; import android.view.WindowManager; import android.widget.EditText; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; import org.junit.Rule; import org.junit.Test; diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java index c7463218b646..1249a4564e8e 100644 --- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java +++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java @@ -18,10 +18,10 @@ package com.android.inputmethod.stresstest; import android.app.Instrumentation; import android.os.RemoteException; -import android.support.test.uiautomator.UiDevice; import androidx.annotation.NonNull; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; import org.junit.rules.TestWatcher; import org.junit.runner.Description; diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp index 827ff4fbd989..ad98e47fa8f0 100644 --- a/tests/Internal/Android.bp +++ b/tests/Internal/Android.bp @@ -24,6 +24,7 @@ android_test { "flickerlib-parsers", "perfetto_trace_java_protos", "flickerlib-trace_processor_shell", + "ravenwood-junit", ], java_resource_dirs: ["res"], certificate: "platform", @@ -39,6 +40,7 @@ android_ravenwood_test { "platform-test-annotations", ], srcs: [ + "src/com/android/internal/graphics/ColorUtilsTest.java", "src/com/android/internal/util/ParcellingTests.java", ], auto_gen_config: true, diff --git a/tests/Internal/AndroidTest.xml b/tests/Internal/AndroidTest.xml index 7b67e9ebcced..2d6c650eb2dc 100644 --- a/tests/Internal/AndroidTest.xml +++ b/tests/Internal/AndroidTest.xml @@ -26,4 +26,12 @@ <option name="package" value="com.android.internal.tests" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> </test> + + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.internal.tests/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> </configuration>
\ No newline at end of file diff --git a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java index d0bb8e3745bc..38a22f2fc2f3 100644 --- a/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java +++ b/tests/Internal/src/com/android/internal/graphics/ColorUtilsTest.java @@ -19,14 +19,19 @@ package com.android.internal.graphics; import static org.junit.Assert.assertTrue; import android.graphics.Color; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import org.junit.Rule; import org.junit.Test; @SmallTest public class ColorUtilsTest { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + @Test public void calculateMinimumBackgroundAlpha_satisfiestContrast() { diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java index 5a48327e7576..9657225588b7 100644 --- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java @@ -214,6 +214,13 @@ public class LegacyProtoLogImplTest { verify(mReader, never()).getViewerString(anyLong()); } + @Test + public void loadViewerConfigOnLogcatGroupRegistration() { + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + mProtoLog.registerGroups(TestProtoLogGroup.TEST_GROUP); + verify(mReader).loadViewerConfig(any(), any()); + } + private static class ProtoLogData { Long mMessageHash = null; Long mElapsedTime = null; diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java index b6672a0e2f4b..4b745b289d33 100644 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static java.io.File.createTempFile; -import static java.nio.file.Files.createTempDirectory; import android.content.Context; import android.os.SystemClock; @@ -45,6 +44,7 @@ import android.tracing.perfetto.DataSource; import android.util.proto.ProtoInputStream; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.protolog.common.IProtoLogGroup; import com.android.internal.protolog.common.LogDataType; @@ -67,7 +67,6 @@ import java.io.File; import java.io.IOException; import java.util.List; import java.util.Random; -import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; /** @@ -78,7 +77,8 @@ import java.util.concurrent.atomic.AtomicInteger; @Presubmit @RunWith(JUnit4.class) public class PerfettoProtoLogImplTest { - private final File mTracingDirectory = createTempDirectory("temp").toFile(); + private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation() + .getTargetContext().getFilesDir(); private final ResultWriter mWriter = new ResultWriter() .forScenario(new ScenarioBuilder() @@ -126,30 +126,35 @@ public class PerfettoProtoLogImplTest { .setMessage("My Test Debug Log Message %b") .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) .setGroupId(1) + .setLocation("com/test/MyTestClass.java:123") ).addMessages( Protolog.ProtoLogViewerConfig.MessageData.newBuilder() .setMessageId(2) .setMessage("My Test Verbose Log Message %b") .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) .setGroupId(1) + .setLocation("com/test/MyTestClass.java:342") ).addMessages( Protolog.ProtoLogViewerConfig.MessageData.newBuilder() .setMessageId(3) .setMessage("My Test Warn Log Message %b") .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN) .setGroupId(1) + .setLocation("com/test/MyTestClass.java:563") ).addMessages( Protolog.ProtoLogViewerConfig.MessageData.newBuilder() .setMessageId(4) .setMessage("My Test Error Log Message %b") .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR) .setGroupId(1) + .setLocation("com/test/MyTestClass.java:156") ).addMessages( Protolog.ProtoLogViewerConfig.MessageData.newBuilder() .setMessageId(5) .setMessage("My Test WTF Log Message %b") .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF) .setGroupId(1) + .setLocation("com/test/MyTestClass.java:192") ); ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock( @@ -379,7 +384,7 @@ public class PerfettoProtoLogImplTest { new Object[]{5}); verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("UNKNOWN MESSAGE#1234 (5)")); + LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)")); verify(mReader).getViewerString(eq(1234L)); } @@ -446,8 +451,8 @@ public class PerfettoProtoLogImplTest { before = SystemClock.elapsedRealtimeNanos(); mProtoLog.log( LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, - "My test message :: %s, %d, %o, %x, %f, %b", - "test", 1, 2, 3, 0.4, true); + "My test message :: %s, %d, %x, %f, %b", + "test", 1, 3, 0.4, true); after = SystemClock.elapsedRealtimeNanos(); } finally { traceMonitor.stop(mWriter); @@ -462,7 +467,27 @@ public class PerfettoProtoLogImplTest { Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) .isAtMost(after); Truth.assertThat(protolog.messages.getFirst().getMessage()) - .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, true"); + .isEqualTo("My test message :: test, 2, 6, 0.400000, true"); + } + + @Test + public void supportsLocationInformation() throws IOException { + PerfettoTraceMonitor traceMonitor = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true).build(); + try { + traceMonitor.start(); + mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.get(0).getLocation()) + .isEqualTo("com/test/MyTestClass.java:123"); } private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) { @@ -698,7 +723,7 @@ public class PerfettoProtoLogImplTest { traceMonitor.start(); mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, - "My test null string: %s", null); + "My test null string: %s", (Object) null); } finally { traceMonitor.stop(mWriter); } diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java new file mode 100644 index 000000000000..e3ec62d5b5a6 --- /dev/null +++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java @@ -0,0 +1,207 @@ +/* + * 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.internal.protolog; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.times; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class ProtoLogCommandHandlerTest { + + @Mock + ProtoLogService mProtoLogService; + @Mock + PrintWriter mPrintWriter; + + @Test + public void printsHelpForAllAvailableCommands() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.onHelp(); + validateOnHelpPrinted(); + } + + @Test + public void printsHelpIfCommandIsNull() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.onCommand(null); + validateOnHelpPrinted(); + } + + @Test + public void handlesGroupListCommand() { + Mockito.when(mProtoLogService.getGroups()) + .thenReturn(new String[] {"MY_TEST_GROUP", "MY_OTHER_GROUP"}); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "groups", "list" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_TEST_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_OTHER_GROUP")); + } + + @Test + public void handlesIncompleteGroupsCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "groups" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesGroupStatusCommand() { + Mockito.when(mProtoLogService.getGroups()).thenReturn(new String[] {"MY_GROUP"}); + Mockito.when(mProtoLogService.isLoggingToLogcat("MY_GROUP")).thenReturn(true); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "groups", "status", "MY_GROUP" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("LOG_TO_LOGCAT = true")); + } + + @Test + public void handlesGroupStatusCommandOfUnregisteredGroups() { + Mockito.when(mProtoLogService.getGroups()).thenReturn(new String[] {}); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "groups", "status", "MY_GROUP" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("UNREGISTERED")); + } + + @Test + public void handlesGroupStatusCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "groups", "status" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesIncompleteLogcatCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesLogcatEnableCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "enable", "MY_GROUP" }); + Mockito.verify(mProtoLogService).enableProtoLogToLogcat("MY_GROUP"); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "enable", "MY_GROUP", "MY_OTHER_GROUP" }); + Mockito.verify(mProtoLogService) + .enableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + } + + @Test + public void handlesLogcatDisableCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "disable", "MY_GROUP" }); + Mockito.verify(mProtoLogService).disableProtoLogToLogcat("MY_GROUP"); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "disable", "MY_GROUP", "MY_OTHER_GROUP" }); + Mockito.verify(mProtoLogService) + .disableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + } + + @Test + public void handlesLogcatEnableCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "enable" }); + Mockito.verify(mPrintWriter).println(contains("Incomplete command")); + } + + @Test + public void handlesLogcatDisableCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogService, mPrintWriter); + + cmdHandler.exec(mProtoLogService, FileDescriptor.in, FileDescriptor.out, FileDescriptor.err, + new String[] { "logcat", "disable" }); + Mockito.verify(mPrintWriter).println(contains("Incomplete command")); + } + + private void validateOnHelpPrinted() { + Mockito.verify(mPrintWriter, times(1)).println(endsWith("help")); + Mockito.verify(mPrintWriter, times(1)) + .println(endsWith("groups (list | status)")); + Mockito.verify(mPrintWriter, times(1)) + .println(endsWith("logcat (enable | disable) <group>")); + Mockito.verify(mPrintWriter, atLeast(0)).println(anyString()); + } +} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java new file mode 100644 index 000000000000..feac59c702ea --- /dev/null +++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogServiceTest.java @@ -0,0 +1,283 @@ +/* + * 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.internal.protolog; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; + +import static java.io.File.createTempFile; +import static java.nio.file.Files.createTempDirectory; + +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.tools.ScenarioBuilder; +import android.tools.Tag; +import android.tools.io.ResultArtifactDescriptor; +import android.tools.io.TraceType; +import android.tools.traces.TraceConfig; +import android.tools.traces.TraceConfigs; +import android.tools.traces.io.ResultReader; +import android.tools.traces.io.ResultWriter; +import android.tools.traces.monitors.PerfettoTraceMonitor; + +import com.google.common.truth.Truth; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import perfetto.protos.Protolog.ProtoLogViewerConfig; +import perfetto.protos.ProtologCommon; +import perfetto.protos.TraceOuterClass.Trace; +import perfetto.protos.TracePacketOuterClass.TracePacket; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class ProtoLogServiceTest { + + private static final String TEST_GROUP = "MY_TEST_GROUP"; + private static final String OTHER_TEST_GROUP = "MY_OTHER_TEST_GROUP"; + + private static final ProtoLogViewerConfig VIEWER_CONFIG = + ProtoLogViewerConfig.newBuilder() + .addGroups( + ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TEST_GROUP) + .setTag(TEST_GROUP) + ).addMessages( + ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(1) + .setMessage("My Test Debug Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + ).addMessages( + ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(2) + .setMessage("My Test Verbose Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) + .setGroupId(1) + ).build(); + + @Mock + IProtoLogClient mMockClient; + + @Mock + IProtoLogClient mSecondMockClient; + + @Mock + IBinder mMockClientBinder; + + @Mock + IBinder mSecondMockClientBinder; + + private final File mTracingDirectory = createTempDirectory("temp").toFile(); + + private final ResultWriter mWriter = new ResultWriter() + .forScenario(new ScenarioBuilder() + .forClass(createTempFile("temp", "").getName()).build()) + .withOutputDir(mTracingDirectory) + .setRunComplete(); + + private final TraceConfigs mTraceConfig = new TraceConfigs( + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false) + ); + + @Captor + ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientArgumentCaptor; + + @Captor + ArgumentCaptor<IBinder.DeathRecipient> mSecondDeathRecipientArgumentCaptor; + + private File mViewerConfigFile; + + public ProtoLogServiceTest() throws IOException { + } + + @Before + public void setUp() { + Mockito.when(mMockClient.asBinder()).thenReturn(mMockClientBinder); + Mockito.when(mSecondMockClient.asBinder()).thenReturn(mSecondMockClientBinder); + + try { + mViewerConfigFile = File.createTempFile("viewer-config", ".pb"); + try (var fos = new FileOutputStream(mViewerConfigFile); + BufferedOutputStream bos = new BufferedOutputStream(fos)) { + + bos.write(VIEWER_CONFIG.toByteArray()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void canRegisterClientWithGroupsOnly() throws RemoteException { + final ProtoLogService service = new ProtoLogService(); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + Truth.assertThat(service.getGroups()).asList().containsExactly(TEST_GROUP); + } + + @Test + public void willDumpViewerConfigOnlyOnceOnTraceStop() + throws RemoteException, InvalidProtocolBufferException { + final ProtoLogService service = new ProtoLogService(); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true)) + .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); + service.registerClient(mMockClient, args); + service.registerClient(mSecondMockClient, args); + + PerfettoTraceMonitor traceMonitor = + PerfettoTraceMonitor.newBuilder().enableProtoLog().build(); + + traceMonitor.start(); + traceMonitor.stop(mWriter); + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final byte[] traceData = reader.getArtifact() + .readBytes(new ResultArtifactDescriptor(TraceType.PERFETTO, Tag.ALL)); + + final Trace trace = Trace.parseFrom(traceData); + + final List<TracePacket> configPackets = trace.getPacketList().stream() + .filter(it -> it.hasProtologViewerConfig()) + // Exclude viewer configs from regular system tracing + .filter(it -> + it.getProtologViewerConfig().getGroups(0).getName().equals(TEST_GROUP)) + .toList(); + Truth.assertThat(configPackets).hasSize(1); + Truth.assertThat(configPackets.get(0).getProtologViewerConfig().toString()) + .isEqualTo(VIEWER_CONFIG.toString()); + } + + @Test + public void willDumpViewerConfigOnLastClientDisconnected() + throws RemoteException, FileNotFoundException { + final ProtoLogService.ViewerConfigFileTracer tracer = + Mockito.mock(ProtoLogService.ViewerConfigFileTracer.class); + final ProtoLogService service = new ProtoLogService(tracer); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig( + TEST_GROUP, true)) + .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); + service.registerClient(mMockClient, args); + service.registerClient(mSecondMockClient, args); + + Mockito.verify(mMockClientBinder) + .linkToDeath(mDeathRecipientArgumentCaptor.capture(), anyInt()); + Mockito.verify(mSecondMockClientBinder) + .linkToDeath(mSecondDeathRecipientArgumentCaptor.capture(), anyInt()); + + mDeathRecipientArgumentCaptor.getValue().binderDied(); + Mockito.verify(tracer, never()).trace(any(), any()); + mSecondDeathRecipientArgumentCaptor.getValue().binderDied(); + Mockito.verify(tracer).trace(any(), eq(mViewerConfigFile.getAbsolutePath())); + } + + @Test + public void sendEnableLoggingToLogcatToClient() throws RemoteException { + final var service = new ProtoLogService(); + + final var args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + service.enableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + + Mockito.verify(mMockClient).toggleLogcat(eq(true), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } + + @Test + public void sendDisableLoggingToLogcatToClient() throws RemoteException { + final ProtoLogService service = new ProtoLogService(); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, true)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + service.disableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + + Mockito.verify(mMockClient).toggleLogcat(eq(false), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } + + @Test + public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException { + final ProtoLogService service = new ProtoLogService(); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + service.enableProtoLogToLogcat(OTHER_TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + + Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any()); + } + + @Test + public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException { + final ProtoLogService service = new ProtoLogService(); + + Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP); + service.enableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + + final ProtoLogService.RegisterClientArgs args = new ProtoLogService.RegisterClientArgs() + .setGroups(new ProtoLogService.RegisterClientArgs.GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Mockito.verify(mMockClient).toggleLogcat(eq(true), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } +} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java index be9fb1b309f6..9a062e3b2f80 100644 --- a/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java +++ b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java @@ -67,7 +67,7 @@ public class ProtologDataSourceTest { @Test public void allEnabledTraceMode() { - final ProtoLogDataSource ds = new ProtoLogDataSource((c) -> {}, () -> {}, (c) -> {}); + final ProtoLogDataSource ds = new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {}); final ProtoLogDataSource.TlsState tlsState = createTlsState( DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( @@ -154,7 +154,7 @@ public class ProtologDataSourceTest { private ProtoLogDataSource.TlsState createTlsState( DataSourceConfigOuterClass.DataSourceConfig config) { final ProtoLogDataSource ds = - Mockito.spy(new ProtoLogDataSource((c) -> {}, () -> {}, (c) -> {})); + Mockito.spy(new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {})); ProtoInputStream configStream = new ProtoInputStream(config.toByteArray()); final ProtoLogDataSource.Instance dsInstance = Mockito.spy( diff --git a/tests/Internal/src/com/android/internal/util/ParcellingTests.java b/tests/Internal/src/com/android/internal/util/ParcellingTests.java index 65a3436a4c5e..fb63422cdf9f 100644 --- a/tests/Internal/src/com/android/internal/util/ParcellingTests.java +++ b/tests/Internal/src/com/android/internal/util/ParcellingTests.java @@ -18,6 +18,7 @@ package com.android.internal.util; import android.os.Parcel; import android.platform.test.annotations.Presubmit; +import android.platform.test.ravenwood.RavenwoodRule; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -26,6 +27,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.util.Parcelling.BuiltIn.ForInstant; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -38,6 +40,9 @@ import java.time.Instant; @RunWith(JUnit4.class) public class ParcellingTests { + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule(); + private Parcel mParcel = Parcel.obtain(); @Test diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java index 3722fefb12ad..c0e90f9232d6 100644 --- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java +++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; @@ -77,6 +78,7 @@ import org.mockito.stubbing.Answer; import java.io.File; import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -413,6 +415,311 @@ public class CrashRecoveryTest { verify(rescuePartyObserver, never()).executeBootLoopMitigation(2); } + @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testCrashLoopWithRescuePartyAndRollbackObserver() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); + RollbackPackageHealthObserver rollbackObserver = + setUpRollbackPackageHealthObserver(watchdog); + VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE); + + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> { + ApplicationInfo info = new ApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + return info; + }); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: SCOPED_DEVICE_CONFIG_RESET + verify(rescuePartyObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: ALL_DEVICE_CONFIG_RESET + verify(rescuePartyObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: WARM_REBOOT + verify(rescuePartyObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Low impact rollback + verify(rollbackObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + + // update available rollbacks to mock rollbacks being applied after the call to + // rollbackObserver.execute + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL)); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + } + + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testCrashLoopWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset() + throws Exception { + PackageWatchdog watchdog = createWatchdog(); + RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); + RollbackPackageHealthObserver rollbackObserver = + setUpRollbackPackageHealthObserver(watchdog); + VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE); + + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> { + ApplicationInfo info = new ApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + return info; + }); + + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: WARM_REBOOT + verify(rescuePartyObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Low impact rollback + verify(rollbackObserver).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + // update available rollbacks to mock rollbacks being applied after the call to + // rollbackObserver.execute + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL)); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied + verify(rescuePartyObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageA, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + } + + @Test + @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserver() throws Exception { + PackageWatchdog watchdog = createWatchdog(); + RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); + RollbackPackageHealthObserver rollbackObserver = + setUpRollbackPackageHealthObserver(watchdog); + String systemUi = "com.android.systemui"; + VersionedPackage versionedPackageUi = new VersionedPackage( + systemUi, VERSION_CODE); + RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW, + ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi)); + + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> { + ApplicationInfo info = new ApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + return info; + }); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: SCOPED_DEVICE_CONFIG_RESET + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: ALL_DEVICE_CONFIG_RESET + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: WARM_REBOOT + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Low impact rollback + verify(rollbackObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + // update available rollbacks to mock rollbacks being applied after the call to + // rollbackObserver.execute + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL)); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: RESET_SETTINGS_UNTRUSTED_DEFAULTS + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 4); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 5); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: RESET_SETTINGS_UNTRUSTED_CHANGES + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 5); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 6); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: RESET_SETTINGS_TRUSTED_DEFAULTS + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 6); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 7); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops. + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 7); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 8); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + } + + @Test + @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS) + public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset() + throws Exception { + PackageWatchdog watchdog = createWatchdog(); + RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog); + RollbackPackageHealthObserver rollbackObserver = + setUpRollbackPackageHealthObserver(watchdog); + String systemUi = "com.android.systemui"; + VersionedPackage versionedPackageUi = new VersionedPackage( + systemUi, VERSION_CODE); + RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW, + ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi)); + + when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> { + ApplicationInfo info = new ApplicationInfo(); + info.flags |= ApplicationInfo.FLAG_PERSISTENT + | ApplicationInfo.FLAG_SYSTEM; + return info; + }); + + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: WARM_REBOOT + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Low impact rollback + verify(rollbackObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + + // update available rollbacks to mock rollbacks being applied after the call to + // rollbackObserver.execute + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL)); + + raiseFatalFailureAndDispatch(watchdog, + Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH); + + // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops. + verify(rescuePartyObserver).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + verify(rescuePartyObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 3); + verify(rollbackObserver, never()).execute(versionedPackageUi, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 2); + } + RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) { RollbackPackageHealthObserver rollbackObserver = spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager)); @@ -424,7 +731,6 @@ public class CrashRecoveryTest { watchdog.registerHealthObserver(rollbackObserver); return rollbackObserver; } - RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) { setCrashRecoveryPropRescueBootCount(0); RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext)); @@ -686,4 +992,20 @@ public class CrashRecoveryTest { mTestLooper.moveTimeForward(milliSeconds); mTestLooper.dispatchAll(); } + + private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog, + List<VersionedPackage> packages, int failureReason) { + long triggerFailureCount = watchdog.getTriggerFailureCount(); + if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK + || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + triggerFailureCount = 1; + } + for (int i = 0; i < triggerFailureCount; i++) { + watchdog.onPackageFailure(packages, failureReason); + } + mTestLooper.dispatchAll(); + if (Flags.recoverabilityDetection()) { + moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS); + } + } } diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index a8b383cd4274..ab406ef4632e 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -23,9 +23,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -33,6 +36,7 @@ import static org.mockito.Mockito.when; import android.Manifest; import android.content.Context; +import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; @@ -116,6 +120,7 @@ public class PackageWatchdogTest { private ConnectivityModuleConnector mConnectivityModuleConnector; @Mock private PackageManager mMockPackageManager; + @Mock Intent mMockIntent; // Mock only sysprop apis private PackageWatchdog.BootThreshold mSpyBootThreshold; @Captor @@ -1669,6 +1674,19 @@ public class PackageWatchdogTest { PackageWatchdog.DEFAULT_TRIGGER_FAILURE_DURATION_MS); } + /** + * Tests device config changes are propagated correctly. + */ + @Test + public void testRegisterShutdownBroadcastReceiver() { + PackageWatchdog watchdog = createWatchdog(); + doReturn(mMockIntent).when(mSpyContext) + .registerReceiverForAllUsers(any(), any(), any(), any()); + + watchdog.registerShutdownBroadcastReceiver(); + verify(mSpyContext).registerReceiverForAllUsers(any(), any(), eq(null), eq(null)); + } + private void adoptShellPermissions(String... permissions) { InstrumentationRegistry .getInstrumentation() diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java index 359eb35384c7..5012c235a2a5 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java @@ -84,6 +84,7 @@ public class SurfaceControlViewHostSyncTest extends Activity implements SurfaceH content.addView(enableSyncButton, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM)); + content.setFitsSystemWindows(true); setContentView(content); mSv.setZOrderOnTop(false); diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java index 73e01634709e..4119ea2c73c3 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java @@ -37,6 +37,7 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde protected void onCreate(Bundle savedInstanceState) { FrameLayout content = new FrameLayout(this); + content.setFitsSystemWindows(true); super.onCreate(savedInstanceState); mView = new SurfaceView(this); content.addView(mView, new FrameLayout.LayoutParams( diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java index ac7dc9e2f31f..528706860b31 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceInputTestActivity.java @@ -88,6 +88,7 @@ public class SurfaceInputTestActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); LinearLayout content = new LinearLayout(this); + content.setFitsSystemWindows(true); mLocalSurfaceView = new SurfaceView(this); content.addView(mLocalSurfaceView, new LinearLayout.LayoutParams( 500, 500, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); diff --git a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java index 09236ffebdf4..459db8a0a1ac 100644 --- a/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/BitmapUploadActivity.java @@ -74,6 +74,9 @@ public class BitmapUploadActivity extends AppCompatActivity { } } + private ObjectAnimator mColorValueAnimator; + private ObjectAnimator mYAnimator; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -81,16 +84,28 @@ public class BitmapUploadActivity extends AppCompatActivity { // animate color to force bitmap uploads UploadView uploadView = findViewById(R.id.upload_view); - ObjectAnimator colorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255); - colorValueAnimator.setRepeatMode(ValueAnimator.REVERSE); - colorValueAnimator.setRepeatCount(ValueAnimator.INFINITE); - colorValueAnimator.start(); + mColorValueAnimator = ObjectAnimator.ofInt(uploadView, "colorValue", 0, 255); + mColorValueAnimator.setRepeatMode(ValueAnimator.REVERSE); + mColorValueAnimator.setRepeatCount(ValueAnimator.INFINITE); + mColorValueAnimator.start(); // animate scene root to guarantee there's a minimum amount of GPU rendering work View uploadRoot = findViewById(R.id.upload_root); - ObjectAnimator yAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100); - yAnimator.setRepeatMode(ValueAnimator.REVERSE); - yAnimator.setRepeatCount(ValueAnimator.INFINITE); - yAnimator.start(); + mYAnimator = ObjectAnimator.ofFloat(uploadRoot, "translationY", 0, 100); + mYAnimator.setRepeatMode(ValueAnimator.REVERSE); + mYAnimator.setRepeatCount(ValueAnimator.INFINITE); + mYAnimator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mColorValueAnimator != null) { + mColorValueAnimator.cancel(); + } + + if (mYAnimator != null) { + mYAnimator.cancel(); + } } } diff --git a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java index 882163bd6b0e..9d10f76198c3 100644 --- a/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/FullscreenOverdrawActivity.java @@ -66,18 +66,29 @@ public class FullscreenOverdrawActivity extends AppCompatActivity { return PixelFormat.OPAQUE; } } + + private ObjectAnimator mObjectAnimator; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); OverdrawDrawable overdraw = new OverdrawDrawable(); getWindow().setBackgroundDrawable(overdraw); - setContentView(new View(this)); - ObjectAnimator objectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255); - objectAnimator.setRepeatMode(ValueAnimator.REVERSE); - objectAnimator.setRepeatCount(ValueAnimator.INFINITE); - objectAnimator.start(); + mObjectAnimator = ObjectAnimator.ofInt(overdraw, "colorValue", 0, 255); + mObjectAnimator.setRepeatMode(ValueAnimator.REVERSE); + mObjectAnimator.setRepeatCount(ValueAnimator.INFINITE); + + mObjectAnimator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mObjectAnimator != null) { + mObjectAnimator.cancel(); + } } } diff --git a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java index b26a660981da..1b28dc29d6aa 100644 --- a/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/GlTextureViewActivity.java @@ -33,6 +33,7 @@ import com.android.test.uibench.opengl.ImageFlipRenderThread; public class GlTextureViewActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener { private ImageFlipRenderThread mRenderThread; private TextureView mTextureView; + private ObjectAnimator mAnimator; @Override protected void onCreate(Bundle savedInstanceState) { @@ -54,17 +55,17 @@ public class GlTextureViewActivity extends AppCompatActivity implements TextureV int distance = Math.max(mTextureView.getWidth(), mTextureView.getHeight()); mTextureView.setCameraDistance(distance * metrics.density); - ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f); - animator.setRepeatMode(ObjectAnimator.REVERSE); - animator.setRepeatCount(ObjectAnimator.INFINITE); - animator.setDuration(4000); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + mAnimator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f); + mAnimator.setRepeatMode(ObjectAnimator.REVERSE); + mAnimator.setRepeatCount(ObjectAnimator.INFINITE); + mAnimator.setDuration(4000); + mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mTextureView.invalidate(); } }); - animator.start(); + mAnimator.start(); } @Override @@ -86,4 +87,11 @@ public class GlTextureViewActivity extends AppCompatActivity implements TextureV public void onSurfaceTextureUpdated(SurfaceTexture surface) { } + @Override + protected void onPause() { + super.onPause(); + if (mAnimator != null) { + mAnimator.cancel(); + } + } }
\ No newline at end of file diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java index 76ed1ae4e445..f1e96c80c85a 100644 --- a/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/InvalidateActivity.java @@ -51,6 +51,7 @@ public class InvalidateActivity extends AppCompatActivity { } private ColorView[][] mColorViews; + private ObjectAnimator mAnimator; @SuppressWarnings("unused") public void setColorValue(int colorValue) { @@ -80,9 +81,17 @@ public class InvalidateActivity extends AppCompatActivity { } } - ObjectAnimator animator = ObjectAnimator.ofInt(this, "colorValue", 0, 255); - animator.setRepeatMode(ValueAnimator.REVERSE); - animator.setRepeatCount(ValueAnimator.INFINITE); - animator.start(); + mAnimator = ObjectAnimator.ofInt(this, "colorValue", 0, 255); + mAnimator.setRepeatMode(ValueAnimator.REVERSE); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAnimator != null) { + mAnimator.cancel(); + } } } diff --git a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java index 804ced14d522..95635720d4f9 100644 --- a/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java +++ b/tests/UiBench/src/com/android/test/uibench/InvalidateTreeActivity.java @@ -33,6 +33,7 @@ public class InvalidateTreeActivity extends AppCompatActivity { private final ArrayList<LinearLayout> mLayouts = new ArrayList<>(); private int mColorToggle = 0; + private ObjectAnimator mAnimator; private void createQuadTree(LinearLayout parent, int remainingDepth) { mLayouts.add(parent); @@ -71,9 +72,17 @@ public class InvalidateTreeActivity extends AppCompatActivity { createQuadTree(root, 8); setContentView(root); - ObjectAnimator animator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000); - animator.setRepeatMode(ValueAnimator.REVERSE); - animator.setRepeatCount(ValueAnimator.INFINITE); - animator.start(); + mAnimator = ObjectAnimator.ofInt(this, "ignoredValue", 0, 1000); + mAnimator.setRepeatMode(ValueAnimator.REVERSE); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.start(); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAnimator != null) { + mAnimator.cancel(); + } } } diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp index 4c531b8f9ee0..a4085e5315a4 100644 --- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp +++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp @@ -23,6 +23,7 @@ android_test { resource_dirs: ["res"], libs: ["android.test.runner"], static_libs: [ + "androidx.core_core", "androidx.test.ext.junit", "androidx.test.rules", "compatibility-device-util-axt", |