summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml25
-rw-r--r--tests/ApkVerityTest/TEST_MAPPING12
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java576
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java140
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestApp.dmbin237348 -> 0 bytes
-rw-r--r--tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dmbin237025 -> 0 bytes
-rw-r--r--tests/ApkVerityTest/testdata/README.md13
-rw-r--r--tests/BinaryTransparencyHostTest/Android.bp2
-rw-r--r--tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java10
-rw-r--r--tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java4
-rw-r--r--tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java185
-rw-r--r--tests/Codegen/src/com/android/codegentest/SampleDataClass.java151
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/OWNERS6
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/README.md17
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/Android.bp50
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml51
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/proguard.flags24
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt107
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt183
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt155
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt109
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt66
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/host/Android.bp50
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml42
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py64
-rw-r--r--tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py29
-rw-r--r--tests/CtsSurfaceControlTestsStaging/Android.bp49
-rw-r--r--tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml (renamed from tests/FlickerTests/manifests/AndroidManifestNotification.xml)19
-rw-r--r--tests/CtsSurfaceControlTestsStaging/AndroidTest.xml29
-rw-r--r--tests/CtsSurfaceControlTestsStaging/OWNERS1
-rw-r--r--tests/CtsSurfaceControlTestsStaging/README.md4
-rw-r--r--tests/CtsSurfaceControlTestsStaging/TEST_MAPPING7
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java775
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java134
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/layout/activity_surface_control.xml31
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--tests/CtsSurfaceControlTestsStaging/src/main/res/values/strings.xml (renamed from tests/FixVibrateSetting/res/values/strings.xml)15
-rw-r--r--tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl27
-rw-r--r--tests/EnforcePermission/perf-app/Android.bp45
-rw-r--r--tests/EnforcePermission/perf-app/AndroidManifest.xml37
-rw-r--r--tests/EnforcePermission/perf-app/AndroidTest.xml43
-rw-r--r--tests/EnforcePermission/perf-app/perfetto.textproto154
-rw-r--r--tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java169
-rw-r--r--tests/EnforcePermission/service-app/Android.bp8
-rw-r--r--tests/EnforcePermission/service-app/AndroidManifest.xml3
-rw-r--r--tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java17
-rw-r--r--tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java74
-rw-r--r--tests/EnforcePermission/test-app/AndroidManifest.xml13
-rw-r--r--tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java59
-rw-r--r--tests/FixVibrateSetting/Android.bp15
-rw-r--r--tests/FixVibrateSetting/AndroidManifest.xml18
-rw-r--r--tests/FixVibrateSetting/OWNERS1
-rw-r--r--tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.pngbin1057 -> 0 bytes
-rw-r--r--tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.pngbin651 -> 0 bytes
-rw-r--r--tests/FixVibrateSetting/res/layout/fix_vibrate.xml46
-rw-r--r--tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java129
-rw-r--r--tests/FlickerTests/ActivityEmbedding/Android.bp34
-rw-r--r--tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml (renamed from tests/FlickerTests/manifests/AndroidManifest.xml)15
-rw-r--r--tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml (renamed from tests/FlickerTests/manifests/AndroidManifestOther.xml)14
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt)8
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt)0
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt)58
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt)5
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt)10
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt)0
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt)1
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt)143
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt237
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt)2
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt82
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt)0
-rw-r--r--tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt)123
-rw-r--r--tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto (renamed from tests/FlickerTests/trace_config/trace_config.textproto)0
-rw-r--r--tests/FlickerTests/Android.bp197
-rw-r--r--tests/FlickerTests/AppClose/Android.bp33
-rw-r--r--tests/FlickerTests/AppClose/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/AppClose/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/AppClose/OWNERS (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS)0
-rw-r--r--tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/AppClose/res/xml/network_security_config.xml (renamed from tests/FlickerTests/manifests/AndroidManifestIme.xml)14
-rw-r--r--tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt)8
-rw-r--r--tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt)8
-rw-r--r--tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt)4
-rw-r--r--tests/FlickerTests/AppClose/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/AppLaunch/Android.bp69
-rw-r--r--tests/FlickerTests/AppLaunch/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/AppLaunch/OWNERS (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS)0
-rw-r--r--tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml (renamed from tests/FlickerTests/manifests/AndroidManifestAppClose.xml)14
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt)4
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt)34
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt)7
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt)7
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt)9
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt)11
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt)10
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt)3
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt209
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt)1
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt)17
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt47
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt)6
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt)8
-rw-r--r--tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt)2
-rw-r--r--tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/FlickerService/Android.bp33
-rw-r--r--tests/FlickerTests/FlickerService/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/FlickerService/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/FlickerService/res/xml/network_security_config.xml (renamed from tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml)14
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt)3
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt)0
-rw-r--r--tests/FlickerTests/FlickerService/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/IME/Android.bp78
-rw-r--r--tests/FlickerTests/IME/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/IME/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/IME/OWNERS (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS)0
-rw-r--r--tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/IME/res/xml/network_security_config.xml22
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt)5
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt)6
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt)0
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt)81
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt)5
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt)4
-rw-r--r--tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt)0
-rw-r--r--tests/FlickerTests/IME/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/Notification/Android.bp33
-rw-r--r--tests/FlickerTests/Notification/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/Notification/AndroidTestTemplate.xml (renamed from tests/FlickerTests/AndroidTestTemplate.xml)23
-rw-r--r--tests/FlickerTests/Notification/OWNERS2
-rw-r--r--tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/Notification/res/xml/network_security_config.xml22
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt (renamed from tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java)11
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt)34
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt)34
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt)23
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt)20
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt)94
-rw-r--r--tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt)18
-rw-r--r--tests/FlickerTests/Notification/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/QuickSwitch/Android.bp33
-rw-r--r--tests/FlickerTests/QuickSwitch/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/QuickSwitch/OWNERS (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS)0
-rw-r--r--tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml22
-rw-r--r--tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt)6
-rw-r--r--tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt)66
-rw-r--r--tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt)6
-rw-r--r--tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/Rotation/Android.bp33
-rw-r--r--tests/FlickerTests/Rotation/AndroidManifest.xml67
-rw-r--r--tests/FlickerTests/Rotation/AndroidTestTemplate.xml90
-rw-r--r--tests/FlickerTests/Rotation/OWNERS (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS)0
-rw-r--r--tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/Rotation/res/xml/network_security_config.xml22
-rw-r--r--tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt)6
-rw-r--r--tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt)0
-rw-r--r--tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt)12
-rw-r--r--tests/FlickerTests/Rotation/trace_config/trace_config.textproto77
-rw-r--r--tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml24
-rw-r--r--tests/FlickerTests/manifests/AndroidManifestRotation.xml24
-rw-r--r--tests/FlickerTests/manifests/AndroidManifestService.xml24
-rw-r--r--tests/FlickerTests/res/xml/network_security_config.xml22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt58
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt189
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt39
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt32
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt47
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt30
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt53
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt43
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt51
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt45
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt46
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt45
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt43
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt51
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/Android.bp42
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt)102
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt)2
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt)4
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt)16
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt)2
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt)6
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt)2
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt)2
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt)39
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt)0
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt32
-rw-r--r--tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt)2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/Android.bp3
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml27
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/avd_anim.xml94
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml7
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java12
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java (renamed from tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java)10
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransferSplashscreenActivity.java52
-rw-r--r--tests/FsVerityTest/Android.bp (renamed from tests/ApkVerityTest/Android.bp)14
-rw-r--r--tests/FsVerityTest/AndroidTest.xml (renamed from tests/ApkVerityTest/AndroidTest.xml)22
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/Android.bp (renamed from tests/ApkVerityTest/ApkVerityTestApp/Android.bp)17
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/AndroidManifest.xml (renamed from tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml)6
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java108
-rw-r--r--tests/FsVerityTest/OWNERS (renamed from tests/ApkVerityTest/OWNERS)0
-rw-r--r--tests/FsVerityTest/TEST_MAPPING7
-rw-r--r--tests/FsVerityTest/block_device_writer/Android.bp (renamed from tests/ApkVerityTest/block_device_writer/Android.bp)0
-rw-r--r--tests/FsVerityTest/block_device_writer/block_device_writer.cpp (renamed from tests/ApkVerityTest/block_device_writer/block_device_writer.cpp)0
-rw-r--r--tests/FsVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java (renamed from tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java)0
-rw-r--r--tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java103
-rw-r--r--tests/FsVerityTest/testdata/Android.bp (renamed from tests/ApkVerityTest/testdata/Android.bp)48
-rw-r--r--tests/FsVerityTest/testdata/ApkVerityTestCert.der (renamed from tests/ApkVerityTest/testdata/ApkVerityTestCert.der)bin1330 -> 1330 bytes
-rw-r--r--tests/FsVerityTest/testdata/ApkVerityTestCert.pem (renamed from tests/ApkVerityTest/testdata/ApkVerityTestCert.pem)0
-rw-r--r--tests/FsVerityTest/testdata/ApkVerityTestKey.pem (renamed from tests/ApkVerityTest/testdata/ApkVerityTestKey.pem)0
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java166
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/res/drawable/robot_repeated.xml18
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java119
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java10
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java38
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java22
-rw-r--r--tests/Input/Android.bp20
-rw-r--r--tests/Input/AndroidManifest.xml7
-rw-r--r--tests/Input/res/raw/dummy_keyboard_layout.kcm311
-rw-r--r--tests/Input/res/raw/input_port_associations.xml4
-rw-r--r--tests/Input/res/raw/input_port_associations_bad_displayport.xml3
-rw-r--r--tests/Input/res/raw/input_port_associations_bad_xml.xml3
-rw-r--r--tests/Input/res/xml/keyboard_layouts.xml89
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt246
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java290
-rw-r--r--tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java246
-rw-r--r--tests/Input/src/android/hardware/input/InputManagerTest.kt153
-rw-r--r--tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt176
-rw-r--r--tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt69
-rw-r--r--tests/Input/src/android/hardware/input/VirtualKeyEventTest.java60
-rw-r--r--tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java59
-rw-r--r--tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java37
-rw-r--r--tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java54
-rw-r--r--tests/Input/src/android/hardware/input/VirtualTouchEventTest.java178
-rw-r--r--tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt355
-rw-r--r--tests/Input/src/com/android/server/input/BatteryControllerTests.kt898
-rw-r--r--tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java91
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt404
-rw-r--r--tests/Input/src/com/android/server/input/InputShellCommandTest.java167
-rw-r--r--tests/Input/src/com/android/server/input/KeyRemapperTests.kt193
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt849
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt1062
-rw-r--r--tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt218
-rw-r--r--tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java111
-rw-r--r--tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java61
-rw-r--r--tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java85
-rw-r--r--tests/Input/src/com/android/test/input/AnrTest.kt14
-rw-r--r--tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt6
-rw-r--r--tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt6
-rw-r--r--tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt12
-rw-r--r--tests/InputMethodStressTest/TEST_MAPPING2
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java2
-rw-r--r--tests/InputScreenshotTest/Android.bp76
-rw-r--r--tests/InputScreenshotTest/AndroidManifest.xml (renamed from tests/notification/AndroidManifest.xml)19
-rw-r--r--tests/InputScreenshotTest/AndroidTest.xml36
-rw-r--r--tests/InputScreenshotTest/OWNERS2
-rw-r--r--tests/InputScreenshotTest/TEST_MAPPING7
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.pngbin0 -> 77207 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.pngbin0 -> 70708 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.pngbin0 -> 73441 bytes
-rw-r--r--tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.pngbin0 -> 71619 bytes
-rw-r--r--tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.pngbin0 -> 45707 bytes
-rw-r--r--tests/InputScreenshotTest/robotests/Android.bp71
-rw-r--r--tests/InputScreenshotTest/robotests/AndroidManifest.xml21
-rw-r--r--tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.pngbin0 -> 70150 bytes
-rw-r--r--tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.pngbin0 -> 66888 bytes
-rw-r--r--tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.pngbin0 -> 39003 bytes
-rw-r--r--tests/InputScreenshotTest/robotests/config/robolectric.properties15
-rw-r--r--tests/InputScreenshotTest/robotests/robo-manifest.xml15
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt66
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt75
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt44
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt90
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt70
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt59
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt70
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt39
-rw-r--r--tests/InputScreenshotTest/src/android/input/screenshot/package-info.java4
-rw-r--r--tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java4
-rw-r--r--tests/MotionPrediction/Android.bp3
-rw-r--r--tests/MultiDeviceInput/Android.bp33
-rw-r--r--tests/MultiDeviceInput/AndroidManifest.xml34
-rw-r--r--tests/MultiDeviceInput/OWNERS1
-rw-r--r--tests/MultiDeviceInput/README.md19
-rw-r--r--tests/MultiDeviceInput/res/layout/activity_main.xml27
-rw-r--r--tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 10486 bytes
-rw-r--r--tests/MultiDeviceInput/res/values-w820dp/dimens.xml20
-rw-r--r--tests/MultiDeviceInput/res/values/colors.xml20
-rw-r--r--tests/MultiDeviceInput/res/values/dimens.xml19
-rw-r--r--tests/MultiDeviceInput/res/values/strings.xml17
-rw-r--r--tests/MultiDeviceInput/res/values/styles.xml23
-rw-r--r--tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt183
-rw-r--r--tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt83
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt8
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt40
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt149
-rw-r--r--tests/SurfaceViewBufferTests/Android.bp1
-rw-r--r--tests/SurfaceViewBufferTests/AndroidManifest.xml7
-rw-r--r--tests/SurfaceViewBufferTests/res/xml/network_security_config.xml22
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt2
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java100
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt3
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt2
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt26
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt2
-rw-r--r--tests/TaskOrganizerTest/Android.bp1
-rw-r--r--tests/TaskOrganizerTest/AndroidManifest.xml4
-rw-r--r--tests/TaskOrganizerTest/res/xml/network_security_config.xml21
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt12
-rw-r--r--tests/UiBench/Android.bp5
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java6
-rw-r--r--tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java2
-rw-r--r--tests/UsbTests/Android.bp9
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java42
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java7
-rw-r--r--tests/componentalias/Android.bp1
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp36
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml23
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml43
-rw-r--r--tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java49
-rw-r--r--tests/inputmethod/OWNERS3
-rw-r--r--tests/notification/Android.bp16
-rw-r--r--tests/notification/OWNERS1
-rw-r--r--tests/notification/res/drawable-nodpi/arubin_hed.jpegbin7568 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-nodpi/bucket.pngbin50391 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-nodpi/matias_hed.jpgbin15835 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-nodpi/page_hed.jpgbin9186 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-nodpi/romainguy_hed.jpgbin19692 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-nodpi/romainguy_rockaway.jpgbin414841 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/add.pngbin211 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/ic_dial_action_call.pngbin3629 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/ic_end_call.pngbin2774 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/ic_media_next.pngbin1364 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/ic_menu_upload.pngbin1142 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/icon.pngbin485 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_alarm.pngbin1750 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_calendar.pngbin823 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_email.pngbin1055 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_missed_call.pngbin1355 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_sms.pngbin1207 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_snooze.pngbin2298 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.pngbin2302 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_notify_talk_text.pngbin1282 -> 0 bytes
-rw-r--r--tests/notification/res/drawable-xhdpi/stat_sys_phone_call.pngbin1164 -> 0 bytes
-rw-r--r--tests/notification/res/layout/full_screen.xml13
-rw-r--r--tests/notification/res/layout/main.xml11
-rw-r--r--tests/notification/res/values/dimens.xml23
-rw-r--r--tests/notification/res/values/strings.xml10
-rw-r--r--tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java494
-rw-r--r--tests/permission/OWNERS1
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java10
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java4
-rw-r--r--tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java24
-rw-r--r--tests/utils/testutils/TEST_MAPPING29
-rw-r--r--tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java81
-rw-r--r--tests/utils/testutils/java/com/android/server/accessibility/TEST_MAPPING7
-rw-r--r--tests/utils/testutils/tests/Android.bp47
-rw-r--r--tests/utils/testutils/tests/AndroidManifest.xml (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt)26
-rw-r--r--tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java (renamed from tests/utils/testutils/java/android/os/test/TestLooperTest.java)0
-rw-r--r--tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java119
-rw-r--r--tests/utils/testutils/tests/src/com/android/test/filters/SelectTestTests.java (renamed from tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java)0
458 files changed, 15922 insertions, 4161 deletions
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml b/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
deleted file mode 100644
index 3f1a4f3a26a1..000000000000
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.apkverity"
- android:isFeatureSplit="true"
- split="feature_x">
- <application>
- <activity android:name=".feature_x.DummyActivity"/>
- </application>
-</manifest>
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
deleted file mode 100644
index 72d96148c69f..000000000000
--- a/tests/ApkVerityTest/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "ApkVerityTest"
- },
- // nextgen test only runs during postsubmit.
- {
- "name": "ApkVerityTest",
- "keywords": ["nextgen"]
- }
- ]
-}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
deleted file mode 100644
index 591ffeb39721..000000000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * Copyright (C) 2019 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.apkverity;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.platform.test.annotations.RootPermissionTest;
-
-import com.android.blockdevicewriter.BlockDeviceWriter;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This test makes sure app installs with fs-verity signature, and on-access verification works.
- *
- * <p>When an app is installed, all or none of the files should have their corresponding .fsv_sig
- * signature file. Otherwise, install will fail.
- *
- * <p>Once installed, file protected by fs-verity is verified by kernel every time a block is loaded
- * from disk to memory. The file is immutable by design, enforced by filesystem.
- *
- * <p>In order to make sure a block of the file is readable only if the underlying block on disk
- * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
- * address against the block device.
- *
- * <p>Requirements to run this test:
- * <ul>
- * <li>Device is rootable</li>
- * <li>The filesystem supports fs-verity</li>
- * <li>The feature flag is enabled</li>
- * </ul>
- */
-@RootPermissionTest
-@RunWith(DeviceJUnit4ClassRunner.class)
-public class ApkVerityTest extends BaseHostJUnit4Test {
- private static final String TARGET_PACKAGE = "com.android.apkverity";
-
- private static final String BASE_APK = "ApkVerityTestApp.apk";
- private static final String BASE_APK_DM = "ApkVerityTestApp.dm";
- private static final String SPLIT_APK = "ApkVerityTestAppSplit.apk";
- private static final String SPLIT_APK_DM = "ApkVerityTestAppSplit.dm";
-
- private static final String INSTALLED_BASE_APK = "base.apk";
- private static final String INSTALLED_BASE_DM = "base.dm";
- private static final String INSTALLED_SPLIT_APK = "split_feature_x.apk";
- private static final String INSTALLED_SPLIT_DM = "split_feature_x.dm";
- private static final String INSTALLED_BASE_APK_FSV_SIG = "base.apk.fsv_sig";
- private static final String INSTALLED_BASE_DM_FSV_SIG = "base.dm.fsv_sig";
- private static final String INSTALLED_SPLIT_APK_FSV_SIG = "split_feature_x.apk.fsv_sig";
- private static final String INSTALLED_SPLIT_DM_FSV_SIG = "split_feature_x.dm.fsv_sig";
-
- private static final String DAMAGING_EXECUTABLE = "/data/local/tmp/block_device_writer";
- private static final String CERT_PATH = "/data/local/tmp/ApkVerityTestCert.der";
-
- /** Only 4K page is supported by fs-verity currently. */
- private static final int FSVERITY_PAGE_SIZE = 4096;
-
- private ITestDevice mDevice;
- private boolean mDmRequireFsVerity;
-
- @Before
- public void setUp() throws DeviceNotAvailableException {
- mDevice = getDevice();
- mDmRequireFsVerity = "true".equals(
- mDevice.getProperty("pm.dexopt.dm.require_fsverity"));
-
- expectRemoteCommandToSucceed("cmd file_integrity append-cert " + CERT_PATH);
- uninstallPackage(TARGET_PACKAGE);
- }
-
- @After
- public void tearDown() throws DeviceNotAvailableException {
- expectRemoteCommandToSucceed("cmd file_integrity remove-last-cert");
- uninstallPackage(TARGET_PACKAGE);
- }
-
- @Test
- public void testFsverityKernelSupports() throws DeviceNotAvailableException {
- ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
- expectRemoteCommandToSucceed("test -f /sys/fs/" + mountPoint.type + "/features/verity");
- }
-
- @Test
- public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK);
- }
-
- @Test
- public void testInstallBaseWithWrongSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFile(SPLIT_APK_DM + ".fsv_sig",
- BASE_APK + ".fsv_sig")
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithSplit()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(SPLIT_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK);
- }
-
- @Test
- public void testInstallBaseWithDm() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM);
- }
-
- @Test
- public void testInstallEverything() throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFileAndSignature(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM);
- }
-
- @Test
- public void testInstallSplitOnly()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFileAndSignature(SPLIT_APK)
- .run();
-
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK);
- }
-
- @Test
- public void testInstallSplitOnlyMissingSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFile(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallSplitOnlyWithoutBaseSignature()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(INSTALLED_BASE_APK);
-
- new InstallMultiple()
- .inheritFrom(TARGET_PACKAGE)
- .addFileAndSignature(SPLIT_APK)
- .run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_APK_FSV_SIG);
- }
-
- @Test
- public void testInstallOnlyDmHasFsvSig()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_DM);
- }
-
- @Test
- public void testInstallDmWithoutFsvSig_Base()
- throws DeviceNotAvailableException, FileNotFoundException {
- InstallMultiple installer = new InstallMultiple()
- .addFile(BASE_APK)
- .addFile(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM);
- if (mDmRequireFsVerity) {
- installer.runExpectingFailure();
- } else {
- installer.run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM,
- INSTALLED_SPLIT_DM_FSV_SIG);
- verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM);
- }
- }
-
- @Test
- public void testInstallDmWithoutFsvSig_Split()
- throws DeviceNotAvailableException, FileNotFoundException {
- InstallMultiple installer = new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFile(SPLIT_APK_DM);
- if (mDmRequireFsVerity) {
- installer.runExpectingFailure();
- } else {
- installer.run();
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_DM,
- INSTALLED_BASE_DM_FSV_SIG,
- INSTALLED_SPLIT_APK,
- INSTALLED_SPLIT_DM);
- verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM);
- }
- }
-
- @Test
- public void testInstallSomeApkIsMissingFsvSig_Base()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFile(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallSomeApkIsMissingFsvSig_Split()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .addFileAndSignature(BASE_APK_DM)
- .addFileAndSignature(SPLIT_APK)
- .addFileAndSignature(SPLIT_APK_DM)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithFsvSigThenSplitWithout()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFileAndSignature(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(
- INSTALLED_BASE_APK,
- INSTALLED_BASE_APK_FSV_SIG);
-
- new InstallMultiple()
- .addFile(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testInstallBaseWithoutFsvSigThenSplitWith()
- throws DeviceNotAvailableException, FileNotFoundException {
- new InstallMultiple()
- .addFile(BASE_APK)
- .run();
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- verifyInstalledFiles(INSTALLED_BASE_APK);
-
- new InstallMultiple()
- .addFileAndSignature(SPLIT_APK)
- .runExpectingFailure();
- }
-
- @Test
- public void testFsverityFileIsImmutableAndReadable() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- assertNotNull(getDevice().getAppPackageInfo(TARGET_PACKAGE));
- expectRemoteCommandToFail("echo -n '' >> " + apkPath);
- expectRemoteCommandToSucceed("cat " + apkPath + " > /dev/null");
- }
-
- @Test
- public void testFsverityFailToReadModifiedBlockAtFront() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- long apkSize = getFileSizeInBytes(apkPath);
- long offsetFirstByte = 0;
-
- // The first two pages should be both readable at first.
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
- if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
- offsetFirstByte + FSVERITY_PAGE_SIZE));
- }
-
- // Damage the file directly against the block device.
- damageFileAgainstBlockDevice(apkPath, offsetFirstByte);
-
- // Expect actual read from disk to fail but only at damaged page.
- BlockDeviceWriter.dropCaches(mDevice);
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
- if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
- long lastByteOfTheSamePage =
- offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
- }
- }
-
- @Test
- public void testFsverityFailToReadModifiedBlockAtBack() throws DeviceNotAvailableException {
- new InstallMultiple().addFileAndSignature(BASE_APK).run();
- String apkPath = getApkPath(TARGET_PACKAGE);
-
- long apkSize = getFileSizeInBytes(apkPath);
- long offsetOfLastByte = apkSize - 1;
-
- // The first two pages should be both readable at first.
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
- if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
- offsetOfLastByte - FSVERITY_PAGE_SIZE));
- }
-
- // Damage the file directly against the block device.
- damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);
-
- // Expect actual read from disk to fail but only at damaged page.
- BlockDeviceWriter.dropCaches(mDevice);
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
- if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
- long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
- assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
- assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
- }
- }
-
- private void verifyInstalledFilesHaveFsverity(String... filenames)
- throws DeviceNotAvailableException {
- // Verify that all files are protected by fs-verity
- String apkPath = getApkPath(TARGET_PACKAGE);
- String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
- long kTargetOffset = 0;
- for (String basename : filenames) {
- String path = appDir + "/" + basename;
- damageFileAgainstBlockDevice(path, kTargetOffset);
-
- // Retry is sometimes needed to pass the test. Package manager may have FD leaks
- // (see b/122744005 as example) that prevents the file in question to be evicted
- // from filesystem cache. Forcing GC workarounds the problem.
- int retry = 5;
- for (; retry > 0; retry--) {
- BlockDeviceWriter.dropCaches(mDevice);
- if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
- break;
- }
- try {
- String openFiles = expectRemoteCommandToSucceed("lsof " + apkPath);
- CLog.d("lsof: " + openFiles);
- Thread.sleep(1000);
- forceGCOnOpenFilesProcess(getOpenFilesPIDs(openFiles));
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- assertTrue("Read from " + path + " should fail", retry > 0);
- }
- }
-
- /**
- * This is a helper method that parses the lsof output to get PIDs of process holding FD.
- * Here is an example output of lsof. This method extracts the second columns(PID).
- *
- * Example lsof output:
- * COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
- * .example.app 1063 u0_a38 mem REG 253,6 8599 12826 example.apk
- * .example.app 1063 u0_a38 99r REG 253,6 8599 12826 example.apk
- */
- private Set<String> getOpenFilesPIDs(String lsof) {
- Set<String> openFilesPIDs = new HashSet<>();
- String[] lines = lsof.split("\n");
- for (int i = 1; i < lines.length; i++) {
- openFilesPIDs.add(lines[i].split("\\s+")[1]);
- }
- return openFilesPIDs;
- }
-
- /**
- * This is a helper method that forces GC on processes given their PIDs.
- * That is to execute shell command "kill -10" on PIDs.
- */
- private void forceGCOnOpenFilesProcess(Set<String> openFilesPIDs)
- throws DeviceNotAvailableException {
- for (String openFilePID : openFilesPIDs) {
- mDevice.executeShellV2Command("kill -10 " + openFilePID);
- }
- }
-
- private void verifyInstalledFiles(String... filenames) throws DeviceNotAvailableException {
- String apkPath = getApkPath(TARGET_PACKAGE);
- String appDir = apkPath.substring(0, apkPath.lastIndexOf("/"));
- // Exclude directories since we only care about files.
- HashSet<String> actualFiles = new HashSet<>(Arrays.asList(
- expectRemoteCommandToSucceed("ls -p " + appDir + " | grep -v '/'").split("\n")));
-
- HashSet<String> expectedFiles = new HashSet<>(Arrays.asList(filenames));
- assertEquals(expectedFiles, actualFiles);
- }
-
- private void damageFileAgainstBlockDevice(String path, long offsetOfTargetingByte)
- throws DeviceNotAvailableException {
- assertTrue(path.startsWith("/data/"));
- ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
- ArrayList<String> args = new ArrayList<>();
- args.add(DAMAGING_EXECUTABLE);
- if ("f2fs".equals(mountPoint.type)) {
- args.add("--use-f2fs-pinning");
- }
- args.add(mountPoint.filesystem);
- args.add(path);
- args.add(Long.toString(offsetOfTargetingByte));
- expectRemoteCommandToSucceed(String.join(" ", args));
- }
-
- private String getApkPath(String packageName) throws DeviceNotAvailableException {
- String line = expectRemoteCommandToSucceed("pm path " + packageName + " | grep base.apk");
- int index = line.trim().indexOf(":");
- assertTrue(index >= 0);
- return line.substring(index + 1);
- }
-
- private long getFileSizeInBytes(String packageName) throws DeviceNotAvailableException {
- return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
- }
-
- private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(cmd);
- assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
- result.getStatus());
- return result.getStdout();
- }
-
- private void expectRemoteCommandToFail(String cmd) throws DeviceNotAvailableException {
- CommandResult result = mDevice.executeShellV2Command(cmd);
- assertTrue("Unexpected success from `" + cmd + "`: " + result.getStderr(),
- result.getStatus() != CommandStatus.SUCCESS);
- }
-
- private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
- InstallMultiple() {
- super(getDevice(), getBuild());
- }
-
- InstallMultiple addFileAndSignature(String filename) {
- try {
- addFile(filename);
- addFile(filename + ".fsv_sig");
- } catch (FileNotFoundException e) {
- fail("Missing test file: " + e);
- }
- return this;
- }
- }
-}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java b/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
deleted file mode 100644
index 02e73d157dde..000000000000
--- a/tests/ApkVerityTest/src/com/android/apkverity/BaseInstallMultiple.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2019 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.apkverity;
-
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Base class for invoking the install-multiple command via ADB. Subclass this for less typing:
- *
- * <code> private class InstallMultiple extends BaseInstallMultiple&lt;InstallMultiple&gt; { public
- * InstallMultiple() { super(getDevice(), null); } } </code>
- */
-/*package*/ class BaseInstallMultiple<T extends BaseInstallMultiple<?>> {
-
- private final ITestDevice mDevice;
- private final IBuildInfo mBuild;
-
- private final List<String> mArgs = new ArrayList<>();
- private final Map<File, String> mFileToRemoteMap = new HashMap<>();
-
- /*package*/ BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo) {
- mDevice = device;
- mBuild = buildInfo;
- addArg("-g");
- }
-
- T addArg(String arg) {
- mArgs.add(arg);
- return (T) this;
- }
-
- T addFile(String filename) throws FileNotFoundException {
- return addFile(filename, filename);
- }
-
- T addFile(String filename, String remoteName) throws FileNotFoundException {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
- mFileToRemoteMap.put(buildHelper.getTestFile(filename), remoteName);
- return (T) this;
- }
-
- T inheritFrom(String packageName) {
- addArg("-r");
- addArg("-p " + packageName);
- return (T) this;
- }
-
- void run() throws DeviceNotAvailableException {
- run(true);
- }
-
- void runExpectingFailure() throws DeviceNotAvailableException {
- run(false);
- }
-
- private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
- final ITestDevice device = mDevice;
-
- // Create an install session
- final StringBuilder cmd = new StringBuilder();
- cmd.append("pm install-create");
- for (String arg : mArgs) {
- cmd.append(' ').append(arg);
- }
-
- String result = device.executeShellCommand(cmd.toString());
- TestCase.assertTrue(result, result.startsWith("Success"));
-
- final int start = result.lastIndexOf("[");
- final int end = result.lastIndexOf("]");
- int sessionId = -1;
- try {
- if (start != -1 && end != -1 && start < end) {
- sessionId = Integer.parseInt(result.substring(start + 1, end));
- }
- } catch (NumberFormatException e) {
- throw new IllegalStateException("Failed to parse install session: " + result);
- }
- if (sessionId == -1) {
- throw new IllegalStateException("Failed to create install session: " + result);
- }
-
- // Push our files into session. Ideally we'd use stdin streaming,
- // but ddmlib doesn't support it yet.
- for (final Map.Entry<File, String> entry : mFileToRemoteMap.entrySet()) {
- final File file = entry.getKey();
- final String remoteName = entry.getValue();
- final String remotePath = "/data/local/tmp/" + file.getName();
- if (!device.pushFile(file, remotePath)) {
- throw new IllegalStateException("Failed to push " + file);
- }
-
- cmd.setLength(0);
- cmd.append("pm install-write");
- cmd.append(' ').append(sessionId);
- cmd.append(' ').append(remoteName);
- cmd.append(' ').append(remotePath);
-
- result = device.executeShellCommand(cmd.toString());
- TestCase.assertTrue(result, result.startsWith("Success"));
- }
-
- // Everything staged; let's pull trigger
- cmd.setLength(0);
- cmd.append("pm install-commit");
- cmd.append(' ').append(sessionId);
-
- result = device.executeShellCommand(cmd.toString());
- if (expectingSuccess) {
- TestCase.assertTrue(result, result.contains("Success"));
- } else {
- TestCase.assertFalse(result, result.contains("Success"));
- }
- }
-}
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm b/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
deleted file mode 100644
index e53a86131366..000000000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestApp.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm b/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
deleted file mode 100644
index 75396f1ba730..000000000000
--- a/tests/ApkVerityTest/testdata/ApkVerityTestAppSplit.dm
+++ /dev/null
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/README.md b/tests/ApkVerityTest/testdata/README.md
deleted file mode 100644
index 163cb183a5ad..000000000000
--- a/tests/ApkVerityTest/testdata/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-This test only runs on rooted / debuggable device.
-
-The test tries to install subsets of base.{apk,dm}, split.{apk,dm} and their
-corresponding .fsv_sig files (generated by build rule). If installed, the
-tests also tries to tamper with the file at absolute disk offset to verify
-if fs-verity is effective.
-
-How to generate dex metadata (.dm)
-==================================
-
- adb shell profman --generate-test-profile=/data/local/tmp/primary.prof
- adb pull /data/local/tmp/primary.prof
- zip foo.dm primary.prof
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index 0dbc022589fc..38cb9869f165 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -35,6 +35,8 @@ java_test_host {
data: [
":BinaryTransparencyTestApp",
":EasterEgg",
+ ":FeatureSplitBase",
+ ":FeatureSplit1",
":com.android.apex.cts.shim.v2_rebootless_prebuilt",
],
test_suites: [
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index 346622f0f467..6e5f08a11ed8 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -91,20 +91,20 @@ public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test {
public void testCollectAllSilentInstalledMbaInfo() throws Exception {
try {
new InstallMultiple()
- .addFile("ApkVerityTestApp.apk")
- .addFile("ApkVerityTestAppSplit.apk")
+ .addFile("FeatureSplitBase.apk")
+ .addFile("FeatureSplit1.apk")
.run();
updatePreloadApp();
- assertNotNull(getDevice().getAppPackageInfo("com.android.apkverity"));
+ assertNotNull(getDevice().getAppPackageInfo("com.android.test.split.feature"));
assertNotNull(getDevice().getAppPackageInfo("com.android.egg"));
assertTrue(getDevice().setProperty("debug.transparency.bg-install-apps",
- "com.android.apkverity,com.android.egg"));
+ "com.android.test.split.feature,com.android.egg"));
runDeviceTest("testCollectAllSilentInstalledMbaInfo");
} finally {
// No need to wait until job complete, since we can't verifying very meaningfully.
cancelPendingJob();
- uninstallPackage("com.android.apkverity");
+ uninstallPackage("com.android.test.split.feature");
uninstallPackage("com.android.egg");
}
}
diff --git a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
index c087a85da2a8..2bc056ee743f 100644
--- a/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
+++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
@@ -121,7 +121,7 @@ public class BinaryTransparencyTest {
// Verify
assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side
- var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg");
+ var expectedAppNames = Set.of("com.android.test.split.feature", "com.android.egg");
var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName)
.collect(Collectors.toList());
assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames);
@@ -141,6 +141,6 @@ public class BinaryTransparencyTest {
}
}
}
- assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit
+ assertThat(actualSplitNames).containsExactly("feature1"); // Name of FeatureSplit1
}
}
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
index 48d050ce4391..5460e4e87e2f 100644
--- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -22,9 +22,9 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.Manifest;
-import android.app.compat.CompatChanges;
import android.hardware.display.DisplayManager;
import android.os.Looper;
import android.support.test.uiautomator.UiDevice;
@@ -34,6 +34,7 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
+import android.view.WindowManager;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
@@ -52,7 +53,7 @@ import java.util.concurrent.TimeUnit;
public class AttachedChoreographerTest {
private static final String TAG = "AttachedChoreographerTest";
private static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID = 170503758;
- private static final long THRESHOLD_MS = 10;
+ private static final long THRESHOLD_MS = 4;
private static final int FRAME_ITERATIONS = 21;
private static final int CALLBACK_MISSED_THRESHOLD = 2;
@@ -66,7 +67,7 @@ public class AttachedChoreographerTest {
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Choreographer mChoreographer;
- private boolean mIsFirstCallback = true;
+ private long mExpectedPresentTime;
private int mCallbackMissedCounter = 0;
@Before
@@ -74,6 +75,13 @@ public class AttachedChoreographerTest {
mScenario = ActivityScenario.launch(GraphicsActivity.class);
mScenario.moveToState(Lifecycle.State.CREATED);
mScenario.onActivity(activity -> {
+ // Lock the display frame rate. This will not allow SurfaceFlinger to use the frame rate
+ // override feature that throttles down the global choreographer for this test.
+ float refreshRate = activity.getDisplay().getMode().getRefreshRate();
+ WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
+ attrs.preferredRefreshRate = refreshRate;
+ activity.getWindow().setAttributes(attrs);
+
mSurfaceView = activity.findViewById(R.id.surface);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@@ -95,6 +103,12 @@ public class AttachedChoreographerTest {
});
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // TODO(b/290634611): clean this up once SF new front end is enabled by default
+ boolean sfNewFeEnabled = uiDevice.executeShellCommand("dumpsys SurfaceFlinger")
+ .indexOf("SurfaceFlinger New Frontend Enabled:true") != -1;
+ assumeTrue(sfNewFeEnabled);
+
uiDevice.wakeUp();
uiDevice.executeShellCommand("wm dismiss-keyguard");
mScenario.moveToState(Lifecycle.State.RESUMED);
@@ -112,18 +126,16 @@ public class AttachedChoreographerTest {
mDisplayManager.setRefreshRateSwitchingType(
DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
- boolean changeIsEnabled =
- CompatChanges.isChangeEnabled(
- DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID);
- Log.i(TAG, "DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGE_ID is "
- + (changeIsEnabled ? "enabled" : "disabled"));
});
}
@After
public void tearDown() {
- mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
- mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ if (mDisplayManager != null) {
+ mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ }
+
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
@@ -407,18 +419,141 @@ public class AttachedChoreographerTest {
}
}
+ @Test
+ public void testChoreographerAttachedAfterSetFrameRate() {
+ Log.i(TAG, "starting testChoreographerAttachedAfterSetFrameRate");
+
+ class TransactionGenerator implements SurfaceControl.TransactionCommittedListener {
+ private SurfaceControl mSc;
+ private int mFrame;
+ private static final int NUM_FRAMES = 50;
+ private final CountDownLatch mCompleteLatch = new CountDownLatch(1);
+
+ TransactionGenerator(SurfaceControl sc) {
+ mSc = sc;
+
+ }
+
+ @Override
+ public void onTransactionCommitted() {
+ mFrame++;
+ if (mFrame >= NUM_FRAMES) {
+ mCompleteLatch.countDown();
+ return;
+ }
+
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction.setAlpha(mSc, 1.0f / mFrame)
+ .addTransactionCommittedListener(Runnable::run, this)
+ .apply();
+
+ }
+
+ public void startAndWaitForCompletion() {
+ onTransactionCommitted();
+ if (waitForCountDown(mCompleteLatch, /* timeoutInSeconds */ 10L)) {
+ fail("failed to send new transactions");
+ }
+ }
+ }
+
+
+ for (int divisor : new int[]{2, 3}) {
+ CountDownLatch choreographerLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = displayRefreshRate / divisor;
+ long callbackDurationMs = Math.round(1000 / fps);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ .apply();
+
+
+ new TransactionGenerator(sc).startAndWaitForCompletion();
+
+ Choreographer choreographer = sc.getChoreographer();
+ verifyVsyncCallbacks(choreographer, callbackDurationMs,
+ choreographerLatch, FRAME_ITERATIONS);
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(choreographerLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+ }
+
+ @Test
+ public void testChoreographerNonDivisorFixedSourceRefreshRate() {
+ CountDownLatch continueLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ Choreographer choreographer = sc.getChoreographer();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = 61.7f; // hopefully not a divisor
+ long callbackDurationMs = Math.round(1000 / displayRefreshRate);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE)
+ .addTransactionCommittedListener(Runnable::run,
+ () -> verifyVsyncCallbacks(choreographer,
+ callbackDurationMs, continueLatch, FRAME_ITERATIONS))
+ .apply();
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+
+ @Test
+ public void testChoreographerNonDivisorRefreshRate() {
+ CountDownLatch continueLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ Choreographer choreographer = sc.getChoreographer();
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float fps = 61.7f; // hopefully not a divisor
+ float expectedFps = displayRefreshRate / Math.round(displayRefreshRate / fps);
+ long callbackDurationMs = Math.round(1000 / expectedFps);
+ mCallbackMissedCounter = 0;
+ transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .addTransactionCommittedListener(Runnable::run,
+ () -> verifyVsyncCallbacks(choreographer,
+ callbackDurationMs, continueLatch, FRAME_ITERATIONS))
+ .apply();
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) {
+ fail("Test not finished in 5 Seconds");
+ }
+ }
+
private void verifyVsyncCallbacks(Choreographer choreographer, long callbackDurationMs,
CountDownLatch continueLatch, int frameCount) {
- long callbackRequestedTimeNs = System.nanoTime();
choreographer.postVsyncCallback(frameData -> {
+ long expectedPresentTime =
+ frameData.getPreferredFrameTimeline().getExpectedPresentationTimeNanos();
if (frameCount > 0) {
- if (!mIsFirstCallback) {
- // Skip the first callback as it takes 1 frame
- // to reflect the new refresh rate
- long callbackDurationDiffMs = getCallbackDurationDiffInMs(
- frameData.getFrameTimeNanos(),
- callbackRequestedTimeNs, callbackDurationMs);
- if (callbackDurationDiffMs < 0 || callbackDurationDiffMs > THRESHOLD_MS) {
+ if (mExpectedPresentTime > 0) {
+ long callbackDurationDiffMs =
+ TimeUnit.NANOSECONDS.toMillis(
+ expectedPresentTime - mExpectedPresentTime);
+ if (callbackDurationDiffMs < 0
+ || Math.abs(callbackDurationMs - callbackDurationDiffMs)
+ > THRESHOLD_MS) {
mCallbackMissedCounter++;
Log.e(TAG, "Frame #" + Math.abs(frameCount - FRAME_ITERATIONS)
+ " vsync callback failed, expected callback in "
@@ -427,25 +562,19 @@ public class AttachedChoreographerTest {
+ " but actual duration difference is " + callbackDurationDiffMs);
}
}
- mIsFirstCallback = false;
+ mExpectedPresentTime = expectedPresentTime;
verifyVsyncCallbacks(choreographer, callbackDurationMs,
continueLatch, frameCount - 1);
} else {
- assertTrue("Missed timeline for " + mCallbackMissedCounter + " callbacks, while "
- + CALLBACK_MISSED_THRESHOLD + " missed callbacks are allowed",
+ assertTrue("Missed timeline for " + mCallbackMissedCounter
+ + " callbacks, while " + CALLBACK_MISSED_THRESHOLD
+ + " missed callbacks are allowed",
mCallbackMissedCounter <= CALLBACK_MISSED_THRESHOLD);
continueLatch.countDown();
}
});
}
- private long getCallbackDurationDiffInMs(long callbackTimeNs, long requestedTimeNs,
- long expectedCallbackMs) {
- long actualTimeMs = TimeUnit.NANOSECONDS.toMillis(callbackTimeNs)
- - TimeUnit.NANOSECONDS.toMillis(requestedTimeNs);
- return Math.abs(expectedCallbackMs - actualTimeMs);
- }
-
private boolean waitForCountDown(CountDownLatch countDownLatch, long timeoutInSeconds) {
try {
return !countDownLatch.await(timeoutInSeconds, TimeUnit.SECONDS);
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 158e0656b574..dc55dd240be0 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -237,6 +237,12 @@ public final class SampleDataClass implements Parcelable {
*/
private transient LinkAddress[] mLinkAddresses6;
/**
+ * For hidden lists, getters, setters and adders will be hidden.
+ * @hide
+ */
+ private @NonNull List<LinkAddress> mLinkAddresses7 = new ArrayList<>();
+
+ /**
* When using transient fields for caching it's often also a good idea to initialize them
* lazily.
*
@@ -486,6 +492,8 @@ public final class SampleDataClass implements Parcelable {
* Making a field public will suppress getter generation in favor of accessing it directly.
* @param linkAddresses5
* Final fields suppress generating a setter (when setters are requested).
+ * @param linkAddresses7
+ * For hidden lists, getters, setters and adders will be hidden.
* @param stringRes
* Fields with certain annotations are automatically validated in constructor
*
@@ -529,9 +537,10 @@ public final class SampleDataClass implements Parcelable {
@State int state,
@NonNull CharSequence charSeq,
@Nullable LinkAddress[] linkAddresses5,
+ @NonNull List<LinkAddress> linkAddresses7,
@StringRes int stringRes,
@android.annotation.IntRange(from = 0, to = 6) int dayOfWeek,
- @Size(2) @NonNull @FloatRange(from = 0f) float[] coords,
+ @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] coords,
@NonNull IBinder token,
@Nullable ICompanionDeviceManager iPCInterface) {
this.mNum = num;
@@ -595,6 +604,9 @@ public final class SampleDataClass implements Parcelable {
AnnotationValidations.validate(
NonNull.class, null, charSeq);
this.mLinkAddresses5 = linkAddresses5;
+ this.mLinkAddresses7 = linkAddresses7;
+ AnnotationValidations.validate(
+ NonNull.class, null, mLinkAddresses7);
this.mStringRes = stringRes;
AnnotationValidations.validate(
StringRes.class, null, mStringRes);
@@ -609,13 +621,11 @@ public final class SampleDataClass implements Parcelable {
"value", 2);
AnnotationValidations.validate(
NonNull.class, null, mCoords);
- int coordsSize = mCoords.length;
- for (int i = 0; i < coordsSize; i++) {
- AnnotationValidations.validate(
- FloatRange.class, null, mCoords[i],
- "from", 0f);
- }
-
+ AnnotationValidations.validate(
+ Each.class, null, mCoords);
+ AnnotationValidations.validate(
+ FloatRange.class, null, mCoords,
+ "from", 0f);
this.mToken = token;
AnnotationValidations.validate(
NonNull.class, null, mToken);
@@ -777,6 +787,16 @@ public final class SampleDataClass implements Parcelable {
}
/**
+ * For hidden lists, getters, setters and adders will be hidden.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<LinkAddress> getLinkAddresses7() {
+ return mLinkAddresses7;
+ }
+
+ /**
* Fields with certain annotations are automatically validated in constructor
*
* You can see overloads in {@link AnnotationValidations} for a list of currently
@@ -815,7 +835,7 @@ public final class SampleDataClass implements Parcelable {
* @see AnnotationValidations#validate(Class, Size, int, String, int)
*/
@DataClass.Generated.Member
- public @Size(2) @NonNull @FloatRange(from = 0f) float[] getCoords() {
+ public @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] getCoords() {
return mCoords;
}
@@ -1065,6 +1085,19 @@ public final class SampleDataClass implements Parcelable {
}
/**
+ * For hidden lists, getters, setters and adders will be hidden.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull SampleDataClass setLinkAddresses7(@NonNull List<LinkAddress> value) {
+ mLinkAddresses7 = value;
+ AnnotationValidations.validate(
+ NonNull.class, null, mLinkAddresses7);
+ return this;
+ }
+
+ /**
* Fields with certain annotations are automatically validated in constructor
*
* You can see overloads in {@link AnnotationValidations} for a list of currently
@@ -1111,20 +1144,18 @@ public final class SampleDataClass implements Parcelable {
* @see AnnotationValidations#validate(Class, Size, int, String, int)
*/
@DataClass.Generated.Member
- public @NonNull SampleDataClass setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) {
+ public @NonNull SampleDataClass setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) {
mCoords = value;
AnnotationValidations.validate(
Size.class, null, mCoords.length,
"value", 2);
AnnotationValidations.validate(
NonNull.class, null, mCoords);
- int coordsSize = mCoords.length;
- for (int i = 0; i < coordsSize; i++) {
- AnnotationValidations.validate(
- FloatRange.class, null, mCoords[i],
- "from", 0f);
- }
-
+ AnnotationValidations.validate(
+ Each.class, null, mCoords);
+ AnnotationValidations.validate(
+ FloatRange.class, null, mCoords,
+ "from", 0f);
return this;
}
@@ -1172,6 +1203,7 @@ public final class SampleDataClass implements Parcelable {
"state = " + stateToString(mState) + ", " +
"charSeq = " + charSeq + ", " +
"linkAddresses5 = " + java.util.Arrays.toString(mLinkAddresses5) + ", " +
+ "linkAddresses7 = " + mLinkAddresses7 + ", " +
"stringRes = " + mStringRes + ", " +
"dayOfWeek = " + mDayOfWeek + ", " +
"coords = " + java.util.Arrays.toString(mCoords) + ", " +
@@ -1210,6 +1242,7 @@ public final class SampleDataClass implements Parcelable {
&& mState == that.mState
&& Objects.equals(charSeq, that.charSeq)
&& java.util.Arrays.equals(mLinkAddresses5, that.mLinkAddresses5)
+ && Objects.equals(mLinkAddresses7, that.mLinkAddresses7)
&& mStringRes == that.mStringRes
&& mDayOfWeek == that.mDayOfWeek
&& java.util.Arrays.equals(mCoords, that.mCoords)
@@ -1241,6 +1274,7 @@ public final class SampleDataClass implements Parcelable {
_hash = 31 * _hash + mState;
_hash = 31 * _hash + Objects.hashCode(charSeq);
_hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses5);
+ _hash = 31 * _hash + Objects.hashCode(mLinkAddresses7);
_hash = 31 * _hash + mStringRes;
_hash = 31 * _hash + mDayOfWeek;
_hash = 31 * _hash + java.util.Arrays.hashCode(mCoords);
@@ -1270,6 +1304,7 @@ public final class SampleDataClass implements Parcelable {
actionInt.acceptInt(this, "state", mState);
actionObject.acceptObject(this, "charSeq", charSeq);
actionObject.acceptObject(this, "linkAddresses5", mLinkAddresses5);
+ actionObject.acceptObject(this, "linkAddresses7", mLinkAddresses7);
actionInt.acceptInt(this, "stringRes", mStringRes);
actionInt.acceptInt(this, "dayOfWeek", mDayOfWeek);
actionObject.acceptObject(this, "coords", mCoords);
@@ -1298,6 +1333,7 @@ public final class SampleDataClass implements Parcelable {
action.acceptObject(this, "state", mState);
action.acceptObject(this, "charSeq", charSeq);
action.acceptObject(this, "linkAddresses5", mLinkAddresses5);
+ action.acceptObject(this, "linkAddresses7", mLinkAddresses7);
action.acceptObject(this, "stringRes", mStringRes);
action.acceptObject(this, "dayOfWeek", mDayOfWeek);
action.acceptObject(this, "coords", mCoords);
@@ -1338,7 +1374,7 @@ public final class SampleDataClass implements Parcelable {
if (mOtherParcelable != null) flg |= 0x40;
if (mLinkAddresses4 != null) flg |= 0x800;
if (mLinkAddresses5 != null) flg |= 0x10000;
- if (mIPCInterface != null) flg |= 0x200000;
+ if (mIPCInterface != null) flg |= 0x400000;
dest.writeLong(flg);
dest.writeInt(mNum);
dest.writeInt(mNum2);
@@ -1357,6 +1393,7 @@ public final class SampleDataClass implements Parcelable {
dest.writeInt(mState);
dest.writeCharSequence(charSeq);
if (mLinkAddresses5 != null) dest.writeTypedArray(mLinkAddresses5, flags);
+ dest.writeParcelableList(mLinkAddresses7, flags);
dest.writeInt(mStringRes);
dest.writeInt(mDayOfWeek);
dest.writeFloatArray(mCoords);
@@ -1395,11 +1432,13 @@ public final class SampleDataClass implements Parcelable {
int state = in.readInt();
CharSequence _charSeq = (CharSequence) in.readCharSequence();
LinkAddress[] linkAddresses5 = (flg & 0x10000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR);
+ List<LinkAddress> linkAddresses7 = new ArrayList<>();
+ in.readParcelableList(linkAddresses7, LinkAddress.class.getClassLoader());
int stringRes = in.readInt();
int dayOfWeek = in.readInt();
float[] coords = in.createFloatArray();
IBinder token = (IBinder) in.readStrongBinder();
- ICompanionDeviceManager iPCInterface = (flg & 0x200000) == 0 ? null : ICompanionDeviceManager.Stub.asInterface(in.readStrongBinder());
+ ICompanionDeviceManager iPCInterface = (flg & 0x400000) == 0 ? null : ICompanionDeviceManager.Stub.asInterface(in.readStrongBinder());
this.mNum = num;
this.mNum2 = num2;
@@ -1462,6 +1501,9 @@ public final class SampleDataClass implements Parcelable {
AnnotationValidations.validate(
NonNull.class, null, charSeq);
this.mLinkAddresses5 = linkAddresses5;
+ this.mLinkAddresses7 = linkAddresses7;
+ AnnotationValidations.validate(
+ NonNull.class, null, mLinkAddresses7);
this.mStringRes = stringRes;
AnnotationValidations.validate(
StringRes.class, null, mStringRes);
@@ -1476,13 +1518,11 @@ public final class SampleDataClass implements Parcelable {
"value", 2);
AnnotationValidations.validate(
NonNull.class, null, mCoords);
- int coordsSize = mCoords.length;
- for (int i = 0; i < coordsSize; i++) {
- AnnotationValidations.validate(
- FloatRange.class, null, mCoords[i],
- "from", 0f);
- }
-
+ AnnotationValidations.validate(
+ Each.class, null, mCoords);
+ AnnotationValidations.validate(
+ FloatRange.class, null, mCoords,
+ "from", 0f);
this.mToken = token;
AnnotationValidations.validate(
NonNull.class, null, mToken);
@@ -1529,9 +1569,10 @@ public final class SampleDataClass implements Parcelable {
private @State int mState;
private @NonNull CharSequence charSeq;
private @Nullable LinkAddress[] mLinkAddresses5;
+ private @NonNull List<LinkAddress> mLinkAddresses7;
private @StringRes int mStringRes;
private @android.annotation.IntRange(from = 0, to = 6) int mDayOfWeek;
- private @Size(2) @NonNull @FloatRange(from = 0f) float[] mCoords;
+ private @Size(2) @NonNull @Each @FloatRange(from = 0f) float[] mCoords;
private @NonNull IBinder mToken;
private @Nullable ICompanionDeviceManager mIPCInterface;
@@ -1823,6 +1864,30 @@ public final class SampleDataClass implements Parcelable {
}
/**
+ * For hidden lists, getters, setters and adders will be hidden.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setLinkAddresses7(@NonNull List<LinkAddress> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20000;
+ mLinkAddresses7 = value;
+ return this;
+ }
+
+ /** @see #setLinkAddresses7 @hide */
+ @DataClass.Generated.Member
+ public @NonNull Builder addLinkAddresses7(@NonNull LinkAddress value) {
+ // You can refine this method's name by providing item's singular name, e.g.:
+ // @DataClass.PluralOf("item")) mItems = ...
+
+ if (mLinkAddresses7 == null) setLinkAddresses7(new ArrayList<>());
+ mLinkAddresses7.add(value);
+ return this;
+ }
+
+ /**
* Fields with certain annotations are automatically validated in constructor
*
* You can see overloads in {@link AnnotationValidations} for a list of currently
@@ -1837,7 +1902,7 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setStringRes(@StringRes int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x20000;
+ mBuilderFieldsSet |= 0x40000;
mStringRes = value;
return this;
}
@@ -1852,7 +1917,7 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setDayOfWeek(@android.annotation.IntRange(from = 0, to = 6) int value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x40000;
+ mBuilderFieldsSet |= 0x80000;
mDayOfWeek = value;
return this;
}
@@ -1867,9 +1932,9 @@ public final class SampleDataClass implements Parcelable {
* @see AnnotationValidations#validate(Class, Size, int, String, int)
*/
@DataClass.Generated.Member
- public @NonNull Builder setCoords(@Size(2) @NonNull @FloatRange(from = 0f) float... value) {
+ public @NonNull Builder setCoords(@Size(2) @NonNull @Each @FloatRange(from = 0f) float... value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x80000;
+ mBuilderFieldsSet |= 0x100000;
mCoords = value;
return this;
}
@@ -1880,7 +1945,7 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setToken(@NonNull IBinder value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x100000;
+ mBuilderFieldsSet |= 0x200000;
mToken = value;
return this;
}
@@ -1891,7 +1956,7 @@ public final class SampleDataClass implements Parcelable {
@DataClass.Generated.Member
public @NonNull Builder setIPCInterface(@NonNull ICompanionDeviceManager value) {
checkNotUsed();
- mBuilderFieldsSet |= 0x200000;
+ mBuilderFieldsSet |= 0x400000;
mIPCInterface = value;
return this;
}
@@ -1899,7 +1964,7 @@ public final class SampleDataClass implements Parcelable {
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull SampleDataClass build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x400000; // Mark builder used
+ mBuilderFieldsSet |= 0x800000; // Mark builder used
if ((mBuilderFieldsSet & 0x10) == 0) {
mName2 = "Bob";
@@ -1935,18 +2000,21 @@ public final class SampleDataClass implements Parcelable {
charSeq = "";
}
if ((mBuilderFieldsSet & 0x20000) == 0) {
- mStringRes = 0;
+ mLinkAddresses7 = new ArrayList<>();
}
if ((mBuilderFieldsSet & 0x40000) == 0) {
- mDayOfWeek = 3;
+ mStringRes = 0;
}
if ((mBuilderFieldsSet & 0x80000) == 0) {
- mCoords = new float[] { 0f, 0f };
+ mDayOfWeek = 3;
}
if ((mBuilderFieldsSet & 0x100000) == 0) {
- mToken = new Binder();
+ mCoords = new float[] { 0f, 0f };
}
if ((mBuilderFieldsSet & 0x200000) == 0) {
+ mToken = new Binder();
+ }
+ if ((mBuilderFieldsSet & 0x400000) == 0) {
mIPCInterface = null;
}
SampleDataClass o = new SampleDataClass(
@@ -1967,6 +2035,7 @@ public final class SampleDataClass implements Parcelable {
mState,
charSeq,
mLinkAddresses5,
+ mLinkAddresses7,
mStringRes,
mDayOfWeek,
mCoords,
@@ -1976,7 +2045,7 @@ public final class SampleDataClass implements Parcelable {
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x400000) != 0) {
+ if ((mBuilderFieldsSet & 0x800000) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -1984,10 +2053,10 @@ public final class SampleDataClass implements Parcelable {
}
@DataClass.Generated(
- time = 1616541539978L,
+ time = 1697693846352L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
- inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final int STATE_UNDEFINED\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange int mDayOfWeek\nprivate @android.annotation.Size @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange float[] mCoords\nprivate @android.annotation.NonNull android.os.IBinder mToken\nprivate @android.annotation.Nullable android.companion.ICompanionDeviceManager mIPCInterface\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
+ inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final int STATE_UNDEFINED\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses7\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange int mDayOfWeek\nprivate @android.annotation.Size @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange float[] mCoords\nprivate @android.annotation.NonNull android.os.IBinder mToken\nprivate @android.annotation.Nullable android.companion.ICompanionDeviceManager mIPCInterface\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/tests/CompanionDeviceMultiDeviceTests/OWNERS b/tests/CompanionDeviceMultiDeviceTests/OWNERS
new file mode 100644
index 000000000000..7517836d9d71
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 708992
+evanxinchen@google.com
+guojing@google.com
+jeremyns@google.com
+raphk@google.com
+yukl@google.com
diff --git a/tests/CompanionDeviceMultiDeviceTests/README.md b/tests/CompanionDeviceMultiDeviceTests/README.md
new file mode 100644
index 000000000000..6cf735aaf00a
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/README.md
@@ -0,0 +1,17 @@
+## CDM Multi-device Tests
+
+### Device Setup
+To test on physical devices, connect _two_ devices locally and enable USB debugging setting on both devices.
+
+When running on a cloudtop or other remote setups, use pontis to connect the devices on remote set up by running `pontis start`.
+Verify that pontis client is connected via `pontis status` and confirm that both devices are in "connected" state via `adb devices`.
+
+See go/pontis for more details regarding this workflow.
+
+To test on virtual devices, follow instructions to [set up netsim on cuttlefish](https://g3doc.corp.google.com/ambient/d2di/sim/g3doc/guide/cuttlefish.md?cl=head).
+Launch _two_ instances of virtual devices by specifying `--num_instances=2` parameter.
+
+### Running the Test
+```
+atest CompanionDeviceManagerMultiDeviceTestCases
+```
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp
new file mode 100644
index 000000000000..1e68c9dd459f
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "cdm_snippet",
+ srcs: ["src/**/*.kt"],
+ manifest: "AndroidManifest.xml",
+
+ platform_apis: true,
+ target_sdk_version: "current",
+
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "cts-companion-common",
+ "cts-companion-uicommon",
+ "kotlin-stdlib",
+ "mobly-snippet-lib",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ optimize: {
+ proguard_compatibility: true,
+ proguard_flags_files: ["proguard.flags"],
+ },
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml b/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml
new file mode 100644
index 000000000000..11dc7634aab3
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.companion.multidevices">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
+ <uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" />
+ <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.DELIVER_COMPANION_MESSAGES" />
+
+ <uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
+ <uses-feature android:name="android.software.companion_device_setup" />
+
+ <application>
+ <!-- Add any classes that implement the Snippet interface as meta-data, whose
+ value is a comma-separated string, each section being the package path
+ of a snippet class -->
+ <meta-data
+ android:name="mobly-snippets"
+ android:value="android.companion.multidevices.CompanionDeviceManagerSnippet" />
+ </application>
+
+ <!-- Add an instrumentation tag so that the app can be launched through an
+ instrument command. The runner `com.google.android.mobly.snippet.SnippetRunner`
+ is derived from `AndroidJUnitRunner`, and is required to use the
+ Mobly Snippet Lib. -->
+ <instrumentation
+ android:name="com.google.android.mobly.snippet.SnippetRunner"
+ android:targetPackage="android.companion.multidevices" />
+</manifest>
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags b/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags
new file mode 100644
index 000000000000..1c70253af87c
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags
@@ -0,0 +1,24 @@
+# Keep all companion classes.
+-keep class android.companion.** {
+ *;
+}
+
+# Do not touch Mobly.
+-keep class com.google.android.mobly.** {
+ *;
+}
+
+# Keep names for easy debugging.
+-dontobfuscate
+
+# Necessary to allow debugging.
+-keepattributes *
+
+# By default, proguard leaves all classes in their original package, which
+# needlessly repeats com.google.android.apps.etc.
+-repackageclasses ""
+
+# Allows proguard to make private and protected methods and fields public as
+# part of optimization. This lets proguard inline trivial getter/setter
+# methods.
+-allowaccessmodification \ No newline at end of file
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt
new file mode 100644
index 000000000000..3e4944a21258
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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.companion.multidevices
+
+import android.companion.AssociationInfo
+import android.companion.CompanionDeviceManager
+import android.companion.CompanionException
+import android.content.IntentSender
+import android.os.OutcomeReceiver
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit.SECONDS
+import java.util.concurrent.TimeoutException
+
+/** Blocking callbacks for Wi-Fi Aware and Connectivity Manager. */
+object CallbackUtils {
+ private const val TAG = "CDM_CallbackUtils"
+ private const val CALLBACK_TIMEOUT_SEC = 30L
+ private const val MESSAGE_CALLBACK_TIMEOUT_SEC = 5L
+
+ class AssociationCallback : CompanionDeviceManager.Callback() {
+ private val pending = CountDownLatch(1)
+ private val created = CountDownLatch(1)
+
+ private var pendingIntent: IntentSender? = null
+ private var associationInfo: AssociationInfo? = null
+ private var error: String? = null
+
+ override fun onAssociationPending(intentSender: IntentSender) {
+ this.pendingIntent = intentSender
+ pending.countDown()
+ }
+
+ override fun onAssociationCreated(associationInfo: AssociationInfo) {
+ this.associationInfo = associationInfo
+ created.countDown()
+ }
+
+ override fun onFailure(error: CharSequence?) {
+ this.error = error?.toString() ?: "There was an unexpected failure."
+ pending.countDown()
+ created.countDown()
+ }
+
+ fun waitForPendingIntent(): IntentSender? {
+ if (!pending.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+ throw TimeoutException("Pending association request timed out.")
+ }
+
+ error?.let {
+ throw CompanionException(it)
+ }
+
+ return pendingIntent
+ }
+
+ fun waitForAssociation(): AssociationInfo? {
+ if (!created.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+ throw TimeoutException("Association request timed out.")
+ }
+
+ error?.let {
+ throw CompanionException(it)
+ }
+
+ return associationInfo
+ }
+ }
+
+ class SystemDataTransferCallback : OutcomeReceiver<Void, CompanionException> {
+ private val completed = CountDownLatch(1)
+
+ private var error: CompanionException? = null
+
+ override fun onResult(result: Void?) {
+ completed.countDown()
+ }
+
+ override fun onError(error: CompanionException) {
+ this.error = error
+ completed.countDown()
+ }
+
+ fun waitForCompletion() {
+ if (!completed.await(CALLBACK_TIMEOUT_SEC, SECONDS)) {
+ throw TimeoutException("System data transfer timed out.")
+ }
+
+ error?.let {
+ throw it
+ }
+ }
+ }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
new file mode 100644
index 000000000000..03352e36576e
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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.companion.multidevices
+
+import android.app.Instrumentation
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.companion.AssociationInfo
+import android.companion.AssociationRequest
+import android.companion.BluetoothDeviceFilter
+import android.companion.CompanionDeviceManager
+import android.companion.CompanionException
+import android.companion.cts.common.CompanionActivity
+import android.companion.multidevices.CallbackUtils.AssociationCallback
+import android.companion.multidevices.CallbackUtils.SystemDataTransferCallback
+import android.companion.multidevices.bluetooth.BluetoothConnector
+import android.companion.multidevices.bluetooth.BluetoothController
+import android.companion.cts.uicommon.CompanionDeviceManagerUi
+import android.content.Context
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.HandlerThread
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.google.android.mobly.snippet.Snippet
+import com.google.android.mobly.snippet.event.EventCache
+import com.google.android.mobly.snippet.rpc.Rpc
+import java.util.concurrent.Executor
+import java.util.regex.Pattern
+
+/**
+ * Snippet class that exposes Android APIs in CompanionDeviceManager.
+ */
+class CompanionDeviceManagerSnippet : Snippet {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()!!
+ private val context: Context = instrumentation.targetContext
+
+ private val btAdapter: BluetoothAdapter by lazy {
+ (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter
+ }
+ private val companionDeviceManager: CompanionDeviceManager by lazy {
+ context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager
+ }
+ private val btConnector: BluetoothConnector by lazy {
+ BluetoothConnector(btAdapter, companionDeviceManager)
+ }
+
+ private val uiDevice by lazy { UiDevice.getInstance(instrumentation) }
+ private val confirmationUi by lazy { CompanionDeviceManagerUi(uiDevice) }
+ private val btController by lazy { BluetoothController(context, btAdapter, uiDevice) }
+
+ private val eventCache = EventCache.getInstance()
+ private val handlerThread = HandlerThread("Snippet-Aware")
+ private val handler: Handler
+ private val executor: Executor
+
+ init {
+ handlerThread.start()
+ handler = Handler(handlerThread.looper)
+ executor = HandlerExecutor(handler)
+ }
+
+ /**
+ * Make device discoverable to other devices via BLE and return device name.
+ */
+ @Rpc(description = "Start advertising device to be discoverable.")
+ fun becomeDiscoverable(): String {
+ btController.becomeDiscoverable()
+ return btAdapter.name
+ }
+
+ /**
+ * Associate with a nearby device with given name and return newly-created association ID.
+ */
+ @Rpc(description = "Start device association flow.")
+ @Throws(Exception::class)
+ fun associate(deviceName: String): Int {
+ val filter = BluetoothDeviceFilter.Builder()
+ .setNamePattern(Pattern.compile(deviceName))
+ .build()
+ val request = AssociationRequest.Builder()
+ .setSingleDevice(true)
+ .addDeviceFilter(filter)
+ .build()
+ val callback = AssociationCallback()
+ companionDeviceManager.associate(request, callback, handler)
+ val pendingConfirmation = callback.waitForPendingIntent()
+ ?: throw CompanionException("Association is pending but intent sender is null.")
+ CompanionActivity.launchAndWait(context)
+ CompanionActivity.startIntentSender(pendingConfirmation)
+ confirmationUi.waitUntilVisible()
+ confirmationUi.waitUntilPositiveButtonIsEnabledAndClick()
+ confirmationUi.waitUntilGone()
+
+ val (_, result) = CompanionActivity.waitForActivityResult()
+ if (result == null) {
+ throw CompanionException("Association result can't be null.")
+ }
+
+ val association = checkNotNull(result.getParcelableExtra(
+ CompanionDeviceManager.EXTRA_ASSOCIATION,
+ AssociationInfo::class.java
+ ))
+ val remoteDevice = association.associatedDevice?.getBluetoothDevice()!!
+
+ // Register associated device
+ btConnector.registerDevice(association.id, remoteDevice)
+
+ return association.id
+ }
+
+ /**
+ * Disassociate an association with given ID.
+ */
+ @Rpc(description = "Disassociate device.")
+ @Throws(Exception::class)
+ fun disassociate(associationId: Int) {
+ companionDeviceManager.disassociate(associationId)
+ }
+
+ /**
+ * Consent to system data transfer and carry it out using Bluetooth socket.
+ */
+ @Rpc(description = "Start permissions sync.")
+ fun startPermissionsSync(associationId: Int) {
+ val pendingIntent = checkNotNull(companionDeviceManager
+ .buildPermissionTransferUserConsentIntent(associationId))
+ CompanionActivity.launchAndWait(context)
+ CompanionActivity.startIntentSender(pendingIntent)
+ confirmationUi.waitUntilSystemDataTransferConfirmationVisible()
+ confirmationUi.clickPositiveButton()
+ confirmationUi.waitUntilGone()
+
+ CompanionActivity.waitForActivityResult()
+
+ val callback = SystemDataTransferCallback()
+ companionDeviceManager.startSystemDataTransfer(associationId, executor, callback)
+ callback.waitForCompletion()
+ }
+
+ @Rpc(description = "Attach transport to the BT client socket.")
+ fun attachClientSocket(id: Int) {
+ btConnector.attachClientSocket(id)
+ }
+
+ @Rpc(description = "Attach transport to the BT server socket.")
+ fun attachServerSocket(id: Int) {
+ btConnector.attachServerSocket(id)
+ }
+
+ @Rpc(description = "Close all open sockets.")
+ fun closeAllSockets() {
+ // Close all open sockets
+ btConnector.closeAllSockets()
+ }
+
+ @Rpc(description = "Disassociate all associations.")
+ fun disassociateAll() {
+ companionDeviceManager.myAssociations.forEach {
+ Log.d(TAG, "Disassociating id=${it.id}.")
+ companionDeviceManager.disassociate(it.id)
+ }
+ }
+
+ companion object {
+ private const val TAG = "CDM_CompanionDeviceManagerSnippet"
+ }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt
new file mode 100644
index 000000000000..c7312d2e4b10
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 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.companion.multidevices.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothServerSocket
+import android.bluetooth.BluetoothSocket
+import android.companion.CompanionDeviceManager
+import android.util.Log
+import java.io.IOException
+import java.util.UUID
+
+class BluetoothConnector(
+ private val adapter: BluetoothAdapter,
+ private val cdm: CompanionDeviceManager
+) {
+ companion object {
+ private const val TAG = "CDM_BluetoothServer"
+
+ private val SERVICE_NAME = "CDM_BluetoothChannel"
+ private val SERVICE_UUID = UUID.fromString("435fe1d9-56c5-455d-a516-d5e6b22c52f9")
+
+ // Registry of bluetooth server threads
+ private val serverThreads = mutableMapOf<Int, BluetoothServerThread>()
+
+ // Registry of remote bluetooth devices
+ private val remoteDevices = mutableMapOf<Int, BluetoothDevice>()
+
+ // Set of connected client sockets
+ private val clientSockets = mutableMapOf<Int, BluetoothSocket>()
+ }
+
+ fun attachClientSocket(associationId: Int) {
+ try {
+ val device = remoteDevices[associationId]!!
+ val socket = device.createRfcommSocketToServiceRecord(SERVICE_UUID)
+ if (clientSockets.containsKey(associationId)) {
+ detachClientSocket(associationId)
+ clientSockets[associationId] = socket
+ } else {
+ clientSockets += associationId to socket
+ }
+
+ socket.connect()
+ Log.d(TAG, "Attaching client socket $socket.")
+ cdm.attachSystemDataTransport(
+ associationId,
+ socket.inputStream,
+ socket.outputStream
+ )
+ } catch (e: IOException) {
+ Log.e(TAG, "Failed to attach client socket.", e)
+ throw RuntimeException(e)
+ }
+ }
+
+ fun attachServerSocket(associationId: Int) {
+ val serverThread: BluetoothServerThread
+ if (serverThreads.containsKey(associationId)) {
+ serverThread = serverThreads[associationId]!!
+ } else {
+ serverThread = BluetoothServerThread(associationId)
+ serverThreads += associationId to serverThread
+ }
+
+ // Start thread
+ if (!serverThread.isOpen) {
+ serverThread.start()
+ }
+ }
+
+ fun closeAllSockets() {
+ val iter = clientSockets.keys.iterator()
+ while (iter.hasNext()) {
+ detachClientSocket(iter.next())
+ }
+ for (thread in serverThreads.values) {
+ thread.shutdown()
+ }
+ serverThreads.clear()
+ }
+
+ fun registerDevice(associationId: Int, remoteDevice: BluetoothDevice) {
+ remoteDevices[associationId] = remoteDevice
+ }
+
+ private fun detachClientSocket(associationId: Int) {
+ try {
+ Log.d(TAG, "Detaching client socket.")
+ cdm.detachSystemDataTransport(associationId)
+ clientSockets[associationId]?.close()
+ } catch (e: IOException) {
+ Log.e(TAG, "Failed to detach client socket.", e)
+ throw RuntimeException(e)
+ }
+ }
+
+ inner class BluetoothServerThread(
+ private val associationId: Int
+ ) : Thread() {
+ private lateinit var mServerSocket: BluetoothServerSocket
+
+ var isOpen = false
+
+ override fun run() {
+ try {
+ Log.d(TAG, "Listening for remote connections...")
+ mServerSocket = adapter.listenUsingRfcommWithServiceRecord(
+ SERVICE_NAME,
+ SERVICE_UUID
+ )
+ isOpen = true
+ do {
+ val socket = mServerSocket.accept()
+ Log.d(TAG, "Attaching server socket $socket.")
+ cdm.attachSystemDataTransport(
+ associationId,
+ socket.inputStream,
+ socket.outputStream
+ )
+ } while (isOpen)
+ } catch (e: IOException) {
+ throw RuntimeException(e)
+ }
+ }
+
+ fun shutdown() {
+ if (!isOpen || !this::mServerSocket.isInitialized) return
+
+ try {
+ Log.d(TAG, "Closing server socket.")
+ cdm.detachSystemDataTransport(associationId)
+ mServerSocket.close()
+ isOpen = false
+ } catch (e: IOException) {
+ throw RuntimeException(e)
+ }
+ }
+ }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt
new file mode 100644
index 000000000000..c4d202694b87
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 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.companion.multidevices.bluetooth
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.SystemClock
+import android.util.Log
+import androidx.test.uiautomator.UiDevice
+import java.util.concurrent.TimeoutException
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+/** Controls the local Bluetooth adapter for testing. */
+class BluetoothController(
+ private val context: Context,
+ private val adapter: BluetoothAdapter,
+ private val ui: UiDevice
+) {
+ companion object {
+ private const val TAG = "CDM_BluetoothController"
+ }
+
+ private val bluetoothUi by lazy { BluetoothUi(ui) }
+
+ init {
+ Log.d(TAG, "Registering pairing listener.")
+ context.registerReceiver(
+ PairingBroadcastReceiver(),
+ IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST)
+ )
+ }
+
+ val isEnabled: Boolean
+ get() = adapter.isEnabled
+
+ /** Turns on the local Bluetooth adapter */
+ fun enableBluetooth() {
+ if (isEnabled) return
+
+ val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ context.startActivity(intent)
+ bluetoothUi.clickAllowButton()
+ waitFor { adapter.state == BluetoothAdapter.STATE_ON }
+ }
+
+ /** Become discoverable for specified duration */
+ fun becomeDiscoverable(duration: Duration = 15.seconds) {
+ enableBluetooth()
+
+ val intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE)
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration.inWholeSeconds)
+ context.startActivity(intent)
+ bluetoothUi.clickAllowButton()
+ }
+
+ /** Unpair all devices for cleanup */
+ fun unpairAllDevices() {
+ for (device in adapter.bondedDevices) {
+ Log.d(TAG, "Unpairing $device.")
+ if (!device.removeBond()) continue
+ waitFor { device.bondState == BluetoothDevice.BOND_NONE }
+ }
+ }
+
+ private fun waitFor(
+ interval: Duration = 1.seconds,
+ timeout: Duration = 5.seconds,
+ condition: () -> Boolean
+ ) {
+ var elapsed = 0L
+ while (elapsed < timeout.inWholeMilliseconds) {
+ if (condition.invoke()) return
+ SystemClock.sleep(interval.inWholeMilliseconds)
+ elapsed += interval.inWholeMilliseconds
+ }
+ throw TimeoutException("Bluetooth did not become an expected state.")
+ }
+
+ inner class PairingBroadcastReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ Log.d(TAG, "Received broadcast for ${intent.action}")
+
+ // onReceive() somehow blocks pairing prompt from launching
+ Thread { bluetoothUi.confirmPairingRequest() }.start()
+ context.unregisterReceiver(this)
+ }
+ }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt
new file mode 100644
index 000000000000..6983cb035498
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.companion.multidevices.bluetooth
+
+import android.companion.cts.uicommon.CompanionDeviceManagerUi
+import android.util.Log
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import java.util.regex.Pattern
+
+class BluetoothUi(private val ui: UiDevice) : CompanionDeviceManagerUi(ui) {
+ fun clickAllowButton() = click(ALLOW_BUTTON, "Allow button")
+
+ fun confirmPairingRequest(): Boolean {
+ if (ui.hasObject(PAIRING_PIN_ENTRY)) {
+ // It is prompting for a custom user pin entry
+ Log.d(TAG, "Is user entry prompt.")
+ ui.findObject(PAIRING_PIN_ENTRY).text = "0000"
+ click(OK_BUTTON, "Ok button")
+ } else {
+ // It just needs user consent
+ Log.d(TAG, "Looking for pair button.")
+ val button = ui.wait(Until.findObject(PAIR_BUTTON), 1_000)
+ if (button != null) {
+ Log.d(TAG, "Pair button found.")
+ button.click()
+ return true
+ }
+ Log.d(TAG, "Pair button not found.")
+ }
+ return false
+ }
+
+ companion object {
+ private const val TAG = "CDM_BluetoothUi"
+
+ private val ALLOW_TEXT_PATTERN = caseInsensitive("allow")
+ private val ALLOW_BUTTON = By.text(ALLOW_TEXT_PATTERN).clickable(true)
+
+ private val PAIRING_PIN_ENTRY = By.clazz(".EditText")
+
+ private val OK_TEXT_PATTERN = caseInsensitive("ok")
+ private val OK_BUTTON = By.text(OK_TEXT_PATTERN).clickable(true)
+
+ private val PAIR_TEXT_PATTERN = caseInsensitive("pair")
+ private val PAIR_BUTTON = By.text(PAIR_TEXT_PATTERN).clickable(true)
+
+ private fun caseInsensitive(text: String): Pattern =
+ Pattern.compile(text, Pattern.CASE_INSENSITIVE)
+ }
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
new file mode 100644
index 000000000000..03335c7cd576
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
@@ -0,0 +1,50 @@
+// Copyright (C) 2022 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+python_test_host {
+ name: "CompanionDeviceManagerMultiDeviceTestCases",
+ main: "cdm_transport_test.py",
+ srcs: ["*.py"],
+ libs: [
+ "mobly",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ test_options: {
+ unit_test: false,
+ tags: ["mobly"],
+ },
+ data: [
+ ":cdm_snippet",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+}
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml
new file mode 100644
index 000000000000..9d1813ff79bc
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Config for CDM multi-device test cases">
+ <option name="test-tag" value="CompanionDeviceMultiDeviceTests" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.software.companion_device_setup" />
+ </object>
+
+ <device name="device1">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="cdm_snippet.apk" />
+ </target_preparer>
+ </device>
+ <device name="device2">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="cdm_snippet.apk" />
+ </target_preparer>
+ </device>
+
+ <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest">
+ <!-- The mobly-par-file-name should match the module name -->
+ <option name="mobly-par-file-name" value="CompanionDeviceManagerMultiDeviceTestCases" />
+ <!-- Timeout limit in milliseconds for all test cases of the python binary -->
+ <option name="mobly-test-timeout" value="60000" />
+ </test>
+</configuration>
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
new file mode 100644
index 000000000000..e74a30c62d8b
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+# Lint as: python3
+"""
+Base class for setting up devices for CDM functionalities.
+"""
+
+from mobly import base_test
+from mobly import utils
+from mobly.controllers import android_device
+
+CDM_SNIPPET_PACKAGE = 'android.companion.multidevices'
+
+
+class BaseTestClass(base_test.BaseTestClass):
+
+ def setup_class(self):
+ # Declare that two Android devices are needed.
+ self.sender, self.receiver = self.register_controller(
+ android_device, min_number=2)
+ self.sender_id = None
+ self.receiver_id = None
+
+ def _setup_device(device):
+ device.load_snippet('cdm', CDM_SNIPPET_PACKAGE)
+ device.adb.shell('input keyevent KEYCODE_WAKEUP')
+ device.adb.shell('input keyevent KEYCODE_MENU')
+ device.adb.shell('input keyevent KEYCODE_HOME')
+
+ # Clean up existing associations
+ device.cdm.disassociateAll()
+
+ # Sets up devices in parallel to save time.
+ utils.concurrent_exec(
+ _setup_device,
+ ((self.sender,), (self.receiver,)),
+ max_workers=2,
+ raise_on_exception=True)
+
+ def associate_devices(self) -> tuple[int, int]:
+ """Associate devices with each other and return association IDs for both"""
+ # If association already exists, don't need another
+ if self.sender_id and self.receiver_id:
+ return (self.sender_id, self.receiver_id)
+
+ receiver_name = self.receiver.cdm.becomeDiscoverable()
+ self.receiver_id = self.sender.cdm.associate(receiver_name)
+
+ sender_name = self.sender.cdm.becomeDiscoverable()
+ self.sender_id = self.receiver.cdm.associate(sender_name)
+
+ return (self.sender_id, self.receiver_id)
+
+ def attach_transports(self):
+ """Attach transports to both devices"""
+ self.associate_devices()
+
+ self.receiver.cdm.attachServerSocket(self.sender_id)
+ self.sender.cdm.attachClientSocket(self.receiver_id)
+
+ def teardown_class(self):
+ """Clean up the opened sockets"""
+ self.sender.cdm.closeAllSockets()
+ self.receiver.cdm.closeAllSockets()
+
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
new file mode 100644
index 000000000000..bf7e1f3c90c7
--- /dev/null
+++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python3
+# Lint as: python3
+"""
+Test E2E CDM functions on mobly.
+"""
+
+import cdm_base_test
+import sys
+
+from mobly import asserts
+from mobly import test_runner
+
+CDM_SNIPPET_PACKAGE = 'android.companion.multidevices'
+
+
+class TransportTestClass(cdm_base_test.BaseTestClass):
+
+ def test_permissions_sync(self):
+ """This tests permissions sync from one device to another."""
+
+ # associate and attach transports
+ self.attach_transports()
+
+ # start permissions sync
+ self.sender.cdm.startPermissionsSync(self.receiver_id)
+
+
+if __name__ == '__main__':
+ test_runner.main() \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/Android.bp b/tests/CtsSurfaceControlTestsStaging/Android.bp
new file mode 100644
index 000000000000..96e4a9ea4300
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/Android.bp
@@ -0,0 +1,49 @@
+// Copyright 2023 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ // SurfaceControl tests for APIs that are not public. If an API becomes public, they should
+ // live in CtsSurfaceControlTests instead.
+ name: "CtsSurfaceControlTestsStaging",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "com.google.android.material_material",
+ "SurfaceFlingerProperties",
+ "truth",
+ ],
+ resource_dirs: ["src/main/res"],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifestNotification.xml b/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml
index ad33deef8cc3..d8eb9ff37e78 100644
--- a/tests/FlickerTests/manifests/AndroidManifestNotification.xml
+++ b/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml
@@ -1,5 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+ ~ Copyright 2023 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.
@@ -15,10 +16,18 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.close">
+ package="android.view.surfacecontroltests">
+
+ <application android:debuggable="true" android:testOnly="true">
+ <uses-library android:name="android.test.runner"/>
+ <activity
+ android:name=".GraphicsActivity"
+ android:exported="false">
+ </activity>
+ </application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.notification"
- android:label="WindowManager Flicker Tests">
+ android:targetPackage="android.view.surfacecontroltests"
+ android:label="Tests of android.view.surfacecontroltests">
</instrumentation>
-</manifest>
+</manifest> \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/AndroidTest.xml b/tests/CtsSurfaceControlTestsStaging/AndroidTest.xml
new file mode 100644
index 000000000000..5c0163fcfa7e
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+<configuration description="Config for CtsSurfaceControlTestsStaging cases">
+ <option name="test-tag" value="CtsSurfaceControlTestsStaging"/>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="CtsSurfaceControlTestsStaging.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.view.surfacecontroltests" />
+ <option name="hidden-api-checks" value="false" />
+ <option name="isolated-storage" value="false" />
+ </test>
+</configuration> \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/OWNERS b/tests/CtsSurfaceControlTestsStaging/OWNERS
new file mode 100644
index 000000000000..8ad3446c7b52
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/OWNERS
@@ -0,0 +1 @@
+include platform/cts:/tests/tests/graphics/src/android/graphics/OWNERS \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/README.md b/tests/CtsSurfaceControlTestsStaging/README.md
new file mode 100644
index 000000000000..1a2c5f64e73f
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/README.md
@@ -0,0 +1,4 @@
+Tests for SurfaceControl for APIs that are not public. If APIs are made public, they can
+be moved into the cts files, e.g.:
+1. cts/tests/surfacecontrol/src/android/view/surfacecontrol/cts/SurfaceControlTest.java
+2. cts/tests/surfacecontrol/src/android/view/surfacecontrol/cts/ASurfaceControlTest.java \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
new file mode 100644
index 000000000000..0b75eb35520e
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsSurfaceControlTestsStaging"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
new file mode 100644
index 000000000000..4548a7df6874
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/GraphicsActivity.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright 2023 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.view.surfacecontroltests;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.sysprop.SurfaceFlingerProperties;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * An Activity to help with frame rate testing.
+ */
+public class GraphicsActivity extends Activity {
+ private static final String TAG = "GraphicsActivity";
+ private static final long FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS = 2;
+ private static final long STABLE_FRAME_RATE_WAIT_SECONDS = 1;
+ private static final long POST_BUFFER_INTERVAL_MILLIS = 500;
+ private static final int PRECONDITION_WAIT_MAX_ATTEMPTS = 5;
+ private static final long PRECONDITION_WAIT_TIMEOUT_SECONDS = 20;
+ private static final long PRECONDITION_VIOLATION_WAIT_TIMEOUT_SECONDS = 3;
+ private static final float FRAME_RATE_TOLERANCE = 0.01f;
+
+ static class FpsRange {
+ // The max difference between refresh rates in order to be considered equal.
+ private static final double FPS_THRESHOLD = 0.001;
+ double mMin;
+ double mMax;
+ FpsRange(double min, double max) {
+ mMin = min;
+ mMax = max;
+ }
+ public boolean includes(double fps) {
+ return fps >= mMin - FPS_THRESHOLD && mMax + FPS_THRESHOLD >= fps;
+ }
+ }
+
+ // TODO(b/293651105): Unhardcode category fps range mapping
+ private static final FpsRange FRAME_RATE_CATEGORY_HIGH = new FpsRange(90, 120);
+ private static final FpsRange FRAME_RATE_CATEGORY_NORMAL = new FpsRange(60, 90);
+ private static final FpsRange FRAME_RATE_CATEGORY_LOW = new FpsRange(30, 30);
+
+ private DisplayManager mDisplayManager;
+ private SurfaceView mSurfaceView;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Object mLock = new Object();
+ private Surface mSurface = null;
+ private float mDisplayModeRefreshRate;
+ private float mDisplayRefreshRate;
+ private ModeChangedEvents mModeChangedEvents = new ModeChangedEvents();
+
+ private enum ActivityState { RUNNING, PAUSED, DESTROYED }
+
+ private ActivityState mActivityState;
+
+ SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurface = holder.getSurface();
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurface = null;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+ };
+
+ DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return;
+ }
+ synchronized (mLock) {
+ Display display = mDisplayManager.getDisplay(displayId);
+ Display.Mode mode = display.getMode();
+ mModeChangedEvents.add(mode);
+ float displayModeRefreshRate = mode.getRefreshRate();
+ float displayRefreshRate = display.getRefreshRate();
+ if (displayModeRefreshRate != mDisplayModeRefreshRate
+ || displayRefreshRate != mDisplayRefreshRate) {
+ Log.i(TAG,
+ String.format("Refresh rate changed: (mode) %.2f --> %.2f, "
+ + "(display) %.2f --> %.2f",
+ mDisplayModeRefreshRate, displayModeRefreshRate,
+ mDisplayRefreshRate, displayRefreshRate));
+ mDisplayModeRefreshRate = displayModeRefreshRate;
+ mDisplayRefreshRate = displayRefreshRate;
+ mLock.notify();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+ };
+
+ // Wrapper around ArrayList for which the only allowed mutable operation is add().
+ // We use this to store all mode change events during a test. When we need to iterate over
+ // all mode changes during a certain operation, we use the number of events in the beginning
+ // and in the end. It's important to never clear or modify the elements in this list hence the
+ // wrapper.
+ private static class ModeChangedEvents {
+ private List<Display.Mode> mEvents = new ArrayList<>();
+
+ public void add(Display.Mode mode) {
+ mEvents.add(mode);
+ }
+
+ public Display.Mode get(int i) {
+ return mEvents.get(i);
+ }
+
+ public int size() {
+ return mEvents.size();
+ }
+ }
+
+ private static class PreconditionViolatedException extends RuntimeException {
+ PreconditionViolatedException() {}
+ }
+
+ private static class FrameRateTimeoutException extends RuntimeException {
+ FrameRateTimeoutException(float expectedFrameRate, float deviceFrameRate) {
+ this.expectedFrameRate = expectedFrameRate;
+ this.deviceFrameRate = deviceFrameRate;
+ }
+
+ public float expectedFrameRate;
+ public float deviceFrameRate;
+ }
+
+ public enum Api {
+ // Much of the code is copied from the SetFrameRate cts test. Add APIs as support grows.
+ SURFACE_CONTROL("SurfaceControl");
+
+ private final String mName;
+ Api(String name) {
+ mName = name;
+ }
+
+ public String toString() {
+ return mName;
+ }
+ }
+
+ private static class TestSurface {
+ private String mName;
+ private SurfaceControl mSurfaceControl;
+ private Surface mSurface;
+ private int mColor;
+ private boolean mLastBufferPostTimeValid;
+ private long mLastBufferPostTime;
+
+ TestSurface(SurfaceControl parentSurfaceControl, Surface parentSurface, String name,
+ Rect destFrame, boolean visible, int color) {
+ mName = name;
+ mColor = color;
+
+ assertNotNull("No parent surface", parentSurfaceControl);
+ mSurfaceControl = new SurfaceControl.Builder()
+ .setParent(parentSurfaceControl)
+ .setName(mName)
+ .setBufferSize(destFrame.right - destFrame.left,
+ destFrame.bottom - destFrame.top)
+ .build();
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setGeometry(mSurfaceControl, null, destFrame, Surface.ROTATION_0)
+ .apply();
+ }
+ mSurface = new Surface(mSurfaceControl);
+
+ setVisibility(visible);
+ postBuffer();
+ }
+
+ Surface getSurface() {
+ return mSurface;
+ }
+
+ SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
+ public int setFrameRate(float frameRate) {
+ Log.i(TAG,
+ String.format("Setting frame rate for %s: frameRate=%.2f", mName, frameRate));
+
+ int rc = 0;
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setFrameRate(
+ mSurfaceControl, frameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT);
+ transaction.apply();
+ }
+ return rc;
+ }
+
+ public int setFrameRateCategory(int category) {
+ Log.i(TAG,
+ String.format(
+ "Setting frame rate category for %s: category=%d", mName, category));
+
+ int rc = 0;
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setFrameRateCategory(mSurfaceControl, category, false);
+ transaction.apply();
+ }
+ return rc;
+ }
+
+ public int setFrameRateSelectionStrategy(int strategy) {
+ Log.i(TAG,
+ String.format("Setting frame rate selection strategy for %s: strategy=%d",
+ mName, strategy));
+
+ int rc = 0;
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setFrameRateSelectionStrategy(mSurfaceControl, strategy);
+ transaction.apply();
+ }
+ return rc;
+ }
+
+ public void setVisibility(boolean visible) {
+ Log.i(TAG,
+ String.format("Setting visibility for %s: %s", mName,
+ visible ? "visible" : "hidden"));
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.setVisibility(mSurfaceControl, visible).apply();
+ }
+ }
+
+ public void postBuffer() {
+ mLastBufferPostTimeValid = true;
+ mLastBufferPostTime = System.nanoTime();
+ Canvas canvas = mSurface.lockHardwareCanvas();
+ canvas.drawColor(mColor);
+ mSurface.unlockCanvasAndPost(canvas);
+ }
+
+ public long getLastBufferPostTime() {
+ assertTrue("No buffer posted yet", mLastBufferPostTimeValid);
+ return mLastBufferPostTime;
+ }
+
+ public void release() {
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+ if (mSurfaceControl != null) {
+ try (SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+ transaction.reparent(mSurfaceControl, null).apply();
+ }
+ mSurfaceControl.release();
+ mSurfaceControl = null;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ release();
+ } finally {
+ super.finalize();
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ synchronized (mLock) {
+ mDisplayManager = getSystemService(DisplayManager.class);
+ Display display = getDisplay();
+ Display.Mode mode = display.getMode();
+ mDisplayModeRefreshRate = mode.getRefreshRate();
+ mDisplayRefreshRate = display.getRefreshRate();
+ // Insert the initial mode so we have the full display mode history.
+ mModeChangedEvents.add(mode);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.setWillNotDraw(false);
+ mSurfaceView.setZOrderOnTop(true);
+ setContentView(mSurfaceView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ synchronized (mLock) {
+ mActivityState = ActivityState.DESTROYED;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ synchronized (mLock) {
+ mActivityState = ActivityState.PAUSED;
+ mLock.notify();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ synchronized (mLock) {
+ mActivityState = ActivityState.RUNNING;
+ mLock.notify();
+ }
+ }
+
+ // Returns the refresh rates with the same resolution as "mode".
+ private ArrayList<Float> getRefreshRates(Display.Mode mode, Display display) {
+ Display.Mode[] modes = display.getSupportedModes();
+ ArrayList<Float> frameRates = new ArrayList<>();
+ for (Display.Mode supportedMode : modes) {
+ if (hasSameResolution(supportedMode, mode)) {
+ frameRates.add(supportedMode.getRefreshRate());
+ }
+ }
+ Collections.sort(frameRates);
+ ArrayList<Float> uniqueFrameRates = new ArrayList<>();
+ for (float frameRate : frameRates) {
+ if (uniqueFrameRates.isEmpty()
+ || frameRate - uniqueFrameRates.get(uniqueFrameRates.size() - 1)
+ >= FRAME_RATE_TOLERANCE) {
+ uniqueFrameRates.add(frameRate);
+ }
+ }
+ Log.i(TAG,
+ "**** Available display refresh rates: "
+ + uniqueFrameRates.stream()
+ .map(Object::toString)
+ .collect(Collectors.joining(", ")));
+ return uniqueFrameRates;
+ }
+
+ private boolean hasSameResolution(Display.Mode mode1, Display.Mode mode2) {
+ return mode1.getPhysicalHeight() == mode2.getPhysicalHeight()
+ && mode1.getPhysicalWidth() == mode2.getPhysicalWidth();
+ }
+
+ private boolean isFrameRateMultiple(float higherFrameRate, float lowerFrameRate) {
+ float multiple = higherFrameRate / lowerFrameRate;
+ int roundedMultiple = Math.round(multiple);
+ return roundedMultiple > 0
+ && Math.abs(roundedMultiple * lowerFrameRate - higherFrameRate)
+ <= FRAME_RATE_TOLERANCE;
+ }
+
+ private boolean frameRateEquals(float frameRate1, float frameRate2) {
+ return Math.abs(frameRate1 - frameRate2) <= FRAME_RATE_TOLERANCE;
+ }
+
+ // Waits until our SurfaceHolder has a surface and the activity is resumed.
+ private void waitForPreconditions() throws InterruptedException {
+ assertNotSame(
+ "Activity was unexpectedly destroyed", mActivityState, ActivityState.DESTROYED);
+ if (mSurface == null || mActivityState != ActivityState.RUNNING) {
+ Log.i(TAG,
+ String.format(
+ "Waiting for preconditions. Have surface? %b. Activity resumed? %b.",
+ mSurface != null, mActivityState == ActivityState.RUNNING));
+ }
+ long nowNanos = System.nanoTime();
+ long endTimeNanos = nowNanos + PRECONDITION_WAIT_TIMEOUT_SECONDS * 1_000_000_000L;
+ while (mSurface == null || mActivityState != ActivityState.RUNNING) {
+ long timeRemainingMillis = (endTimeNanos - nowNanos) / 1_000_000;
+ assertTrue(String.format("Timed out waiting for preconditions. Have surface? %b."
+ + " Activity resumed? %b.",
+ mSurface != null, mActivityState == ActivityState.RUNNING),
+ timeRemainingMillis > 0);
+ mLock.wait(timeRemainingMillis);
+ assertNotSame(
+ "Activity was unexpectedly destroyed", mActivityState, ActivityState.DESTROYED);
+ nowNanos = System.nanoTime();
+ }
+ // Make sure any previous mode changes are completed.
+ waitForStableFrameRate();
+ }
+
+ // Returns true if we encounter a precondition violation, false otherwise.
+ private boolean waitForPreconditionViolation() throws InterruptedException {
+ assertNotSame(
+ "Activity was unexpectedly destroyed", mActivityState, ActivityState.DESTROYED);
+ long nowNanos = System.nanoTime();
+ long endTimeNanos = nowNanos + PRECONDITION_VIOLATION_WAIT_TIMEOUT_SECONDS * 1_000_000_000L;
+ while (mSurface != null && mActivityState == ActivityState.RUNNING) {
+ long timeRemainingMillis = (endTimeNanos - nowNanos) / 1_000_000;
+ if (timeRemainingMillis <= 0) {
+ break;
+ }
+ mLock.wait(timeRemainingMillis);
+ assertNotSame(
+ "Activity was unexpectedly destroyed", mActivityState, ActivityState.DESTROYED);
+ nowNanos = System.nanoTime();
+ }
+ return mSurface == null || mActivityState != ActivityState.RUNNING;
+ }
+
+ private void verifyPreconditions() {
+ if (mSurface == null || mActivityState != ActivityState.RUNNING) {
+ throw new PreconditionViolatedException();
+ }
+ }
+
+ // Returns true if we reached waitUntilNanos, false if some other event occurred.
+ private boolean waitForEvents(long waitUntilNanos, TestSurface[] surfaces)
+ throws InterruptedException {
+ int numModeChangedEvents = mModeChangedEvents.size();
+ long nowNanos = System.nanoTime();
+ while (nowNanos < waitUntilNanos) {
+ long surfacePostTime = Long.MAX_VALUE;
+ for (TestSurface surface : surfaces) {
+ surfacePostTime = Math.min(surfacePostTime,
+ surface.getLastBufferPostTime()
+ + (POST_BUFFER_INTERVAL_MILLIS * 1_000_000L));
+ }
+ long timeoutNs = Math.min(waitUntilNanos, surfacePostTime) - nowNanos;
+ long timeoutMs = timeoutNs / 1_000_000L;
+ int remainderNs = (int) (timeoutNs % 1_000_000L);
+ // Don't call wait(0, 0) - it blocks indefinitely.
+ if (timeoutMs > 0 || remainderNs > 0) {
+ mLock.wait(timeoutMs, remainderNs);
+ }
+ nowNanos = System.nanoTime();
+ verifyPreconditions();
+ if (mModeChangedEvents.size() > numModeChangedEvents) {
+ return false;
+ }
+ if (nowNanos >= surfacePostTime) {
+ for (TestSurface surface : surfaces) {
+ surface.postBuffer();
+ }
+ }
+ }
+ return true;
+ }
+
+ private void waitForStableFrameRate(TestSurface... surfaces) throws InterruptedException {
+ verifyCompatibleAndStableFrameRate(0, surfaces);
+ }
+
+ private void verifyExactAndStableFrameRate(
+ float expectedFrameRate,
+ TestSurface... surfaces) throws InterruptedException {
+ verifyFrameRate(expectedFrameRate, false, surfaces);
+ }
+
+ private void verifyCompatibleAndStableFrameRate(
+ float expectedFrameRate,
+ TestSurface... surfaces) throws InterruptedException {
+ verifyFrameRate(expectedFrameRate, true, surfaces);
+ }
+
+ // Set expectedFrameRate to 0.0 to verify only stable frame rate.
+ private void verifyFrameRate(
+ float expectedFrameRate, boolean multiplesAllowed,
+ TestSurface... surfaces) throws InterruptedException {
+ Log.i(TAG, "Verifying compatible and stable frame rate");
+ long nowNanos = System.nanoTime();
+ long gracePeriodEndTimeNanos =
+ nowNanos + FRAME_RATE_SWITCH_GRACE_PERIOD_SECONDS * 1_000_000_000L;
+ while (true) {
+ if (expectedFrameRate > FRAME_RATE_TOLERANCE) { // expectedFrameRate > 0
+ // Wait until we switch to a compatible frame rate.
+ Log.i(TAG,
+ String.format(
+ "Verifying expected frame rate: actual=%.2f, expected=%.2f",
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate,
+ expectedFrameRate));
+ if (multiplesAllowed) {
+ while (!isFrameRateMultiple(mDisplayModeRefreshRate, expectedFrameRate)
+ && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+ // Empty
+ }
+ } else {
+ while (!frameRateEquals(mDisplayRefreshRate, expectedFrameRate)
+ && !waitForEvents(gracePeriodEndTimeNanos, surfaces)) {
+ // Empty
+ }
+ }
+ nowNanos = System.nanoTime();
+ if (nowNanos >= gracePeriodEndTimeNanos) {
+ throw new FrameRateTimeoutException(expectedFrameRate,
+ multiplesAllowed ? mDisplayModeRefreshRate : mDisplayRefreshRate);
+ }
+ }
+
+ // We've switched to a compatible frame rate. Now wait for a while to see if we stay at
+ // that frame rate.
+ long endTimeNanos = nowNanos + STABLE_FRAME_RATE_WAIT_SECONDS * 1_000_000_000L;
+ while (endTimeNanos > nowNanos) {
+ int numModeChangedEvents = mModeChangedEvents.size();
+ if (waitForEvents(endTimeNanos, surfaces)) {
+ Log.i(TAG,
+ String.format("Stable frame rate %.2f verified",
+ multiplesAllowed ? mDisplayModeRefreshRate
+ : mDisplayRefreshRate));
+ return;
+ }
+ nowNanos = System.nanoTime();
+ if (mModeChangedEvents.size() > numModeChangedEvents) {
+ break;
+ }
+ }
+ }
+ }
+
+ private void verifyModeSwitchesDontChangeResolution(int fromId, int toId) {
+ assertTrue(fromId <= toId);
+ for (int eventId = fromId; eventId < toId; eventId++) {
+ Display.Mode fromMode = mModeChangedEvents.get(eventId - 1);
+ Display.Mode toMode = mModeChangedEvents.get(eventId);
+ assertTrue("Resolution change was not expected, but there was such from " + fromMode
+ + " to " + toMode + ".",
+ hasSameResolution(fromMode, toMode));
+ }
+ }
+
+ // Unfortunately, we can't just use Consumer<Api> for this, because we need to declare that it
+ // throws InterruptedException.
+ private interface TestInterface {
+ void run() throws InterruptedException;
+ }
+
+ private interface OneSurfaceTestInterface {
+ void run(TestSurface surface) throws InterruptedException;
+ }
+
+ // Runs the given test for each api, waiting for the preconditions to be satisfied before
+ // running the test. Includes retry logic when the test fails because the preconditions are
+ // violated. E.g. if we lose the SurfaceHolder's surface, or the activity is paused/resumed,
+ // we'll retry the test. The activity being intermittently paused/resumed has been observed to
+ // cause test failures in practice.
+ private void runTestsWithPreconditions(TestInterface test, String testName)
+ throws InterruptedException {
+ synchronized (mLock) {
+ for (Api api : Api.values()) {
+ Log.i(TAG, String.format("Testing %s %s", api, testName));
+ int attempts = 0;
+ boolean testPassed = false;
+ try {
+ while (!testPassed) {
+ waitForPreconditions();
+ try {
+ test.run();
+ testPassed = true;
+ } catch (PreconditionViolatedException exc) {
+ // The logic below will retry if we're below max attempts.
+ } catch (FrameRateTimeoutException exc) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ exc.printStackTrace(printWriter);
+ String stackTrace = stringWriter.toString();
+
+ // Sometimes we get a test timeout failure before we get the
+ // notification that the activity was paused, and it was the pause that
+ // caused the timeout failure. Wait for a bit to see if we get notified
+ // of a precondition violation, and if so, retry the test. Otherwise
+ // fail.
+ assertTrue(String.format(
+ "Timed out waiting for a stable and compatible frame"
+ + " rate. expected=%.2f received=%.2f."
+ + " Stack trace: " + stackTrace,
+ exc.expectedFrameRate, exc.deviceFrameRate),
+ waitForPreconditionViolation());
+ }
+
+ if (!testPassed) {
+ Log.i(TAG,
+ String.format("Preconditions violated while running the test."
+ + " Have surface? %b. Activity resumed? %b.",
+ mSurface != null,
+ mActivityState == ActivityState.RUNNING));
+ attempts++;
+ assertTrue(String.format(
+ "Exceeded %d precondition wait attempts. Giving up.",
+ PRECONDITION_WAIT_MAX_ATTEMPTS),
+ attempts < PRECONDITION_WAIT_MAX_ATTEMPTS);
+ }
+ }
+ } finally {
+ String passFailMessage = String.format(
+ "%s %s %s", testPassed ? "Passed" : "Failed", api, testName);
+ if (testPassed) {
+ Log.i(TAG, passFailMessage);
+ } else {
+ Log.e(TAG, passFailMessage);
+ }
+ }
+ }
+ }
+ }
+
+ private void runOneSurfaceTest(OneSurfaceTestInterface test) throws InterruptedException {
+ TestSurface surface = null;
+ try {
+ surface = new TestSurface(mSurfaceView.getSurfaceControl(), mSurface, "testSurface",
+ mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+
+ test.run(surface);
+ } finally {
+ if (surface != null) {
+ surface.release();
+ }
+ }
+ }
+
+ private void testSurfaceControlFrameRateCategoryInternal(int category)
+ throws InterruptedException {
+ runOneSurfaceTest((TestSurface surface) -> {
+ Log.i(TAG, "**** Running testSurfaceControlFrameRateCategory for category " + category);
+
+ float expectedFrameRate = getExpectedFrameRate(category);
+ int initialNumEvents = mModeChangedEvents.size();
+ surface.setFrameRateCategory(category);
+ verifyCompatibleAndStableFrameRate(expectedFrameRate, surface);
+ verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
+ });
+ }
+
+ public void testSurfaceControlFrameRateCategory(int category) throws InterruptedException {
+ runTestsWithPreconditions(()
+ -> testSurfaceControlFrameRateCategoryInternal(category),
+ "frame rate category=" + category);
+ }
+
+ private void testSurfaceControlFrameRateSelectionStrategyInternal(int parentStrategy)
+ throws InterruptedException {
+ Log.i(TAG,
+ "**** Running testSurfaceControlFrameRateSelectionStrategy for strategy "
+ + parentStrategy);
+ TestSurface parent = null;
+ TestSurface child = null;
+ try {
+ parent = new TestSurface(mSurfaceView.getSurfaceControl(), mSurface,
+ "testSurfaceParent", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.RED);
+ child = new TestSurface(parent.getSurfaceControl(), parent.getSurface(),
+ "testSurfaceChild", mSurfaceView.getHolder().getSurfaceFrame(),
+ /*visible=*/true, Color.BLUE);
+
+ // Test
+ Display display = getDisplay();
+ List<Float> frameRates = getRefreshRates(display.getMode(), display);
+ assumeTrue("**** SKIPPED due to frame rate override disabled",
+ SurfaceFlingerProperties.enable_frame_rate_override().orElse(true));
+ float childFrameRate = Collections.max(frameRates);
+ float parentFrameRate = childFrameRate / 2;
+ int initialNumEvents = mModeChangedEvents.size();
+ parent.setFrameRate(parentFrameRate);
+ parent.setFrameRateSelectionStrategy(parentStrategy);
+ child.setFrameRate(childFrameRate);
+
+ // Verify
+ float expectedFrameRate =
+ parentStrategy == SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN
+ ? parentFrameRate
+ : childFrameRate;
+ verifyExactAndStableFrameRate(expectedFrameRate, parent, child);
+ verifyModeSwitchesDontChangeResolution(initialNumEvents, mModeChangedEvents.size());
+ } finally {
+ if (parent != null) {
+ parent.release();
+ }
+ if (child != null) {
+ child.release();
+ }
+ }
+ }
+
+ public void testSurfaceControlFrameRateSelectionStrategy(int parentStrategy)
+ throws InterruptedException {
+ runTestsWithPreconditions(
+ () -> testSurfaceControlFrameRateSelectionStrategyInternal(parentStrategy),
+ "frame rate strategy=" + parentStrategy);
+ }
+
+ private float getExpectedFrameRate(int category) {
+ Display display = getDisplay();
+ List<Float> frameRates = getRefreshRates(display.getMode(), display);
+
+ if (category == Surface.FRAME_RATE_CATEGORY_DEFAULT) {
+ // Max due to default vote and no other frame rate specifications.
+ return Collections.max(frameRates);
+ } else if (category == Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE) {
+ return Collections.min(frameRates);
+ }
+
+ FpsRange categoryRange = convertCategory(category);
+ Optional<Float> expectedFrameRate = frameRates.stream()
+ .filter(fps -> categoryRange.includes(fps))
+ .min(Comparator.naturalOrder());
+ assumeTrue("**** testSurfaceControlFrameRateCategory SKIPPED for category " + category,
+ expectedFrameRate.isPresent());
+ return expectedFrameRate.get();
+ }
+
+ private FpsRange convertCategory(int category) {
+ switch (category) {
+ case Surface.FRAME_RATE_CATEGORY_HIGH:
+ return FRAME_RATE_CATEGORY_HIGH;
+ case Surface.FRAME_RATE_CATEGORY_NORMAL:
+ return FRAME_RATE_CATEGORY_NORMAL;
+ case Surface.FRAME_RATE_CATEGORY_LOW:
+ return FRAME_RATE_CATEGORY_LOW;
+ case Surface.FRAME_RATE_CATEGORY_DEFAULT:
+ case Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE:
+ fail("Should not get range for category=" + category);
+ }
+ return new FpsRange(0, 0);
+ }
+}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
new file mode 100644
index 000000000000..bed9cff75e1d
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2023 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.view.surfacecontroltests;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.hardware.display.DisplayManager;
+import android.os.SystemProperties;
+import android.support.test.uiautomator.UiDevice;
+import android.view.Surface;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.DisplayUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SurfaceControlTest {
+ private static final String TAG = "SurfaceControlTest";
+
+ @Rule
+ public ActivityTestRule<GraphicsActivity> mActivityRule =
+ new ActivityTestRule<>(GraphicsActivity.class);
+
+ private int mInitialRefreshRateSwitchingType;
+ private DisplayManager mDisplayManager;
+
+ @Before
+ public void setUp() throws Exception {
+ GraphicsActivity activity = mActivityRule.getActivity();
+
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // TODO(b/290634611): clean this up once SF new front end is enabled by default
+ assumeTrue(SystemProperties.getBoolean(
+ "persist.debug.sf.enable_layer_lifecycle_manager", false));
+
+ uiDevice.wakeUp();
+ uiDevice.executeShellCommand("wm dismiss-keyguard");
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS);
+
+ // Prevent DisplayManager from limiting the allowed refresh rate range based on
+ // non-app policies (e.g. low battery, user settings, etc).
+ mDisplayManager = activity.getSystemService(DisplayManager.class);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
+
+ mInitialRefreshRateSwitchingType = DisplayUtil.getRefreshRateSwitchingType(mDisplayManager);
+ mDisplayManager.setRefreshRateSwitchingType(
+ DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS);
+ }
+
+ @After
+ public void tearDown() {
+ if (mDisplayManager != null) {
+ mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ }
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateCategoryHigh() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_HIGH);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateCategoryNormal() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_NORMAL);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateCategoryLow() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_LOW);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateCategoryNoPreference() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateCategoryDefault() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateCategory(Surface.FRAME_RATE_CATEGORY_DEFAULT);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategySelf() throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_SELF);
+ }
+
+ @Test
+ public void testSurfaceControlFrameRateSelectionStrategyOverrideChildren()
+ throws InterruptedException {
+ GraphicsActivity activity = mActivityRule.getActivity();
+ activity.testSurfaceControlFrameRateSelectionStrategy(
+ SurfaceControl.FRAME_RATE_SELECTION_STRATEGY_OVERRIDE_CHILDREN);
+ }
+}
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/res/layout/activity_surface_control.xml b/tests/CtsSurfaceControlTestsStaging/src/main/res/layout/activity_surface_control.xml
new file mode 100644
index 000000000000..d6c821228c9a
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/layout/activity_surface_control.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright 2023 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.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <SurfaceView
+ android:id="@+id/surface"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-hdpi/ic_launcher.png b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000000..cde69bcccec6
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-mdpi/ic_launcher.png b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..c133a0cbd379
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xhdpi/ic_launcher.png b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..bfa42f0e7b91
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..324e72cdd748
--- /dev/null
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/FixVibrateSetting/res/values/strings.xml b/tests/CtsSurfaceControlTestsStaging/src/main/res/values/strings.xml
index 269cef3a715b..364a5c3d94fe 100644
--- a/tests/FixVibrateSetting/res/values/strings.xml
+++ b/tests/CtsSurfaceControlTestsStaging/src/main/res/values/strings.xml
@@ -1,25 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
- <string name="app_label">Fix Vibrate</string>
- <string name="title">Fix vibrate setting</string>
- <string name="current_setting">"Ringer: %1$s\nNotification: %2$s"</string>
- <string name="fix">Fix the setting</string>
- <string name="unfix">Break the setting</string>
- <string name="test">Test the setting</string>
+ <string name="app_name">SurfaceControlTests</string>
</resources>
-
diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
index 18e3aecfa832..6e59b042d6ff 100644
--- a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
+++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
@@ -31,4 +31,31 @@ interface IProtected {
@EnforcePermission("INTERNET")
void ProtectedByInternetAndReadSyncSettingsImplicitly();
+
+ @EnforcePermission("TURN_SCREEN_ON")
+ void ProtectedByTurnScreenOn();
+
+ @EnforcePermission("READ_CONTACTS")
+ void ProtectedByReadContacts();
+
+ @EnforcePermission("READ_CALENDAR")
+ void ProtectedByReadCalendar();
+
+ @EnforcePermission(allOf={"INTERNET", "VIBRATE"})
+ void ProtectedByInternetAndVibrate();
+
+ @EnforcePermission(allOf={"INTERNET", "READ_SYNC_SETTINGS"})
+ void ProtectedByInternetAndReadSyncSettings();
+
+ @EnforcePermission(anyOf={"ACCESS_WIFI_STATE", "VIBRATE"})
+ void ProtectedByAccessWifiStateOrVibrate();
+
+ @EnforcePermission(anyOf={"INTERNET", "VIBRATE"})
+ void ProtectedByInternetOrVibrate();
+
+ @RequiresNoPermission
+ void NotProtected();
+
+ @PermissionManuallyEnforced
+ void ManuallyProtected();
}
diff --git a/tests/EnforcePermission/perf-app/Android.bp b/tests/EnforcePermission/perf-app/Android.bp
new file mode 100644
index 000000000000..b494bb754370
--- /dev/null
+++ b/tests/EnforcePermission/perf-app/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "EnforcePermissionPerfTests",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+ static_libs: [
+ "EnforcePermissionTestLib",
+ "androidx.benchmark_benchmark-common",
+ "androidx.benchmark_benchmark-junit4",
+ "apct-perftests-utils",
+ "collector-device-lib",
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ data: [
+ ":EnforcePermissionTestHelper",
+ ":perfetto_artifacts",
+ "perfetto.textproto",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/tests/EnforcePermission/perf-app/AndroidManifest.xml b/tests/EnforcePermission/perf-app/AndroidManifest.xml
new file mode 100644
index 000000000000..900270d27304
--- /dev/null
+++ b/tests/EnforcePermission/perf-app/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.tests.enforcepermission.tests">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <!-- Required by perfetto -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <queries>
+ <package android:name="android.tests.enforcepermission.service" />
+ </queries>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <profileable android:shell="true" />
+ <!-- Instance of the Service within the app. This is to test performance for same-process calls. -->
+ <service android:name=".TestService" />
+ </application>
+ <instrumentation android:name="androidx.benchmark.junit4.AndroidBenchmarkRunner"
+ android:targetPackage="android.tests.enforcepermission.tests"/>
+</manifest>
diff --git a/tests/EnforcePermission/perf-app/AndroidTest.xml b/tests/EnforcePermission/perf-app/AndroidTest.xml
new file mode 100644
index 000000000000..3bc1d2dc2eeb
--- /dev/null
+++ b/tests/EnforcePermission/perf-app/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Runs EnforcePermission Perf Tests">
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file" key="perfetto.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="EnforcePermissionTestHelper.apk"/>
+ <option name="test-file-name" value="EnforcePermissionPerfTests.apk"/>
+ <option name="cleanup-apks" value="true" />
+ </target_preparer>
+
+ <option name="isolated-storage" value="false" />
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="perfetto_file_path" />
+ <option name="collect-on-run-ended-only" value="false" />
+ </metrics_collector>
+
+ <option name="test-tag" value="EnforcePermissionTests"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.tests.enforcepermission.tests"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener" />
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
+ <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
+ </test>
+</configuration>
diff --git a/tests/EnforcePermission/perf-app/perfetto.textproto b/tests/EnforcePermission/perf-app/perfetto.textproto
new file mode 100644
index 000000000000..8a3eea4ef402
--- /dev/null
+++ b/tests/EnforcePermission/perf-app/perfetto.textproto
@@ -0,0 +1,154 @@
+# Copyright (C) 2023 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.
+
+# Based on trace_config_detailed.textproto
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 1000
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 10000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers {
+ size_kb: 32768
+ fill_policy: RING_BUFFER
+}
+
+# procfs polling
+buffers {
+ size_kb: 8192
+ fill_policy: RING_BUFFER
+}
+
+# perf memory
+buffers {
+ size_kb: 65536
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.ftrace"
+ target_buffer: 0
+ ftrace_config {
+ throttle_rss_stat: true
+ # These parameters affect only the kernel trace buffer size and how
+ # frequently it gets moved into the userspace buffer defined above.
+ buffer_size_kb: 16384
+ drain_period_ms: 250
+
+ # Store certain high-volume "sched" ftrace events in a denser format
+ # (falling back to the default format if not supported by the tracer).
+ compact_sched {
+ enabled: true
+ }
+
+ # Enables symbol name resolution against /proc/kallsyms
+ symbolize_ksyms: true
+ # Parse kallsyms before acknowledging that the ftrace data source has been started. In
+ # combination with "perfetto --background-wait" as the consumer, it lets us defer the
+ # test we're tracing until after the cpu has quieted down from the cpu-bound kallsyms parsing.
+ initialize_ksyms_synchronously_for_testing: true
+ # Avoid re-parsing kallsyms on every test run, as it takes 200-500ms per run. See b/239951079
+ ksyms_mem_policy: KSYMS_RETAIN
+
+ # We need to do process tracking to ensure kernel ftrace events targeted at short-lived
+ # threads are associated correctly
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ ftrace_events: "sched/sched_process_exit"
+ ftrace_events: "sched/sched_process_free"
+
+ # Memory events
+ ftrace_events: "rss_stat"
+ ftrace_events: "ion_heap_shrink"
+ ftrace_events: "ion_heap_grow"
+ ftrace_events: "ion/ion_stat"
+ ftrace_events: "dmabuf_heap/dma_heap_stat"
+ ftrace_events: "oom_score_adj_update"
+ ftrace_events: "gpu_mem/gpu_mem_total"
+ ftrace_events: "fastrpc/fastrpc_dma_stat"
+
+ # Power events
+ ftrace_events: "power/suspend_resume"
+ ftrace_events: "power/cpu_frequency"
+ ftrace_events: "power/cpu_idle"
+ ftrace_events: "power/gpu_frequency"
+
+ # Old (kernel) LMK
+ ftrace_events: "lowmemorykiller/lowmemory_kill"
+
+ atrace_apps: "*"
+
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "bionic"
+ atrace_categories: "camera"
+ atrace_categories: "wm"
+ atrace_categories: "dalvik"
+ atrace_categories: "sched"
+ atrace_categories: "freq"
+ atrace_categories: "gfx"
+ atrace_categories: "view"
+ atrace_categories: "webview"
+ atrace_categories: "input"
+ atrace_categories: "hal"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sync"
+ atrace_categories: "workq"
+ atrace_categories: "res"
+ atrace_categories: "power"
+
+ }
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 1
+ process_stats_config {
+ proc_stats_poll_ms: 10000
+ }
+ }
+}
+
+data_sources {
+ config {
+ name: "linux.perf"
+ target_buffer: 2
+ perf_event_config {
+ timebase {
+ frequency: 80
+ }
+ callstack_sampling {
+ scope {
+ target_cmdline: "android.tests.enforcepermission.tests"
+ target_cmdline: "android.tests.enforcepermission.service"
+ target_cmdline: "system_server"
+ }
+ kernel_frames: true
+ }
+ }
+ }
+}
diff --git a/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java b/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java
new file mode 100644
index 000000000000..7cbf5674c29a
--- /dev/null
+++ b/tests/EnforcePermission/perf-app/src/android/tests/enforcepermission/tests/ServicePerfTest.java
@@ -0,0 +1,169 @@
+/**
+ * Copyright (C) 2023 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.tests.enforcepermission.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.tests.enforcepermission.IProtected;
+import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** Performance tests for EnforcePermission annotation.
+ *
+ * Permission check results are cached on the service side as it relies on
+ * PermissionManager. It means that only the first request will trigger a
+ * lookup to system_server. Subsequent requests will use the cached result. As
+ * this timing is similar to a permission check for a service hosted in
+ * system_server, we keep this cache active for the tests. The BenchmarkState
+ * used by PerfStatusReporter includes a warm-up stage. It means that the extra
+ * time taken by the first request will not be reflected in the outcome of the
+ * test.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ServicePerfTest {
+
+ private static final String TAG = "EnforcePermission.PerfTests";
+ private static final String SERVICE_PACKAGE = "android.tests.enforcepermission.service";
+ private static final String LOCAL_SERVICE_PACKAGE = "android.tests.enforcepermission.tests";
+ private static final int SERVICE_TIMEOUT_SEC = 5;
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Context mContext;
+ private volatile ServiceConnection mServiceConnection;
+
+ private void bindService(Intent intent) throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mServiceConnection = new ServiceConnection();
+ assertTrue(mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE));
+ }
+
+ public void bindRemoteService() throws Exception {
+ Log.d(TAG, "bindRemoteService");
+ Intent intent = new Intent();
+ intent.setClassName(SERVICE_PACKAGE, SERVICE_PACKAGE + ".TestService");
+ bindService(intent);
+ }
+
+ public void bindLocalService() throws Exception {
+ Log.d(TAG, "bindLocalService");
+ Intent intent = new Intent();
+ intent.setClassName(LOCAL_SERVICE_PACKAGE, SERVICE_PACKAGE + ".TestService");
+ bindService(intent);
+ }
+
+ @After
+ public void unbindTestService() throws Exception {
+ mContext.unbindService(mServiceConnection);
+ }
+
+ private static final class ServiceConnection implements android.content.ServiceConnection {
+ private volatile CompletableFuture<IProtected> mFuture = new CompletableFuture<>();
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mFuture.complete(IProtected.Stub.asInterface(service));
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mFuture = new CompletableFuture<>();
+ }
+
+ public IProtected get() {
+ try {
+ return mFuture.get(SERVICE_TIMEOUT_SEC, TimeUnit.SECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ throw new RuntimeException("Unable to reach TestService: " + e.toString());
+ }
+ }
+ }
+
+ @Test
+ public void testAnnotatedPermission() throws Exception {
+ bindRemoteService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().ProtectedByInternet();
+ }
+ }
+
+ @Test
+ public void testNoPermission() throws Exception {
+ bindRemoteService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().NotProtected();
+ }
+ }
+
+ @Test
+ public void testManuallyProtected() throws Exception {
+ bindRemoteService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().ManuallyProtected();
+ }
+ }
+
+ @Test
+ public void testAnnotatedPermissionLocal()
+ throws Exception {
+ bindLocalService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().ProtectedByInternet();
+ }
+ }
+
+ @Test
+ public void testNoPermissionLocal() throws Exception {
+ bindLocalService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().NotProtected();
+ }
+ }
+
+ @Test
+ public void testManuallyProtectedLocal() throws Exception {
+ bindLocalService();
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ mServiceConnection.get().ManuallyProtected();
+ }
+ }
+}
diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp
index a4ac1d7c6134..787821546018 100644
--- a/tests/EnforcePermission/service-app/Android.bp
+++ b/tests/EnforcePermission/service-app/Android.bp
@@ -21,6 +21,14 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
+android_library {
+ name: "EnforcePermissionTestLib",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+}
+
android_test_helper_app {
name: "EnforcePermissionTestHelper",
srcs: [
diff --git a/tests/EnforcePermission/service-app/AndroidManifest.xml b/tests/EnforcePermission/service-app/AndroidManifest.xml
index ddafe15ab88f..eba1230ff7a2 100644
--- a/tests/EnforcePermission/service-app/AndroidManifest.xml
+++ b/tests/EnforcePermission/service-app/AndroidManifest.xml
@@ -15,6 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.tests.enforcepermission.service">
+
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+
<application>
<service
android:name=".TestService"
diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java
index 7879a1214c01..0f083c994738 100644
--- a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java
@@ -18,13 +18,21 @@ package android.tests.enforcepermission.service;
import android.annotation.EnforcePermission;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.os.PermissionEnforcer;
import android.tests.enforcepermission.INested;
import android.util.Log;
public class NestedTestService extends Service {
private static final String TAG = "EnforcePermission.NestedTestService";
+ private INested.Stub mBinder;
+
+ @Override
+ public void onCreate() {
+ mBinder = new Stub(this);
+ }
@Override
public IBinder onBind(Intent intent) {
@@ -32,7 +40,12 @@ public class NestedTestService extends Service {
return mBinder;
}
- private final INested.Stub mBinder = new INested.Stub() {
+ private static class Stub extends INested.Stub {
+
+ Stub(Context context) {
+ super(PermissionEnforcer.fromContext(context));
+ }
+
@Override
@EnforcePermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
public void ProtectedByAccessNetworkState() {
@@ -44,5 +57,5 @@ public class NestedTestService extends Service {
public void ProtectedByReadSyncSettings() {
ProtectedByReadSyncSettings_enforcePermission();
}
- };
+ }
}
diff --git a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java
index e9b897db1294..8b809cf41c3e 100644
--- a/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java
@@ -17,11 +17,13 @@
package android.tests.enforcepermission.service;
import android.annotation.EnforcePermission;
+import android.annotation.RequiresNoPermission;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.os.PermissionEnforcer;
import android.os.RemoteException;
import android.tests.enforcepermission.INested;
import android.tests.enforcepermission.IProtected;
@@ -36,9 +38,11 @@ public class TestService extends Service {
private static final String TAG = "EnforcePermission.TestService";
private volatile ServiceConnection mNestedServiceConnection;
+ private IProtected.Stub mBinder;
@Override
public void onCreate() {
+ mBinder = new Stub(this);
mNestedServiceConnection = new ServiceConnection();
Intent intent = new Intent(this, NestedTestService.class);
boolean bound = bindService(intent, mNestedServiceConnection, Context.BIND_AUTO_CREATE);
@@ -78,7 +82,12 @@ public class TestService extends Service {
return mBinder;
}
- private final IProtected.Stub mBinder = new IProtected.Stub() {
+ private class Stub extends IProtected.Stub {
+
+ Stub(Context context) {
+ super(PermissionEnforcer.fromContext(context));
+ }
+
@Override
@EnforcePermission(android.Manifest.permission.INTERNET)
public void ProtectedByInternet() {
@@ -105,7 +114,6 @@ public class TestService extends Service {
ProtectedByInternetAndAccessNetworkStateImplicitly_enforcePermission();
mNestedServiceConnection.get().ProtectedByAccessNetworkState();
-
}
@Override
@@ -115,5 +123,65 @@ public class TestService extends Service {
mNestedServiceConnection.get().ProtectedByReadSyncSettings();
}
- };
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.TURN_SCREEN_ON)
+ public void ProtectedByTurnScreenOn() {
+ ProtectedByTurnScreenOn_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_CONTACTS)
+ public void ProtectedByReadContacts() {
+ ProtectedByReadContacts_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_CALENDAR)
+ public void ProtectedByReadCalendar() {
+ ProtectedByReadCalendar_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(allOf = {
+ android.Manifest.permission.INTERNET,
+ android.Manifest.permission.VIBRATE})
+ public void ProtectedByInternetAndVibrate() {
+ ProtectedByInternetAndVibrate_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(allOf = {
+ android.Manifest.permission.INTERNET,
+ android.Manifest.permission.READ_SYNC_SETTINGS})
+ public void ProtectedByInternetAndReadSyncSettings() {
+ ProtectedByInternetAndReadSyncSettings_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(anyOf = {
+ android.Manifest.permission.ACCESS_WIFI_STATE,
+ android.Manifest.permission.VIBRATE})
+ public void ProtectedByAccessWifiStateOrVibrate() {
+ ProtectedByAccessWifiStateOrVibrate_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(anyOf = {
+ android.Manifest.permission.INTERNET,
+ android.Manifest.permission.VIBRATE})
+ public void ProtectedByInternetOrVibrate() {
+ ProtectedByInternetOrVibrate_enforcePermission();
+ }
+
+ @Override
+ @RequiresNoPermission
+ public void NotProtected() {
+ }
+
+ @Override
+ public void ManuallyProtected() {
+ enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "access denied");
+ }
+ }
}
diff --git a/tests/EnforcePermission/test-app/AndroidManifest.xml b/tests/EnforcePermission/test-app/AndroidManifest.xml
index 4a0c6a86628f..8bd05d7350e5 100644
--- a/tests/EnforcePermission/test-app/AndroidManifest.xml
+++ b/tests/EnforcePermission/test-app/AndroidManifest.xml
@@ -16,9 +16,20 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.tests.enforcepermission.tests">
- <!-- Expected for the tests (not actually used) -->
+ <!-- Expected permissions for the tests (not actually used). These
+ are granted automatically at runtime by Tradefed (see
+ GrantPermissionPreparer). -->
+ <!-- normal -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+ <!-- normal|appops -->
+ <uses-permission android:name="android.permission.TURN_SCREEN_ON" />
+ <!-- dangerous -->
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <!-- Used by the tests to activate/deactivate AppOps -->
+ <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES" />
+ <uses-permission android:name="android.permission.MANAGE_APPOPS" />
<queries>
<package android:name="android.tests.enforcepermission.service" />
diff --git a/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
index d2a4a037f125..e09097cd2a04 100644
--- a/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
+++ b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
@@ -21,11 +21,13 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.tests.enforcepermission.IProtected;
import android.util.Log;
@@ -126,4 +128,61 @@ public class ServiceTest {
throws RemoteException {
mServiceConnection.get().ProtectedByInternetAndReadSyncSettingsImplicitly();
}
+
+ @Test
+ public void testAppOpPermissionGranted_succeeds() throws RemoteException {
+ AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+ appOpsManager.setUidMode(AppOpsManager.OP_TURN_SCREEN_ON,
+ Process.myUid(), AppOpsManager.MODE_ALLOWED);
+
+ mServiceConnection.get().ProtectedByTurnScreenOn();
+ }
+
+ @Test
+ public void testAppOpPermissionDenied_fails() throws RemoteException {
+ AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+ appOpsManager.setUidMode(AppOpsManager.OP_TURN_SCREEN_ON,
+ Process.myUid(), AppOpsManager.MODE_ERRORED);
+
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByTurnScreenOn());
+ assertThat(ex.getMessage(), containsString("TURN_SCREEN_ON"));
+ }
+
+ @Test
+ public void testRuntimePermissionGranted_succeeds() throws RemoteException {
+ mServiceConnection.get().ProtectedByReadContacts();
+ }
+
+ @Test
+ public void testRuntimePermissionDenied_fails() throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByReadCalendar());
+ assertThat(ex.getMessage(), containsString("READ_CALENDAR"));
+ }
+
+ @Test
+ public void testAllOfPermissionGranted_succeeds() throws RemoteException {
+ mServiceConnection.get().ProtectedByInternetAndReadSyncSettings();
+ }
+
+ @Test
+ public void testAllOfPermissionDenied_fails() throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByInternetAndVibrate());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ }
+
+ @Test
+ public void testAnyOfPermissionGranted_succeeds() throws RemoteException {
+ mServiceConnection.get().ProtectedByInternetOrVibrate();
+ }
+
+ @Test
+ public void testAnyOfPermissionDenied_fails() throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByAccessWifiStateOrVibrate());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ assertThat(ex.getMessage(), containsString("ACCESS_WIFI_STATE"));
+ }
}
diff --git a/tests/FixVibrateSetting/Android.bp b/tests/FixVibrateSetting/Android.bp
deleted file mode 100644
index bd7c701026ba..000000000000
--- a/tests/FixVibrateSetting/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
- // 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"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_app {
- name: "FixVibrateSetting",
- srcs: ["**/*.java"],
- sdk_version: "current",
- certificate: "platform",
-}
diff --git a/tests/FixVibrateSetting/AndroidManifest.xml b/tests/FixVibrateSetting/AndroidManifest.xml
deleted file mode 100644
index c2d5918a4681..000000000000
--- a/tests/FixVibrateSetting/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.fixvibratesetting">
- <uses-permission android:name="android.permission.VIBRATE"/>
-
- <application>
- <activity android:name="FixVibrateSetting"
- android:label="@string/app_label"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/FixVibrateSetting/OWNERS b/tests/FixVibrateSetting/OWNERS
deleted file mode 100644
index cc63ceb2c7ad..000000000000
--- a/tests/FixVibrateSetting/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/vibrator/OWNERS
diff --git a/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png b/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png
deleted file mode 100644
index 37c8853a4694..000000000000
--- a/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png
+++ /dev/null
Binary files differ
diff --git a/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png b/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png
deleted file mode 100644
index be00f470ad6a..000000000000
--- a/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png
+++ /dev/null
Binary files differ
diff --git a/tests/FixVibrateSetting/res/layout/fix_vibrate.xml b/tests/FixVibrateSetting/res/layout/fix_vibrate.xml
deleted file mode 100644
index c505e653e550..000000000000
--- a/tests/FixVibrateSetting/res/layout/fix_vibrate.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <TextView android:id="@+id/current_setting"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:layout_marginTop="30dp"
- android:layout_marginBottom="50dp"
- android:paddingLeft="8dp"
- android:paddingTop="4dp"
- android:textSize="20sp"
- android:textColor="#ffffffff"
- />
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal"
- >
- <Button android:id="@+id/fix"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fix"
- />
-
- <Button android:id="@+id/unfix"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/unfix"
- />
- </LinearLayout>
-
- <Button android:id="@+id/test"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="@string/test"
- />
-
-</LinearLayout>
-
diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
deleted file mode 100644
index 761efe4a8484..000000000000
--- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 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.fixvibratesetting;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.os.Bundle;
-
-public class FixVibrateSetting extends Activity implements View.OnClickListener
-{
- AudioManager mAudioManager;
- NotificationManager mNotificationManager;
- TextView mCurrentSetting;
- View mFix;
- View mUnfix;
- View mTest;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- setContentView(R.layout.fix_vibrate);
-
- mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
- mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
-
- mCurrentSetting = (TextView)findViewById(R.id.current_setting);
-
- mFix = findViewById(R.id.fix);
- mFix.setOnClickListener(this);
-
- mUnfix = findViewById(R.id.unfix);
- mUnfix.setOnClickListener(this);
-
- mTest = findViewById(R.id.test);
- mTest.setOnClickListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- update();
- }
-
- private String getSettingValue(int vibrateType) {
- int setting = mAudioManager.getVibrateSetting(vibrateType);
- switch (setting) {
- case AudioManager.VIBRATE_SETTING_OFF:
- return "off";
- case AudioManager.VIBRATE_SETTING_ON:
- return "on";
- case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
- return "silent-only";
- default:
- return "unknown";
- }
- }
-
- public void onClick(View v) {
- if (v == mFix) {
- fix();
- update();
- } else if (v == mUnfix) {
- unfix();
- update();
- } else if (v == mTest) {
- test();
- update();
- }
- }
-
- private void update() {
- String ringer = getSettingValue(AudioManager.VIBRATE_TYPE_RINGER);
- String notification = getSettingValue(AudioManager.VIBRATE_TYPE_NOTIFICATION);
- String text = getString(R.string.current_setting, ringer, notification);
- mCurrentSetting.setText(text);
- }
-
- private void fix() {
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
- AudioManager.VIBRATE_SETTING_ON);
- }
-
- private void unfix() {
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
- AudioManager.VIBRATE_SETTING_OFF);
- }
-
- private void test() {
- Intent intent = new Intent(this, FixVibrateSetting.class);
- PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
-
- Notification n = new Notification.Builder(this)
- .setSmallIcon(R.drawable.stat_sys_warning)
- .setTicker("Test notification")
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Test notification")
- .setContentText("Test notification")
- .setContentIntent(pending)
- .setVibrate(new long[] { 0, 700, 500, 1000 })
- .setAutoCancel(true)
- .build();
-
- mNotificationManager.notify(1, n);
- }
-}
-
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
new file mode 100644
index 000000000000..9eeec7c8ddda
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsOther",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ package_name: "com.android.server.wm.flicker",
+ instrumentation_target_package: "com.android.server.wm.flicker",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/manifests/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
index 1a34d9ea0f83..f867ffb679c5 100644
--- a/tests/FlickerTests/manifests/AndroidManifest.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.server.wm.flicker">
+ package="com.android.server.wm.flick">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
<!-- Read and write traces from external storage -->
@@ -44,8 +44,12 @@
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
<!-- ActivityOptions.makeCustomTaskAnimation() -->
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
- <!-- Allow the test to write directly to /sdcard/ -->
- <application android:requestLegacyExternalStorage="true" android:largeHeap="true">
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
<uses-library android:name="android.test.runner"/>
<uses-library android:name="androidx.window.extensions" android:required="false"/>
@@ -55,4 +59,9 @@
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
</manifest>
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..439cf136c220
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml
index 47749b8133b1..4bd9ca049f55 100644
--- a/tests/FlickerTests/manifests/AndroidManifestOther.xml
+++ b/tests/FlickerTests/ActivityEmbedding/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
index 45176448a9f4..6209a0838d9b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
@@ -16,9 +16,9 @@
package com.android.server.wm.flicker.activityembedding
-import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.Before
@@ -36,10 +36,8 @@ abstract class ActivityEmbeddingTestBase(flicker: LegacyFlickerTest) : BaseTest(
/** Asserts the background animation layer is never visible during bounds change transition. */
@Presubmit
@Test
- fun backgroundLayerNeverVisible() {
+ open fun backgroundLayerNeverVisible() {
val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
- flicker.assertLayers {
- isInvisible(backgroundColorLayer)
- }
+ flicker.assertLayers { isInvisible(backgroundColorLayer) }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
index 0c36c59a8a83..0c36c59a8a83 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
index cfc0d8b5f33b..955e801e7e0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/layoutchange/HorizontalSplitChangeRatioTest.kt
@@ -22,9 +22,10 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,9 +36,8 @@ import org.junit.runners.Parameterized
* Test changing split ratio at runtime on a horizona split.
*
* Setup: Launch A|B in horizontal split with B being the secondary activity, by default A and B
- * windows are equal in size. B is on the top and A is on the bottom.
- * Transitions:
- * Change the split ratio to A:B=0.7:0.3, expect bounds change for both A and B.
+ * windows are equal in size. B is on the top and A is on the bottom. Transitions: Change the split
+ * ratio to A:B=0.7:0.3, expect bounds change for both A and B.
*
* To run this test: `atest FlickerTestsOther:HorizontalSplitChangeRatioTest`
*/
@@ -46,7 +46,7 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
- ActivityEmbeddingTestBase(flicker) {
+ ActivityEmbeddingTestBase(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit = {
setup {
@@ -54,18 +54,24 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
testApp.launchViaIntent(wmHelper)
testApp.launchSecondaryActivityHorizontally(wmHelper)
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds
- ?: error("Display not found")
- }
- transitions {
- testApp.changeSecondaryActivityRatio(wmHelper)
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
}
+ transitions { testApp.changeSecondaryActivityRatio(wmHelper) }
teardown {
tapl.goHome()
testApp.exit(wmHelper)
}
}
+ @FlakyTest(bugId = 293075402)
+ @Test
+ override fun backgroundLayerNeverVisible() = super.backgroundLayerNeverVisible()
+
+ @FlakyTest(bugId = 293075402)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
/** Assert the Main activity window is always visible. */
@Presubmit
@Test
@@ -85,7 +91,8 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
@Test
fun secondaryActivityWindowIsAlwaysVisible() {
flicker.assertWm {
- isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) }
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
}
/** Assert the Secondary activity window is always visible. */
@@ -101,15 +108,17 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
fun secondaryActivityAdjustsHeightRuntime() {
flicker.assertLayersStart {
val topLayerRegion =
- this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
val bottomLayerRegion =
- this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(topLayerRegion.region.height).isEqual(bottomLayerRegion.region.height)
+ .that(topLayerRegion.region.height)
+ .isEqual(bottomLayerRegion.region.height)
check { "width" }
- .that(topLayerRegion.region.width).isEqual(bottomLayerRegion.region.width)
+ .that(topLayerRegion.region.width)
+ .isEqual(bottomLayerRegion.region.width)
topLayerRegion.notOverlaps(bottomLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds)
@@ -117,20 +126,20 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
flicker.assertLayersEnd {
val topLayerRegion =
- this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
val bottomLayerRegion =
- this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(topLayerRegion.region.height).isLower(bottomLayerRegion.region.height)
+ .that(topLayerRegion.region.height)
+ .isLower(bottomLayerRegion.region.height)
check { "height" }
- .that(
- topLayerRegion.region.height / 0.3f -
- bottomLayerRegion.region.height / 0.7f)
- .isLower(0.1f)
+ .that(topLayerRegion.region.height / 0.3f - bottomLayerRegion.region.height / 0.7f)
+ .isLower(0.1f)
check { "width" }
- .that(topLayerRegion.region.width).isEqual(bottomLayerRegion.region.width)
+ .that(topLayerRegion.region.width)
+ .isEqual(bottomLayerRegion.region.width)
topLayerRegion.notOverlaps(bottomLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
topLayerRegion.plus(bottomLayerRegion.region).coversExactly(startDisplayBounds)
@@ -140,6 +149,7 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
+
/**
* Creates the test configurations.
*
@@ -150,4 +160,4 @@ class HorizontalSplitChangeRatioTest(flicker: LegacyFlickerTest) :
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index b0ae7383cf61..ce9c337ff9bd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -16,13 +16,13 @@
package com.android.server.wm.flicker.activityembedding.open
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
@@ -70,6 +70,8 @@ class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest
@Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
+ @FlakyTest(bugId = 291575593) override fun entireScreenCovered() {}
+
@Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
@Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}
@@ -131,6 +133,7 @@ class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
+
/**
* Creates the test configurations.
*
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index 48edf6ddeba6..59ff0c65c4fc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -88,14 +88,20 @@ class OpenActivityEmbeddingPlaceholderSplitTest(flicker: LegacyFlickerTest) :
flicker.assertWm {
notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
.then()
- .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ .isAppWindowInvisible(
+ ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT,
+ isOptional = true
+ )
.then()
.isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
}
flicker.assertWm {
notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
.then()
- .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ .isAppWindowInvisible(
+ ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT,
+ isOptional = true
+ )
.then()
.isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 365782012c54..365782012c54 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
index 5551aa6b2fea..9f9fc23081c5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -18,7 +18,6 @@ package com.android.server.wm.flicker.activityembedding.open
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
-import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 6432f1f10cf1..30e833f433a8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -16,16 +16,14 @@
package com.android.server.wm.flicker.activityembedding.open
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.datatypes.Rect
-import android.tools.common.datatypes.Region
import android.tools.common.flicker.subject.region.RegionSubject
-import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
@@ -40,8 +38,8 @@ import org.junit.runners.Parameterized
*
* Setup: Launch Activity A in fullscreen.
*
- * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and
- * finishes itself, end up in split A|B.
+ * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes
+ * itself, end up in split A|B.
*
* To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest`
*/
@@ -55,12 +53,10 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
tapl.setExpectedRotationCheckEnabled(false)
testApp.launchViaIntent(wmHelper)
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds
- ?: error("Can't get display bounds")
- }
- transitions {
- testApp.launchTrampolineActivity(wmHelper)
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
}
+ transitions { testApp.launchTrampolineActivity(wmHelper) }
teardown {
tapl.goHome()
testApp.exit(wmHelper)
@@ -88,9 +84,7 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
@Presubmit
@Test
fun mainActivityWindowAlwaysVisible() {
- flicker.assertWm {
- isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- }
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
}
// TODO(b/289140963): After this is fixed, assert the main Activity window is visible
@@ -99,12 +93,8 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
@Presubmit
@Test
fun mainActivityLayerAlwaysVisible() {
- flicker.assertLayersStart {
- isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- }
- flicker.assertLayersEnd {
- isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- }
+ flicker.assertLayersStart { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ flicker.assertLayersEnd { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
}
/** Secondary activity is launched from the trampoline activity. */
@@ -113,10 +103,13 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
fun secondaryActivityWindowLaunchedFromTrampoline() {
flicker.assertWm {
notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- .then()
- .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- .then()
- .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowInvisible(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
+ isOptional = true
+ )
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
}
}
@@ -126,8 +119,8 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
fun secondaryActivityLayerLaunchedFromTrampoline() {
flicker.assertLayers {
isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- .then()
- .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
}
}
@@ -137,73 +130,85 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
fun mainActivityWindowGoesFromFullscreenToSplit() {
flicker.assertWm {
this.invoke("mainActivityStartsInFullscreen") {
- it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
.coversExactly(startDisplayBounds)
- }
- // Begin of transition.
- .then()
- .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- .then()
- .invoke("mainAndSecondaryInSplit") {
- val mainActivityRegion =
- RegionSubject(
- it.visibleRegion(
- ActivityEmbeddingAppHelper
- .MAIN_ACTIVITY_COMPONENT).region,
- it.timestamp)
- val secondaryActivityRegion =
- RegionSubject(
- it.visibleRegion(
- ActivityEmbeddingAppHelper
- .SECONDARY_ACTIVITY_COMPONENT).region,
- it.timestamp)
- check { "height" }
- .that(mainActivityRegion.region.height)
- .isEqual(secondaryActivityRegion.region.height)
- check { "width" }
- .that(mainActivityRegion.region.width)
- .isEqual(secondaryActivityRegion.region.width)
- mainActivityRegion
- .plus(secondaryActivityRegion.region)
- .coversExactly(startDisplayBounds)
- }
+ }
+ // Begin of transition.
+ .then()
+ .isAppWindowInvisible(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
+ isOptional = true
+ )
+ .then()
+ .invoke("mainAndSecondaryInSplit") {
+ val mainActivityRegion =
+ RegionSubject(
+ it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .region,
+ it.timestamp
+ )
+ val secondaryActivityRegion =
+ RegionSubject(
+ it.visibleRegion(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT
+ )
+ .region,
+ it.timestamp
+ )
+ check { "height" }
+ .that(mainActivityRegion.region.height)
+ .isEqual(secondaryActivityRegion.region.height)
+ check { "width" }
+ .that(mainActivityRegion.region.width)
+ .isEqual(secondaryActivityRegion.region.width)
+ mainActivityRegion
+ .plus(secondaryActivityRegion.region)
+ .coversExactly(startDisplayBounds)
+ }
}
}
@FlakyTest(bugId = 290736037)
/** Main activity should go from fullscreen to being a split with secondary activity. */
- @Presubmit
@Test
fun mainActivityLayerGoesFromFullscreenToSplit() {
flicker.assertLayers {
this.invoke("mainActivityStartsInFullscreen") {
- it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
.coversExactly(startDisplayBounds)
- }
- .then()
- .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- .then()
- .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ .then()
+ .isInvisible(
+ ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
+ isOptional = true
+ )
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
}
flicker.assertLayersEnd {
- val leftLayerRegion = visibleRegion(
- ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val leftLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightLayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
// Compare dimensions of two splits, given we're using default split attributes,
// both activities take up the same visible size on the display.
check { "height" }
- .that(leftLayerRegion.region.height)
- .isEqual(rightLayerRegion.region.height)
+ .that(leftLayerRegion.region.height)
+ .isEqual(rightLayerRegion.region.height)
check { "width" }
- .that(leftLayerRegion.region.width)
- .isEqual(rightLayerRegion.region.width)
+ .that(leftLayerRegion.region.width)
+ .isEqual(rightLayerRegion.region.width)
leftLayerRegion.notOverlaps(rightLayerRegion.region)
// Layers of two activities sum to be fullscreen size on display.
leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
}
}
+ @FlakyTest(bugId = 288591571)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
companion object {
/** {@inheritDoc} */
private var startDisplayBounds = Rect.EMPTY
@@ -218,4 +223,4 @@ class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbedding
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
new file mode 100644
index 000000000000..12a57d52491a
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding.pip
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary Activity into Picture-In-Picture mode.
+ *
+ * Setup: Start from a split A|B. Transition: B enters PIP, observe the window first goes fullscreen
+ * then shrink to the bottom right corner on screen.
+ *
+ * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SecondaryActivityEnterPipTest(flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ transitions { testApp.secondaryActivityEnterPip(wmHelper) }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /** We expect the background layer to be visible during this transition. */
+ @Presubmit @Test override fun backgroundLayerNeverVisible() {}
+
+ /** Main and secondary activity start from a split each taking half of the screen. */
+ @Presubmit
+ @Test
+ fun layersStartFromEqualSplit() {
+ flicker.assertLayersStart {
+ val leftLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val rightLayerRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ // Compare dimensions of two splits, given we're using default split attributes,
+ // both activities take up the same visible size on the display.
+ check { "height" }
+ .that(leftLayerRegion.region.height)
+ .isEqual(rightLayerRegion.region.height)
+ check { "width" }
+ .that(leftLayerRegion.region.width)
+ .isEqual(rightLayerRegion.region.width)
+ leftLayerRegion.notOverlaps(rightLayerRegion.region)
+ leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /** Main Activity is visible throughout the transition and becomes fullscreen. */
+ @Presubmit
+ @Test
+ fun mainActivityWindowBecomesFullScreen() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ flicker.assertWmEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /** Main Activity is visible throughout the transition and becomes fullscreen. */
+ @Presubmit
+ @Test
+ fun mainActivityLayerBecomesFullScreen() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(TRANSITION_SNAPSHOT)
+ .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT, isOptional = true)
+ }
+ flicker.assertLayersEnd {
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Secondary Activity is visible throughout the transition and shrinks to the bottom right
+ * corner.
+ */
+ @Presubmit
+ @Test
+ fun secondaryWindowShrinks() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ flicker.assertWmEnd {
+ val pipWindowRegion =
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check { "height" }
+ .that(pipWindowRegion.region.height)
+ .isLower(startDisplayBounds.height / 2)
+ check { "width" }.that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ /** During the transition Secondary Activity shrinks to the bottom right corner. */
+ @Presubmit
+ @Test
+ fun secondaryLayerShrinks() {
+ flicker.assertLayers {
+ val pipLayerList = layers {
+ ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
+ }
+ pipLayerList.zipWithNext { previous, current ->
+ if (startDisplayBounds.width > startDisplayBounds.height) {
+ // Only verify when the display is landscape, because otherwise the final pip
+ // window can be to the left of the original secondary activity.
+ current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+ }
+ current.screenBounds.overlaps(previous.screenBounds.region)
+ current.screenBounds.notBiggerThan(previous.screenBounds.region)
+ }
+ }
+ flicker.assertLayersEnd {
+ val pipRegion = visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ check { "height" }.that(pipRegion.region.height).isLower(startDisplayBounds.height / 2)
+ check { "width" }.that(pipRegion.region.width).isLower(startDisplayBounds.width)
+ }
+ }
+
+ /** The secondary layer should never jump to the left. */
+ @Presubmit
+ @Test
+ fun secondaryLayerNotJumpToLeft() {
+ flicker.assertLayers {
+ invoke("secondaryLayerNotJumpToLeft") {
+ val secondaryVisibleRegion =
+ it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ if (secondaryVisibleRegion.region.isNotEmpty) {
+ check { "left" }
+ .that(secondaryVisibleRegion.region.bounds.left)
+ .isGreater(0)
+ }
+ }
+ }
+ }
+
+ /**
+ * The pip overlay layer should cover exactly the secondary activity layer when both are
+ * visible.
+ */
+ @Presubmit
+ @Test
+ fun pipContentOverlayLayerCoversExactlySecondaryLayer() {
+ flicker.assertLayers {
+ isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .then()
+ .isVisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .invoke("pipContentOverlayLayerCoversExactlySecondaryLayer") {
+ val overlayVisibleRegion =
+ it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ val secondaryVisibleRegion =
+ it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region)
+ }
+ .then()
+ .isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // Expected for the main activity to become invisible for 1-2 frames because the snapshot
+ // covers it.
+ flicker.assertLayers {
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ )
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
index 4f7d8a474da1..e8389d1917d8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -23,9 +23,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import com.android.server.wm.flicker.rotation.RotationTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
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
new file mode 100644
index 000000000000..1123c5bb6ea8
--- /dev/null
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rotation/RotationTransition.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding.rotation
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.Test
+
+/** Base class for app rotation tests */
+abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected abstract val testApp: StandardAppHelper
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { testApp.exit(wmHelper) }
+ transitions { this.setRotation(flicker.scenario.endRotation) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher("", "SecondaryHomeHandle")
+ )
+ )
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the start of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_StartingPos() {
+ flicker.assertLayersStart {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ /** Checks that [testApp] layer covers the entire screen at the end of the transition */
+ @Presubmit
+ @Test
+ open fun appLayerRotates_EndingPos() {
+ flicker.assertLayersEnd {
+ this.entry.displays.map { display ->
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
+ }
+ }
+ }
+
+ override fun cujCompleted() {
+ super.cujCompleted()
+ appLayerRotates_StartingPos()
+ appLayerRotates_EndingPos()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
index 6be78f829b34..6be78f829b34 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/rtl/RTLStartSecondaryWithPlaceholderTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index 87231c86ef19..576eec847066 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -24,10 +24,14 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.toFlickerComponent
-import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.utils.*
+import com.android.wm.shell.flicker.utils.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import com.android.wm.shell.flicker.utils.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.utils.splitScreenDividerBecomesVisible
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
@@ -35,11 +39,11 @@ import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/***
+/**
* Test entering System SplitScreen with Activity Embedding Split and another app.
*
- * Setup: Launch A|B in split and secondaryApp, return to home.
- * Transitions: Let AE Split A|B enter splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
+ * Setup: Launch A|B in split and secondaryApp, return to home. Transitions: Let AE Split A|B enter
+ * splitscreen with secondaryApp. Resulting in A|B|secondaryApp.
*
* To run this test: `atest FlickerTestsOther:EnterSystemSplitTest`
*/
@@ -47,8 +51,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
- ActivityEmbeddingTestBase(flicker) {
+class EnterSystemSplitTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) {
private val secondaryApp = SplitScreenUtils.getPrimary(instrumentation)
override val transition: FlickerBuilder.() -> Unit = {
@@ -58,16 +61,22 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
secondaryApp.launchViaIntent(wmHelper)
tapl.goHome()
wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds ?:
- error("Display not found")
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
}
transitions {
- SplitScreenUtils.enterSplit(wmHelper, tapl, device, testApp, secondaryApp)
+ SplitScreenUtils.enterSplit(
+ wmHelper,
+ tapl,
+ device,
+ testApp,
+ secondaryApp,
+ flicker.scenario.startRotation
+ )
SplitScreenUtils.waitForSplitComplete(wmHelper, testApp, secondaryApp)
}
}
@@ -80,7 +89,10 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
@Test
fun activityEmbeddingSplitLayerBecomesVisible() {
flicker.splitAppLayerBoundsIsVisibleAtEnd(
- testApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
+ testApp,
+ landscapePosLeft = tapl.isTablet,
+ portraitPosTop = false
+ )
}
@Presubmit
@@ -91,7 +103,10 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
@Test
fun secondaryLayerBecomesVisible() {
flicker.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
+ secondaryApp,
+ landscapePosLeft = !tapl.isTablet,
+ portraitPosTop = true
+ )
}
@Presubmit
@@ -100,73 +115,73 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
/**
* After the transition there should be both ActivityEmbedding activities,
- * SplitScreenPrimaryActivity and the system split divider on screen.
- * Verify the layers are in expected sizes.
+ * SplitScreenPrimaryActivity and the system split divider on screen. Verify the layers are in
+ * expected sizes.
*/
@Presubmit
@Test
fun activityEmbeddingSplitSurfaceAreEven() {
flicker.assertLayersEnd {
val leftAELayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightAELayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
val secondaryAppLayerRegion =
- visibleRegion(
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
val systemDivider = visibleRegion(SPLIT_SCREEN_DIVIDER_COMPONENT)
leftAELayerRegion
- .plus(rightAELayerRegion.region)
- .plus(secondaryAppLayerRegion.region)
- .plus(systemDivider.region)
- .coversExactly(startDisplayBounds)
+ .plus(rightAELayerRegion.region)
+ .plus(secondaryAppLayerRegion.region)
+ .plus(systemDivider.region)
+ .coversExactly(startDisplayBounds)
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAELayerRegion.region.height)
- .isEqual(rightAELayerRegion.region.height)
+ .that(leftAELayerRegion.region.height)
+ .isEqual(rightAELayerRegion.region.height)
check { "SystemSplitHeight" }
- .that(rightAELayerRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAELayerRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
// TODO(b/292283182): Remove this special case handling.
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(
- leftAELayerRegion.region.width - rightAELayerRegion.region.width))
- .isLower(2)
+ .that(Math.abs(leftAELayerRegion.region.width - rightAELayerRegion.region.width))
+ .isLower(2)
check { "SystemSplitWidth" }
- .that(Math.abs(secondaryAppLayerRegion.region.width -
- 2 * rightAELayerRegion.region.width))
- .isLower(2)
+ .that(
+ Math.abs(
+ secondaryAppLayerRegion.region.width - 2 * rightAELayerRegion.region.width
+ )
+ )
+ .isLower(2)
}
}
- /**
- * Verify the windows are in expected sizes.
- */
+ /** Verify the windows are in expected sizes. */
@Presubmit
@Test
fun activityEmbeddingSplitWindowsAreEven() {
flicker.assertWmEnd {
val leftAEWindowRegion =
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightAEWindowRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
// There's no window for the divider bar.
val secondaryAppLayerRegion =
- visibleRegion(
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
+ visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
check { "ActivityEmbeddingSplitHeight" }
- .that(leftAEWindowRegion.region.height)
- .isEqual(rightAEWindowRegion.region.height)
+ .that(leftAEWindowRegion.region.height)
+ .isEqual(rightAEWindowRegion.region.height)
check { "SystemSplitHeight" }
- .that(rightAEWindowRegion.region.height)
- .isEqual(secondaryAppLayerRegion.region.height)
+ .that(rightAEWindowRegion.region.height)
+ .isEqual(secondaryAppLayerRegion.region.height)
check { "ActivityEmbeddingSplitWidth" }
- .that(Math.abs(
- leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
- .isLower(2)
+ .that(Math.abs(leftAEWindowRegion.region.width - rightAEWindowRegion.region.width))
+ .isLower(2)
check { "SystemSplitWidth" }
- .that(Math.abs(secondaryAppLayerRegion.region.width -
- 2 * rightAEWindowRegion.region.width))
- .isLower(2)
+ .that(
+ Math.abs(
+ secondaryAppLayerRegion.region.width - 2 * rightAEWindowRegion.region.width
+ )
+ )
+ .isLower(2)
}
}
@@ -186,4 +201,4 @@ class EnterSystemSplitTest(flicker: LegacyFlickerTest) :
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto
index c9a35aca9085..c9a35aca9085 100644
--- a/tests/FlickerTests/trace_config/trace_config.textproto
+++ b/tests/FlickerTests/ActivityEmbedding/trace_config/trace_config.textproto
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 3042a053d2fa..514f89511fb1 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -24,65 +24,14 @@ package {
}
filegroup {
- name: "FlickerTestsBase-src",
- srcs: ["src/com/android/server/wm/flicker/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppClose-src",
- srcs: ["src/**/close/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsActivityEmbedding-src",
- srcs: [
- "src/**/activityembedding/*.kt",
- "src/**/activityembedding/open/*.kt",
- "src/**/activityembedding/close/*.kt",
- "src/**/activityembedding/layoutchange/*.kt",
- "src/**/activityembedding/pip/*.kt",
- "src/**/activityembedding/rotation/*.kt",
- "src/**/activityembedding/rtl/*.kt",
- "src/**/activityembedding/splitscreen/*.kt",
- ],
-}
-
-filegroup {
- name: "FlickerTestsIme-src",
- srcs: ["src/**/ime/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsAppLaunch-src",
- srcs: ["src/**/launch/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsNotification-src",
- srcs: ["src/**/notification/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsQuickswitch-src",
- srcs: ["src/**/quickswitch/*.kt"],
-}
-
-filegroup {
- name: "FlickerTestsRotation-src",
- srcs: ["src/**/rotation/*.kt"],
-}
-
-filegroup {
name: "FlickerServiceTests-src",
srcs: [
- "src/com/android/server/wm/flicker/service/**/*.kt",
+ "src/**/*",
],
}
java_defaults {
name: "FlickerTestsDefault",
- manifest: "manifests/AndroidManifest.xml",
- test_config_template: "AndroidTestTemplate.xml",
platform_apis: true,
certificate: "platform",
optimize: {
@@ -95,6 +44,7 @@ java_defaults {
"flickertestapplib",
"flickerlib",
"flickerlib-helpers",
+ "flickerlib-trace_processor_shell",
"platform-test-annotations",
"wm-flicker-common-app-helpers",
"wm-shell-flicker-utils",
@@ -105,120 +55,6 @@ java_defaults {
],
}
-android_test {
- name: "FlickerTestsOther",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestOther.xml"],
- package_name: "com.android.server.wm.flicker",
- instrumentation_target_package: "com.android.server.wm.flicker",
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
- exclude_srcs: [
- ":FlickerTestsAppClose-src",
- ":FlickerTestsIme-src",
- ":FlickerTestsAppLaunch-src",
- ":FlickerTestsQuickswitch-src",
- ":FlickerTestsRotation-src",
- ":FlickerTestsNotification-src",
- ":FlickerServiceTests-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsAppClose",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppClose.xml"],
- package_name: "com.android.server.wm.flicker.close",
- instrumentation_target_package: "com.android.server.wm.flicker.close",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppClose-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsIme",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestIme.xml"],
- package_name: "com.android.server.wm.flicker.ime",
- instrumentation_target_package: "com.android.server.wm.flicker.ime",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsIme-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsAppLaunch",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"],
- package_name: "com.android.server.wm.flicker.launch",
- instrumentation_target_package: "com.android.server.wm.flicker.launch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsAppLaunch-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsNotification",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestNotification.xml"],
- package_name: "com.android.server.wm.flicker.notification",
- instrumentation_target_package: "com.android.server.wm.flicker.notification",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsNotification-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsQuickswitch",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestQuickswitch.xml"],
- package_name: "com.android.server.wm.flicker.quickswitch",
- instrumentation_target_package: "com.android.server.wm.flicker.quickswitch",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsQuickswitch-src",
- ],
-}
-
-android_test {
- name: "FlickerTestsRotation",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestRotation.xml"],
- package_name: "com.android.server.wm.flicker.rotation",
- instrumentation_target_package: "com.android.server.wm.flicker.rotation",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerTestsRotation-src",
- ],
- exclude_srcs: [
- ":FlickerTestsActivityEmbedding-src",
- ],
-}
-
-android_test {
- name: "FlickerServiceTests",
- defaults: ["FlickerTestsDefault"],
- additional_manifests: ["manifests/AndroidManifestService.xml"],
- package_name: "com.android.server.wm.flicker.service",
- instrumentation_target_package: "com.android.server.wm.flicker.service",
- srcs: [
- ":FlickerTestsBase-src",
- ":FlickerServiceTests-src",
- ],
-}
-
java_library {
name: "wm-flicker-common-assertions",
platform_apis: true,
@@ -229,34 +65,11 @@ java_library {
"src/**/*Assertions.java",
"src/**/*Assertions.kt",
],
- exclude_srcs: [
- "**/helpers/*",
- ],
- static_libs: [
- "flickerlib",
- "flickerlib-helpers",
- "truth",
- "app-helpers-core",
- ],
-}
-
-java_library {
- name: "wm-flicker-common-app-helpers",
- platform_apis: true,
- optimize: {
- enabled: false,
- },
- srcs: [
- "**/helpers/*",
- ],
static_libs: [
- "flickertestapplib",
"flickerlib",
- "flickerlib-apphelpers",
"flickerlib-helpers",
"truth",
"app-helpers-core",
- "wm-flicker-window-extensions",
],
}
@@ -274,3 +87,9 @@ java_library {
],
installable: false,
}
+
+java_library {
+ name: "FlickerTestsBase",
+ defaults: ["FlickerTestsDefault"],
+ srcs: ["src/**/*"],
+}
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
new file mode 100644
index 000000000000..151d12f2a8ca
--- /dev/null
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsAppClose",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/AppClose/AndroidManifest.xml b/tests/FlickerTests/AppClose/AndroidManifest.xml
new file mode 100644
index 000000000000..e75e178b388c
--- /dev/null
+++ b/tests/FlickerTests/AppClose/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.close">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.close"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..4b6224efaf60
--- /dev/null
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.close/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/AppClose/OWNERS
index f7c0a87f5dac..f7c0a87f5dac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
+++ b/tests/FlickerTests/AppClose/OWNERS
diff --git a/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/AppClose/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestIme.xml b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml
index abd03af4888a..4bd9ca049f55 100644
--- a/tests/FlickerTests/manifests/AndroidManifestIme.xml
+++ b/tests/FlickerTests/AppClose/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.ime">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.ime"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index dbbc771809de..64dd44d8b6e0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,13 +16,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,12 +70,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
+class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ed930fc8c236..eb256b56193d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,13 +16,12 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -71,12 +70,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
+class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 8737edb445f1..ea025c74b8db 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/AppClose/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
@@ -24,6 +23,7 @@ import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUN
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
diff --git a/tests/FlickerTests/AppClose/trace_config/trace_config.textproto b/tests/FlickerTests/AppClose/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/AppClose/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/AppLaunch/Android.bp b/tests/FlickerTests/AppLaunch/Android.bp
new file mode 100644
index 000000000000..f33384df6e7f
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/Android.bp
@@ -0,0 +1,69 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunchCommon-src",
+ srcs: ["src/**/common/*"],
+}
+
+filegroup {
+ name: "FlickerTestsAppLaunch1-src",
+ srcs: ["src/**/OpenApp*"],
+}
+
+java_library {
+ name: "FlickerTestsAppLaunchCommon",
+ defaults: ["FlickerTestsDefault"],
+ srcs: [":FlickerTestsAppLaunchCommon-src"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+android_test {
+ name: "FlickerTestsAppLaunch1",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [":FlickerTestsAppLaunch1-src"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsAppLaunchCommon",
+ ],
+}
+
+android_test {
+ name: "FlickerTestsAppLaunch2",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ exclude_srcs: [
+ ":FlickerTestsAppLaunchCommon-src",
+ ":FlickerTestsAppLaunch1-src",
+ ],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsAppLaunchCommon",
+ ],
+}
diff --git a/tests/FlickerTests/AppLaunch/AndroidManifest.xml b/tests/FlickerTests/AppLaunch/AndroidManifest.xml
new file mode 100644
index 000000000000..b89af1a5260b
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.launch">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.launch"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..583bcb74ffc2
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.launch/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/AppLaunch/OWNERS
index 2c414a27cacb..2c414a27cacb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
+++ b/tests/FlickerTests/AppLaunch/OWNERS
diff --git a/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml
index 4cdcb903b498..4bd9ca049f55 100644
--- a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml
+++ b/tests/FlickerTests/AppLaunch/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.close">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.close"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
index 3461907f71dd..89ab41e49270 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.toFlickerComponent
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -53,11 +52,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index ae1f78a5a4ef..413767ce7618 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,14 +16,11 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
-import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
+import com.android.server.wm.flicker.launch.common.OpenAppFromIconTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -51,33 +48,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) :
- OpenAppFromLauncherTransition(flicker) {
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- if (flicker.scenario.isTablet) {
- tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- } else {
- tapl.setExpectedRotation(Rotation.ROTATION_0.value)
- }
- RemoveAllTasksButHomeRule.removeAllTasksButHome()
- }
- transitions {
- tapl
- .goHome()
- .switchToAllApps()
- .getAppIcon(testApp.launcherName)
- .launch(testApp.`package`)
- }
- teardown { testApp.exit(wmHelper) }
- }
+ OpenAppFromIconTransition(flicker) {
@FlakyTest(bugId = 240916028)
@Test
@@ -90,6 +65,7 @@ open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) :
override fun appWindowReplacesLauncherAsTopWindow() {
super.appWindowReplacesLauncherAsTopWindow()
}
+
@FlakyTest(bugId = 240916028)
@Test
override fun appWindowAsTopWindowAtEnd() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index c95c548e7221..4168bdc705f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -21,7 +21,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -34,11 +34,10 @@ import org.junit.runners.Parameterized
*
* Notes: Some default assertions are inherited [OpenAppTransition]
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) :
+class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) :
OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index e39a578fd321..9c55c98a8f4f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -24,8 +24,8 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,12 +53,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) :
+class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) :
OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index d2c38076e72d..fc6cdb1ee01e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,15 +16,15 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,12 +53,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) :
+class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) :
OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060e3370..de666dd42877 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -25,8 +24,10 @@ import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.launch.common.OpenAppFromLockscreenTransition
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -71,7 +72,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
* Checks that the [ComponentNameMatcher.NAV_BAR] layer starts invisible, becomes visible during
* unlocking animation and remains visible at the end
*/
- @Presubmit
+ @FlakyTest(bugId = 288341660)
@Test
fun navBarLayerVisibilityChanges() {
Assume.assumeFalse(flicker.scenario.isTablet)
@@ -93,7 +94,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
* Checks that the [ComponentNameMatcher.NAV_BAR] starts the transition invisible, then becomes
* visible during the unlocking animation and remains visible at the end of the transition
*/
- @Presubmit
+ @FlakyTest(bugId = 293581770)
@Test
fun navBarWindowsVisibilityChanges() {
Assume.assumeFalse(flicker.scenario.isTablet)
@@ -200,7 +201,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 251217585)
+ @FlakyTest(bugId = 285980483)
@Test
override fun focusChanges() {
super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index eec6bfde8b9f..f8a9961d1d28 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.flicker.annotation.FlickerServiceCompatible
@@ -24,8 +23,9 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,13 +55,11 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) :
- OpenAppFromLauncherTransition(flicker) {
+class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 1bdb6e717b12..0aceb354dec6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -30,6 +30,7 @@ import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.view.KeyEvent
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launch.common.OpenAppFromLauncherTransition
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
diff --git a/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
new file mode 100644
index 000000000000..f41a2a297ad9
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OpenTransferSplashscreenAppFromLauncherTransition.kt
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.TransferSplashscreenAppHelper
+import com.android.server.wm.flicker.launch.common.OpenAppFromIconTransition
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launching an app from launcher
+ *
+ * To run this test: `atest FlickerTests:OpenTransferSplashscreenAppFromLauncherTransition`
+ *
+ * Actions:
+ * ```
+ * Inherit from OpenAppFromIconColdTest, Launch an app [testApp] with an animated splash screen
+ * by clicking it's icon on all apps, and wait for transfer splash screen complete
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Verify no flickering when transfer splash screen to app window.
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenTransferSplashscreenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
+ OpenAppFromIconTransition(flicker) {
+ override val testApp = TransferSplashscreenAppHelper(instrumentation)
+
+ /**
+ * Checks that [ComponentNameMatcher.LAUNCHER] window is the top window at the start of the
+ * transition, and is replaced by [ComponentNameMatcher.SPLASH_SCREEN], then [testApp] remains
+ * visible until the end
+ */
+ @Presubmit
+ @Test
+ fun appWindowAfterSplash() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN)
+ .then()
+ .isAppWindowOnTop(testApp)
+ .isAppWindowInvisible(ComponentNameMatcher.SPLASH_SCREEN)
+ }
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() {
+ super.appWindowReplacesLauncherAsTopWindow()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowAsTopWindowAtEnd() {
+ super.appWindowAsTopWindowAtEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesTopWindow() {
+ super.appWindowBecomesTopWindow()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowBecomesVisible() {
+ super.appWindowBecomesVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appWindowIsTopWindowAtEnd() {
+ super.appWindowIsTopWindowAtEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun appLayerBecomesVisible() {
+ super.appLayerBecomesVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun cujCompleted() {
+ super.cujCompleted()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() {
+ super.navBarWindowIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest(bugId = 240916028)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index 98e3646f4083..93ca41c4181c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -31,7 +31,6 @@ import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.R
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 39c8ca089dbd..9c2899ac6087 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -19,8 +19,8 @@ package com.android.server.wm.flicker.launch
import android.app.Instrumentation
import android.app.WallpaperManager
import android.content.res.Resources
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -32,6 +32,7 @@ import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NewTasksAppHelper
@@ -121,7 +122,7 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
@FlakyTest(bugId = 265007895)
@Test
fun transitionHasColorBackground() {
- val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ val backgroundColorLayer = ComponentNameMatcher("", "animation-background")
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
flicker.assertLayers {
this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
@@ -190,6 +191,16 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
}
}
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+ )
+ }
+ }
+
companion object {
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
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
new file mode 100644
index 000000000000..802c755116af
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromIconTransition.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch.common
+
+import android.tools.common.Rotation
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+
+abstract class OpenAppFromIconTransition(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ if (flicker.scenario.isTablet) {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ } else {
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+ }
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ }
+ transitions {
+ tapl
+ .goHome()
+ .switchToAllApps()
+ .getAppIcon(testApp.appName)
+ .launch(testApp.packageName)
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
index 62fb5704b2e8..9d7096ea0e73 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLauncherTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
@@ -30,7 +30,7 @@ abstract class OpenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
@Presubmit
@Test
open fun focusChanges() {
- flicker.assertEventLog { this.focusChanges("NexusLauncherActivity", testApp.`package`) }
+ flicker.assertEventLog { this.focusChanges("NexusLauncherActivity", testApp.packageName) }
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
index 687bc1958a5a..7b088431b0bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppFromLockscreenTransition.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.Assume
@@ -46,7 +46,7 @@ abstract class OpenAppFromLockscreenTransition(flicker: LegacyFlickerTest) :
@Presubmit
@Test
open fun focusChanges() {
- flicker.assertEventLog { this.focusChanges("", testApp.`package`) }
+ flicker.assertEventLog { this.focusChanges("", testApp.packageName) }
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
index bb11be5bb520..989619e08d98 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/AppLaunch/src/com/android/server/wm/flicker/launch/common/OpenAppTransition.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.launch.common
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
diff --git a/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto b/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/AppLaunch/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/FlickerService/Android.bp b/tests/FlickerTests/FlickerService/Android.bp
new file mode 100644
index 000000000000..1a381150dfb0
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerServiceTests",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/FlickerService/AndroidManifest.xml b/tests/FlickerTests/FlickerService/AndroidManifest.xml
new file mode 100644
index 000000000000..f31e8206a28d
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.service">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.service"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..d6ae2b3a5d94
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.service/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml
index 659a745ba480..4bd9ca049f55 100644
--- a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml
+++ b/tests/FlickerTests/FlickerService/res/xml/network_security_config.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 The Android Open Source Project
~
@@ -14,11 +15,8 @@
~ limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.launch">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.launch"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
index 8242e9a31992..8242e9a31992 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/Utils.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
index 030b29269e48..b3c9c96c614c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonLandscape.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButton3ButtonLandscape :
CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_90) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
index 770da143b184..29c11a4257f6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButton3ButtonPortrait.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButton3ButtonPortrait :
CloseAppBackButton(NavBar.MODE_3BUTTON, Rotation.ROTATION_0) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
index 4b2206d78276..1bb4a253ff41 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavLandscape.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButtonGesturalNavLandscape :
CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_90) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
index 54b471e5ab28..17cb6e194ec3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppBackButtonGesturalNavPortrait.kt
@@ -31,7 +31,8 @@ import org.junit.runner.RunWith
@RunWith(FlickerServiceJUnit4ClassRunner::class)
class CloseAppBackButtonGesturalNavPortrait :
CloseAppBackButton(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) {
- @ExpectedScenarios(["APP_CLOSE_TO_HOME"])
+ // TODO: Missing CUJ (b/300078127)
+ @ExpectedScenarios(["ENTIRE_TRACE"])
@Test
override fun closeAppBackButtonTest() = super.closeAppBackButtonTest()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
index 47c25294f66e..47c25294f66e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
index b18148f97759..b18148f97759 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppHomeButton3ButtonPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
index 85430154e202..85430154e202 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
index f88963b7a341..f88963b7a341 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/flicker/CloseAppSwipeToHomeGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
index 2aaacde52547..2aaacde52547 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppBackButton.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
index 08683a3ab43d..08683a3ab43d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppHomeButton.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
index 360e11408c92..360e11408c92 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/close/scenarios/CloseAppSwipeToHome.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
index bb18770a8e7c..bb18770a8e7c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
index 1c3cc2188c7c..1c3cc2188c7c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationCold3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
index 46d36dbd6b09..46d36dbd6b09 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
index f6a668feeed6..f6a668feeed6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationColdGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
index 93200ba1b6fe..93200ba1b6fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
index f5d41d250b1e..f5d41d250b1e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarm3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
index 28f322bee130..28f322bee130 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
index beb3fae8be48..beb3fae8be48 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWarmGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
index 4adcc8bf5f0b..4adcc8bf5f0b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
index f7211e7287cf..f7211e7287cf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayApp3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
index 1ade9560d90f..1ade9560d90f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
index ea26f0867c7f..ea26f0867c7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromLockscreenNotificationWithOverlayAppGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
index 866190f78827..866190f78827 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
index a8d628328ed9..a8d628328ed9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationCold3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
index ef84c3f1177e..ef84c3f1177e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
index 5bb6b3713e9f..5bb6b3713e9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationColdGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
index 39f25e97f04c..39f25e97f04c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
index 28816bcba2a1..28816bcba2a1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarm3ButtonNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
index 94ffe4e57994..94ffe4e57994 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
index e5f5ec4a6fe8..e5f5ec4a6fe8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/flicker/OpenAppFromNotificationWarmGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
index ac4e16969bad..ac4e16969bad 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/NotificationUtils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
index 9c53ab30e8cd..9c53ab30e8cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationCold.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
index 31f55d9d2e99..31f55d9d2e99 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWarm.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
index b971555f2747..b971555f2747 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromLockscreenNotificationWithOverlayApp.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
index fed077ed0e50..fed077ed0e50 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationCold.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
index 403790e904d8..403790e904d8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/notification/scenarios/OpenAppFromNotificationWarm.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
index d611a420edcb..d611a420edcb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
index e6bcbba661f3..e6bcbba661f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsBackGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
index 2a26d633adaf..2a26d633adaf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
index 5ce714371db0..5ce714371db0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchBetweenTwoAppsForwardGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
index cff47bb8cc19..cff47bb8cc19 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavLandscape.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
index 33095a6ac078..33095a6ac078 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/flicker/QuickSwitchFromLauncherGesturalNavPortrait.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
index 93130c4680ff..a1708fe3a4b6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsBack.kt
@@ -19,6 +19,7 @@ package com.android.server.wm.flicker.service.quickswitch.scenarios
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -46,7 +47,9 @@ abstract class QuickSwitchBetweenTwoAppsBack(val rotation: Rotation = Rotation.R
tapl.setExpectedRotation(rotation.value)
tapl.setIgnoreTaskbarVisibility(true)
testApp1.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp2.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
}
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
index 4941eea12129..fcf442a4e300 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchBetweenTwoAppsForward.kt
@@ -19,6 +19,7 @@ package com.android.server.wm.flicker.service.quickswitch.scenarios
import android.app.Instrumentation
import android.tools.common.NavBar
import android.tools.common.Rotation
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -46,7 +47,9 @@ abstract class QuickSwitchBetweenTwoAppsForward(val rotation: Rotation = Rotatio
tapl.setExpectedRotation(rotation.value)
testApp1.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
testApp2.launchViaIntent(wmHelper)
+ ChangeDisplayOrientationRule.setRotation(rotation)
tapl.launchedAppState.quickSwitchToPreviousApp()
wmHelper
.StateSyncBuilder()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
index 7afe5268ede4..7afe5268ede4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/quickswitch/scenarios/QuickSwitchFromLauncher.kt
diff --git a/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto b/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
new file mode 100644
index 000000000000..057d9fcdb796
--- /dev/null
+++ b/tests/FlickerTests/IME/Android.bp
@@ -0,0 +1,78 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "FlickerTestsImeCommon-src",
+ srcs: ["src/**/common/*"],
+}
+
+filegroup {
+ name: "FlickerTestsIme1-src",
+ srcs: ["src/**/Close*"],
+}
+
+android_test {
+ name: "FlickerTestsIme",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+java_library {
+ name: "FlickerTestsImeCommon",
+ defaults: ["FlickerTestsDefault"],
+ srcs: [":FlickerTestsImeCommon-src"],
+ static_libs: ["FlickerTestsBase"],
+}
+
+android_test {
+ name: "FlickerTestsIme1",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: [":FlickerTestsIme1-src"],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsImeCommon",
+ ],
+}
+
+android_test {
+ name: "FlickerTestsIme2",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ exclude_srcs: [
+ ":FlickerTestsIme1-src",
+ ":FlickerTestsImeCommon-src",
+ ],
+ static_libs: [
+ "FlickerTestsBase",
+ "FlickerTestsImeCommon",
+ ],
+}
diff --git a/tests/FlickerTests/IME/AndroidManifest.xml b/tests/FlickerTests/IME/AndroidManifest.xml
new file mode 100644
index 000000000000..d6ca683e13f2
--- /dev/null
+++ b/tests/FlickerTests/IME/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.ime">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.ime"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..988f76f4175c
--- /dev/null
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.ime/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/IME/OWNERS
index 301fafa5309e..301fafa5309e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
+++ b/tests/FlickerTests/IME/OWNERS
diff --git a/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/IME/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/IME/res/xml/network_security_config.xml b/tests/FlickerTests/IME/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/FlickerTests/IME/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 98446c1a76b2..47a1619e0e9c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
import org.junit.FixMethodOrder
@@ -33,11 +32,10 @@ import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index b995d3df8055..48c54eabc5e2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
@@ -37,11 +36,10 @@ import org.junit.runners.Parameterized
* Test IME window closing to home transitions. To run this test: `atest
* FlickerTests:CloseImeWindowToHomeTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index 765bb4cf3067..31d5d7f837d5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import org.junit.FixMethodOrder
@@ -45,11 +44,10 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index c87217b30783..180ae5d6f097 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import org.junit.FixMethodOrder
@@ -45,12 +44,10 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) :
- BaseTest(flicker) {
+class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index c74870bef102..a0573b7b4b16 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
@@ -38,11 +37,10 @@ import org.junit.runners.Parameterized
* Test IME window closing back to app window transitions. To run this test: `atest
* FlickerTests:CloseImeWindowToAppTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 21fd590025bc..b44f1a607b05 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
@@ -24,7 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -40,11 +39,10 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val simpleApp = SimpleAppHelper(instrumentation)
private val testApp = ImeAppHelper(instrumentation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 976ac82c8bb5..976ac82c8bb5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 44bd8c8688e9..ea7c7c4276dc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -19,15 +19,13 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.common.Timestamp
-import android.tools.common.flicker.subject.exceptions.ExceptionMessageBuilder
-import android.tools.common.flicker.subject.exceptions.InvalidPropertyException
+import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.setRotation
@@ -43,11 +41,10 @@ import org.junit.runners.Parameterized
* (e.g. Launcher activity). To run this test: `atest
* FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val imeTestApp =
ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
@@ -82,47 +79,43 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Le
flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
}
+ @FlakyTest(bugId = 290767483)
@Postsubmit
@Test
fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
- // Check if the snapshot appeared during the trace
- var imeSnapshotRemovedTimestamp: Timestamp? = null
-
- val layerTrace = flicker.reader.readLayersTrace()
- val layerTraceEntries = layerTrace?.entries?.toList() ?: emptyList()
-
- layerTraceEntries.zipWithNext { prev, next ->
- val prevSnapshotLayerVisible =
- ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers)
- val nextSnapshotLayerVisible =
- ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)
-
- if (
- imeSnapshotRemovedTimestamp == null &&
- (prevSnapshotLayerVisible && !nextSnapshotLayerVisible)
- ) {
- imeSnapshotRemovedTimestamp = next.timestamp
- }
- }
-
- // if so, make an assertion
- imeSnapshotRemovedTimestamp?.let { timestamp ->
- val stateAfterSnapshot =
- layerTrace?.getEntryAt(timestamp) ?: error("State not found for $timestamp")
-
- val imeLayers =
- ComponentNameMatcher.IME.filterLayers(stateAfterSnapshot.visibleLayers.toList())
-
- require(imeLayers.isNotEmpty()) { "IME layer not found" }
- if (imeLayers.any { it.color.a != 1.0f }) {
- val errorMsgBuilder =
- ExceptionMessageBuilder()
- .setTimestamp(timestamp)
- .forInvalidProperty("IME layer alpha")
- .setExpected("is 1.0")
- .setActual("not 1.0")
- .addExtraDescription("Filter", ComponentNameMatcher.IME.toLayerIdentifier())
- throw InvalidPropertyException(errorMsgBuilder)
+ val layerTrace = flicker.reader.readLayersTrace() ?: error("Unable to read layers trace")
+
+ // Find the entries immediately after the IME snapshot has disappeared
+ val imeSnapshotRemovedEntries =
+ layerTrace.entries
+ .asSequence()
+ .zipWithNext { prev, next ->
+ if (
+ ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers) &&
+ !ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)
+ ) {
+ next
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+
+ // If we find it, make sure the IME is visible and fully animated in.
+ imeSnapshotRemovedEntries.forEach { entry ->
+ val entrySubject = LayerTraceEntrySubject(entry)
+ val imeLayerSubjects =
+ entrySubject.subjects.filter {
+ ComponentNameMatcher.IME.layerMatchesAnyOf(it.layer) && it.isVisible
+ }
+
+ entrySubject
+ .check { "InputMethod must exist and be visible" }
+ .that(imeLayerSubjects.isNotEmpty())
+ .isEqual(true)
+
+ imeLayerSubjects.forEach { imeLayerSubject ->
+ imeLayerSubject.check { "alpha" }.that(imeLayerSubject.layer.color.a).isEqual(1.0f)
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index ae05e37a84cc..05babd67758c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -24,7 +24,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.reopenAppFromOverview
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.setRotation
@@ -37,11 +36,10 @@ import org.junit.runners.Parameterized
/**
* Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index c991651a9d08..aff8e657bec0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -39,12 +38,11 @@ import org.junit.runners.Parameterized
* Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest
* FlickerTests:SwitchImeWindowsFromGestureNavTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
-open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val imeTestApp =
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 976352146fc0..4ffdcea455ba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
@@ -75,11 +74,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
private val initializeApp = ImeStateInitializeHelper(instrumentation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 7bd58252d3ba..918e1ea3bcd0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
import org.junit.FixMethodOrder
@@ -33,11 +32,10 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 0b168baa6622..6ad235ce892e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -26,7 +26,6 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.view.WindowInsets.Type.ime
import android.view.WindowInsets.Type.navigationBars
import android.view.WindowInsets.Type.statusBars
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import org.junit.Assert.assertFalse
@@ -41,12 +40,10 @@ import org.junit.runners.Parameterized
* Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
* To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) :
- BaseTest(flicker) {
+class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 7722c934d11b..f1bc125b99dc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -24,7 +24,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.WindowManagerStateHelper
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
@@ -41,11 +40,10 @@ import org.junit.runners.Parameterized
* Test IME window layer will be associated with the app task when going to the overview screen. To
* run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val imeTestApp =
ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt
index 777231e001f7..777231e001f7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/common/CommonAssertions.kt
diff --git a/tests/FlickerTests/IME/trace_config/trace_config.textproto b/tests/FlickerTests/IME/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/IME/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/Notification/Android.bp b/tests/FlickerTests/Notification/Android.bp
new file mode 100644
index 000000000000..5bed568aacd1
--- /dev/null
+++ b/tests/FlickerTests/Notification/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsNotification",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/Notification/AndroidManifest.xml b/tests/FlickerTests/Notification/AndroidManifest.xml
new file mode 100644
index 000000000000..d212c10ff414
--- /dev/null
+++ b/tests/FlickerTests/Notification/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.notification">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.notification"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index 44a824513b91..403685831be7 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -26,6 +26,15 @@
<!-- Increase trace size: 20mb for WM and 80mb for SF -->
<option name="run-command" value="cmd window tracing size 20480"/>
<option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
@@ -74,21 +83,7 @@
<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.server.wm.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.close/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.ime/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.launch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.server.wm.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/Notification/OWNERS b/tests/FlickerTests/Notification/OWNERS
new file mode 100644
index 000000000000..68c37bd82faa
--- /dev/null
+++ b/tests/FlickerTests/Notification/OWNERS
@@ -0,0 +1,2 @@
+# Android > Android OS & Apps > System UI > Internal Only > Notifications
+# https://b.corp.google.com/issues/new?component=181562&template=686802
diff --git a/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/Notification/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/Notification/res/xml/network_security_config.xml b/tests/FlickerTests/Notification/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/FlickerTests/Notification/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt
index fe9126003967..b81439e8a1d1 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/feature_split/src/com/android/apkverity/feature_x/DummyActivity.java
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/Consts.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.apkverity.feature_x;
+package com.android.server.wm.flicker.notification
-import android.app.Activity;
+import android.tools.common.traces.component.ComponentNameMatcher
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
+object Consts {
+ val IMAGE_WALLPAPER = ComponentNameMatcher("", "com.android.systemui.wallpapers.ImageWallpaper")
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index 6f5daeb22bed..620502e521c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -17,12 +17,15 @@
package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import org.junit.ClassRule
import org.junit.FixMethodOrder
@@ -47,21 +50,23 @@ import org.junit.runners.Parameterized
open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest) :
OpenAppFromNotificationColdTest(flicker) {
- override val openingNotificationsFromLockScreen = true
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- // Needs to run at start of transition,
- // so before the transition defined in super.transition
- transitions { device.wakeUp() }
-
- super.transition(this)
+ transitions {
+ device.wakeUp()
+ openAppFromLockNotification()
+ }
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
+ device.wakeUpAndGoToHomeScreen()
+ launchAppAndPostNotification()
+ clearOverview()
device.sleep()
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+
+ teardown { testApp.exit(wmHelper) }
}
/** {@inheritDoc} */
@@ -107,6 +112,21 @@ open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest)
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher.SECONDARY_HOME_HANDLE,
+ Consts.IMAGE_WALLPAPER
+ )
+ )
+ }
+ }
+
companion object {
/**
* Creates the test configurations.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index 483caa71abee..2a458ef82448 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.notification
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
@@ -25,6 +24,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.ClassRule
@@ -49,21 +49,22 @@ import org.junit.runners.Parameterized
class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
OpenAppFromNotificationWarmTest(flicker) {
- override val openingNotificationsFromLockScreen = true
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- // Needs to run at start of transition,
- // so before the transition defined in super.transition
- transitions { device.wakeUp() }
-
- super.transition(this)
+ transitions {
+ device.wakeUp()
+ openAppFromLockNotification()
+ }
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
+ launchAppAndPostNotification()
+ goHome()
device.sleep()
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+
+ teardown { testApp.exit(wmHelper) }
}
/**
@@ -139,6 +140,21 @@ class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher.SECONDARY_HOME_HANDLE,
+ Consts.IMAGE_WALLPAPER
+ )
+ )
+ }
+ }
+
companion object {
/**
* Creates the test configurations.
@@ -159,7 +175,7 @@ class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
val disableUnseenNotifFilterRule =
SettingOverrideRule(
Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- /* value= */ "0",
+ "0",
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index 4e8a697d8902..00aad2783d5d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.notification
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
@@ -25,6 +24,7 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
import org.junit.FixMethodOrder
@@ -51,17 +51,21 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker
OpenAppFromLockscreenNotificationColdTest(flicker) {
private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
- // Although we are technically still locked here, the overlay app means we should open the
- // notification shade as if we were unlocked.
- override val openingNotificationsFromLockScreen = false
-
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
+ transitions {
+ device.wakeUp()
+ // Although we are technically still locked here, the overlay app means we should
+ // open the
+ // notification shade as if we were unlocked.
+ openAppFromNotification()
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ }
setup {
device.wakeUpAndGoToHomeScreen()
-
+ launchAppAndPostNotification()
+ clearOverview()
// Launch an activity that is shown when the device is locked
showWhenLockedApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder().withFullScreenApp(showWhenLockedApp).waitForAndVerify()
@@ -70,7 +74,10 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker
wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
- teardown { showWhenLockedApp.exit(wmHelper) }
+ teardown {
+ testApp.exit(wmHelper)
+ showWhenLockedApp.exit(wmHelper)
+ }
}
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index 50dec3bf2f02..f8d78b5ddd1e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -23,7 +23,8 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.FixMethodOrder
import org.junit.Ignore
@@ -37,7 +38,6 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -47,16 +47,16 @@ open class OpenAppFromNotificationColdTest(flicker: LegacyFlickerTest) :
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this)
-
setup {
- // Close the app that posted the notification to trigger a cold start next time
- // it is open - can't just kill it because that would remove the notification.
- tapl.setExpectedRotationCheckEnabled(false)
- tapl.goHome()
- tapl.workspace.switchToOverview()
- tapl.overview.dismissAllTasks()
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(flicker.scenario.startRotation)
+ launchAppAndPostNotification()
+ clearOverview()
}
+
+ transitions { openAppFromNotification() }
+
+ teardown { testApp.exit(wmHelper) }
}
@Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 19a070b1a402..5c7ec46d6f04 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -21,12 +21,12 @@ import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTestData
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import android.view.WindowInsets
import android.view.WindowManager
-import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.NotificationAppHelper
@@ -49,7 +49,6 @@ import org.junit.runners.Parameterized
*
* To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -57,59 +56,68 @@ open class OpenAppFromNotificationWarmTest(flicker: LegacyFlickerTest) :
OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
- open val openingNotificationsFromLockScreen = false
-
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
device.wakeUpAndGoToHomeScreen()
this.setRotation(flicker.scenario.startRotation)
- testApp.launchViaIntent(wmHelper)
- wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
- testApp.postNotification(wmHelper)
- device.pressHome()
- wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ launchAppAndPostNotification()
+ goHome()
}
- transitions {
- var startY = 10
- var endY = 3 * device.displayHeight / 4
- var steps = 25
- if (openingNotificationsFromLockScreen) {
- val wm: WindowManager =
- instrumentation.context.getSystemService(WindowManager::class.java)
- ?: error("Unable to connect to WindowManager service")
- val metricInsets = wm.currentWindowMetrics.windowInsets
- val insets =
- metricInsets.getInsetsIgnoringVisibility(
- WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
- )
-
- startY = insets.top + 100
- endY = device.displayHeight / 2
- steps = 4
- }
-
- // Swipe down to show the notification shade
- val x = device.displayWidth / 2
- device.swipe(x, startY, x, endY, steps)
- device.waitForIdle(2000)
- instrumentation.uiAutomation.syncInputTransactions()
-
- // Launch the activity by clicking the notification
- val notification =
- device.wait(Until.findObject(By.text("Flicker Test Notification")), 2000L)
- notification?.click() ?: error("Notification not found")
- instrumentation.uiAutomation.syncInputTransactions()
-
- // Wait for the app to launch
- wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
- }
+ transitions { openAppFromNotification() }
teardown { testApp.exit(wmHelper) }
}
+ protected fun FlickerTestData.launchAppAndPostNotification() {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ testApp.postNotification(wmHelper)
+ }
+
+ protected fun FlickerTestData.goHome() {
+ device.pressHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(ComponentNameMatcher.NOTIFICATION_SHADE)
+ .waitForAndVerify()
+ }
+ protected fun FlickerTestData.openAppFromNotification() {
+ doOpenAppAndWait(startY = 10, endY = 3 * device.displayHeight / 4, steps = 25)
+ }
+
+ protected fun FlickerTestData.openAppFromLockNotification() {
+ val wm: WindowManager =
+ instrumentation.context.getSystemService(WindowManager::class.java)
+ ?: error("Unable to connect to WindowManager service")
+ val metricInsets = wm.currentWindowMetrics.windowInsets
+ val insets =
+ metricInsets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
+ )
+
+ doOpenAppAndWait(startY = insets.top + 100, endY = device.displayHeight / 2, steps = 4)
+ }
+
+ protected fun FlickerTestData.doOpenAppAndWait(startY: Int, endY: Int, steps: Int) {
+ // Swipe down to show the notification shade
+ val x = device.displayWidth / 2
+ device.swipe(x, startY, x, endY, steps)
+ device.waitForIdle(2000)
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Launch the activity by clicking the notification
+ val notification =
+ device.wait(Until.findObject(By.text("Flicker Test Notification")), 2000L)
+ notification?.click() ?: error("Notification not found")
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Wait for the app to launch
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ }
@Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
@Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
index 684b4b70f950..4e0e3c04ae5a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
@@ -19,26 +19,22 @@ package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
-import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
/** Base class for app launch tests */
abstract class OpenAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
- /** {@inheritDoc} */
- override val transition: FlickerBuilder.() -> Unit = {
- setup {
- tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(flicker.scenario.startRotation)
- }
- teardown { testApp.exit(wmHelper) }
+ protected fun clearOverview() {
+ // Close the app that posted the notification to trigger a cold start next time
+ // it is open - can't just kill it because that would remove the notification.
+ tapl.expectedRotationCheckEnabled = false
+ tapl.goHome()
+ tapl.workspace.switchToOverview()
+ tapl.overview.dismissAllTasks()
}
/**
diff --git a/tests/FlickerTests/Notification/trace_config/trace_config.textproto b/tests/FlickerTests/Notification/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/Notification/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/QuickSwitch/Android.bp b/tests/FlickerTests/QuickSwitch/Android.bp
new file mode 100644
index 000000000000..64f718333a59
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsQuickswitch",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/QuickSwitch/AndroidManifest.xml b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml
new file mode 100644
index 000000000000..41b0cd41f0ba
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.quickswitch">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.quickswitch"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..797ca4eacd5f
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.quickswitch/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/QuickSwitch/OWNERS
index 897fe5dee7fb..897fe5dee7fb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
+++ b/tests/FlickerTests/QuickSwitch/OWNERS
diff --git a/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index 7883910323de..13fcc2ba0b54 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
@@ -25,7 +24,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -48,11 +47,10 @@ import org.junit.runners.Parameterized
* Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 1c4c7cd89e42..badd7c8c712c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.datatypes.Rect
import android.tools.common.traces.component.ComponentNameMatcher
@@ -25,7 +23,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -49,11 +47,10 @@ import org.junit.runners.Parameterized
* Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2]
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
@@ -94,7 +91,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
* entirety of the display.
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun startsWithApp1WindowsCoverFullScreen() {
flicker.assertWmStart {
@@ -114,7 +111,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
}
/** Checks that the transition starts with [testApp1] being the top window. */
- @Presubmit
@Test
open fun startsWithApp1WindowBeingOnTop() {
flicker.assertWmStart { this.isAppWindowOnTop(testApp1) }
@@ -124,7 +120,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of
* the transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2WindowsCoveringFullScreen() {
flicker.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
@@ -134,7 +130,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2LayersCoveringFullScreen() {
flicker.assertLayersEnd {
@@ -147,7 +143,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp2] is the top window at the end of the transition once we have fully
* quick switched from [testApp1] back to the [testApp2].
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun endsWithApp2BeingOnTop() {
flicker.assertWmEnd { this.isAppWindowOnTop(testApp2) }
@@ -157,7 +153,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Presubmit
@Test
open fun app2WindowBecomesAndStaysVisible() {
flicker.assertWm {
@@ -173,7 +168,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2LayerBecomesAndStaysVisible() {
flicker.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) }
@@ -183,7 +178,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun app1WindowBecomesAndStaysInvisible() {
flicker.assertWm { this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) }
@@ -193,7 +188,6 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Presubmit
@Test
open fun app1LayerBecomesAndStaysInvisible() {
flicker.assertLayers { this.isVisible(testApp1).then().isInvisible(testApp1) }
@@ -204,7 +198,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
flicker.assertWm {
@@ -223,7 +217,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
flicker.assertLayers {
@@ -238,7 +232,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
}
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 298544839)
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
@@ -247,15 +241,49 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : Ba
@Test
override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- @FlakyTest(bugId = 246284708)
+ @FlakyTest(bugId = 298544839)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
- @FlakyTest(bugId = 250518877)
+ @FlakyTest(bugId = 298544839)
@Test
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ @FlakyTest(bugId = 298544839)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
companion object {
private var startDisplayBounds = Rect.EMPTY
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 641745693187..f51be908750a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/QuickSwitch/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.quickswitch
-import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -26,7 +25,7 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -48,11 +47,10 @@ import org.junit.runners.Parameterized
* Swipe right from the bottom of the screen to quick switch back to the app
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto b/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/QuickSwitch/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
new file mode 100644
index 000000000000..8e93b5b340c4
--- /dev/null
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FlickerTestsRotation",
+ defaults: ["FlickerTestsDefault"],
+ manifest: "AndroidManifest.xml",
+ test_config_template: "AndroidTestTemplate.xml",
+ srcs: ["src/**/*"],
+ static_libs: ["FlickerTestsBase"],
+}
diff --git a/tests/FlickerTests/Rotation/AndroidManifest.xml b/tests/FlickerTests/Rotation/AndroidManifest.xml
new file mode 100644
index 000000000000..6bbb1f6c0d08
--- /dev/null
+++ b/tests/FlickerTests/Rotation/AndroidManifest.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.server.wm.flicker.rotation">
+
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- Read and write traces from external storage -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <!-- Allow the test to write directly to /sdcard/ -->
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+ <!-- Write secure settings -->
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <!-- Capture screen contents -->
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <!-- Enable / Disable tracing !-->
+ <uses-permission android:name="android.permission.DUMP" />
+ <!-- Force-stop test apps -->
+ <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+ <!-- Run layers trace -->
+ <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+ <!-- Capture screen recording -->
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
+ <!-- Workaround grant runtime permission exception from b/152733071 -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <!-- ATM.removeRootTasksWithActivityTypes() -->
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
+ <application android:requestLegacyExternalStorage="true"
+ android:networkSecurityConfig="@xml/network_security_config"
+ android:largeHeap="true">
+ <uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.wm.flicker.rotation"
+ android:label="WindowManager Flicker Tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
new file mode 100644
index 000000000000..b5ea7390e9ba
--- /dev/null
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager {MODULE}">
+ <option name="test-tag" value="FlickerTests"/>
+ <!-- Needed for storing the perfetto trace files in the sdcard/test_results-->
+ <option name="isolated-storage" value="false"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <!-- keeps the screen on during tests -->
+ <option name="screen-always-on" value="on"/>
+ <!-- prevents the phone from restarting -->
+ <option name="force-skip-system-props" value="true"/>
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="cmd window tracing level all"/>
+ <!-- set WM tracing to frame (avoid incomplete states) -->
+ <option name="run-command" value="cmd window tracing frame"/>
+ <!-- ensure lock screen mode is swipe -->
+ <option name="run-command" value="locksettings set-disabled false"/>
+ <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests -->
+ <option name="run-command" value="pm disable com.google.android.internal.betterbug"/>
+ <!-- restart launcher to activate TAPL -->
+ <option name="run-command"
+ value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/>
+ <!-- Increase trace size: 20mb for WM and 80mb for SF -->
+ <option name="run-command" value="cmd window tracing size 20480"/>
+ <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/>
+ <!-- b/307664397 - Ensure camera has the correct permissions and doesn't show a dialog -->
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.CAMERA"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.RECORD_AUDIO"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_FINE_LOCATION"/>
+ <option name="run-command"
+ value="pm grant com.google.android.GoogleCamera android.permission.ACCESS_COARSE_LOCATION"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="test-user-token" value="%TEST_USER%"/>
+ <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
+ <option name="run-command" value="settings put system show_touches 1"/>
+ <option name="run-command" value="settings put system pointer_location 1"/>
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard"/>
+ <option name="teardown-command" value="settings delete system show_touches"/>
+ <option name="teardown-command" value="settings delete system pointer_location"/>
+ <option name="teardown-command"
+ value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="test-file-name" value="{MODULE}.apk"/>
+ <option name="test-file-name" value="FlickerTestApp.apk"/>
+ </target_preparer>
+ <!-- Needed for pushing the trace config file -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="push-file"
+ key="trace_config.textproto"
+ value="/data/misc/perfetto-traces/trace_config.textproto"
+ />
+ <!--Install the content provider automatically when we push some file in sdcard folder.-->
+ <!--Needed to avoid the installation during the test suite.-->
+ <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/>
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="{PACKAGE}"/>
+ <option name="shell-timeout" value="6600s"/>
+ <option name="test-timeout" value="6600s"/>
+ <option name="hidden-api-checks" value="false"/>
+ <option name="device-listeners" value="android.device.collectors.PerfettoListener"/>
+ <!-- PerfettoListener related arguments -->
+ <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/>
+ <option name="instrumentation-arg"
+ key="perfetto_config_file"
+ value="trace_config.textproto"
+ />
+ <option name="instrumentation-arg" key="per_run" value="true"/>
+ </test>
+ <!-- Needed for pulling the collected trace config on to the host -->
+ <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.server.wm.flicker.rotation/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/Rotation/OWNERS
index f7c0a87f5dac..f7c0a87f5dac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
+++ b/tests/FlickerTests/Rotation/OWNERS
diff --git a/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/Rotation/res/anim/show_hide_show_3000ms.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fillAfter="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000" />
+
+ <alpha
+ android:startOffset="2000"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="1000" />
+</set> \ No newline at end of file
diff --git a/tests/FlickerTests/Rotation/res/xml/network_security_config.xml b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/FlickerTests/Rotation/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 842ece38c282..bdbf0d24e624 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
@@ -81,11 +80,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
+class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
override val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -100,7 +98,7 @@ open class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransitio
@Presubmit
@Test
fun focusChanges() {
- flicker.assertEventLog { this.focusChanges(testApp.`package`) }
+ flicker.assertEventLog { this.focusChanges(testApp.packageName) }
}
/**
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index b0ca4d230e12..b0ca4d230e12 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index b6ad3cc7fc50..6d3ae43c1472 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/Rotation/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -26,7 +26,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.view.WindowManager
-import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
@@ -88,11 +87,10 @@ import org.junit.runners.Parameterized
* apps are running before setup
* ```
*/
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
+class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
override val testApp = SeamlessRotationAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -118,8 +116,8 @@ open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransit
flicker.assertWm {
this.invoke("isFullScreen") {
val appWindow =
- it.windowState(testApp.`package`)
- ?: error("App window for package ${testApp.`package`} not found")
+ it.windowState(testApp.packageName)
+ ?: error("App window for package ${testApp.packageName} not found")
val flags = appWindow.windowState.attributes.flags
appWindow
.check { "isFullScreen" }
@@ -136,8 +134,8 @@ open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransit
flicker.assertWm {
this.invoke("isRotationSeamless") {
val appWindow =
- it.windowState(testApp.`package`)
- ?: error("App window for package ${testApp.`package`} not found")
+ it.windowState(testApp.packageName)
+ ?: error("App window for package ${testApp.packageName} not found")
val rotationAnimation = appWindow.windowState.attributes.rotationAnimation
appWindow
.check { "isRotationSeamless" }
diff --git a/tests/FlickerTests/Rotation/trace_config/trace_config.textproto b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto
new file mode 100644
index 000000000000..c9a35aca9085
--- /dev/null
+++ b/tests/FlickerTests/Rotation/trace_config/trace_config.textproto
@@ -0,0 +1,77 @@
+# Copyright (C) 2023 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.
+
+# proto-message: TraceConfig
+
+# Enable periodic flushing of the trace buffer into the output file.
+write_into_file: true
+
+# Writes the userspace buffer into the file every 1s.
+file_write_period_ms: 2500
+
+# See b/126487238 - we need to guarantee ordering of events.
+flush_period_ms: 30000
+
+# The trace buffers needs to be big enough to hold |file_write_period_ms| of
+# trace data. The trace buffer sizing depends on the number of trace categories
+# enabled and the device activity.
+
+# RSS events
+buffers: {
+ size_kb: 63488
+ fill_policy: RING_BUFFER
+}
+
+data_sources {
+ config {
+ name: "linux.process_stats"
+ target_buffer: 0
+ # polled per-process memory counters and process/thread names.
+ # If you don't want the polled counters, remove the "process_stats_config"
+ # section, but keep the data source itself as it still provides on-demand
+ # thread/process naming for ftrace data below.
+ process_stats_config {
+ scan_all_processes_on_start: true
+ }
+ }
+}
+
+data_sources: {
+ config {
+ name: "linux.ftrace"
+ ftrace_config {
+ ftrace_events: "ftrace/print"
+ ftrace_events: "task/task_newtask"
+ ftrace_events: "task/task_rename"
+ atrace_categories: "ss"
+ atrace_categories: "wm"
+ atrace_categories: "am"
+ atrace_categories: "aidl"
+ atrace_categories: "input"
+ atrace_categories: "binder_driver"
+ atrace_categories: "sched_process_exit"
+ atrace_apps: "com.android.server.wm.flicker"
+ atrace_apps: "com.android.server.wm.flicker.other"
+ atrace_apps: "com.android.server.wm.flicker.close"
+ atrace_apps: "com.android.server.wm.flicker.ime"
+ atrace_apps: "com.android.server.wm.flicker.launch"
+ atrace_apps: "com.android.server.wm.flicker.quickswitch"
+ atrace_apps: "com.android.server.wm.flicker.rotation"
+ atrace_apps: "com.android.server.wm.flicker.testapp"
+ atrace_apps: "com.android.systemui"
+ atrace_apps: "com.google.android.apps.nexuslauncher"
+ }
+ }
+}
+
diff --git a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml b/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml
deleted file mode 100644
index 203035d30584..000000000000
--- a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.quickswitch">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.quickswitch"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestRotation.xml b/tests/FlickerTests/manifests/AndroidManifestRotation.xml
deleted file mode 100644
index 2852cf23a35b..000000000000
--- a/tests/FlickerTests/manifests/AndroidManifestRotation.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.rotation">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.rotation"
- android:label="WindowManager Flicker Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestService.xml b/tests/FlickerTests/manifests/AndroidManifestService.xml
deleted file mode 100644
index 3a7bc5095c08..000000000000
--- a/tests/FlickerTests/manifests/AndroidManifestService.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- ~ Copyright (C) 2023 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.service">
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.wm.flicker.service"
- android:label="WindowManager Flicker Service Tests">
- </instrumentation>
-</manifest>
diff --git a/tests/FlickerTests/res/xml/network_security_config.xml b/tests/FlickerTests/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/FlickerTests/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index b18a1a84c61b..4032121d4211 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -19,6 +19,8 @@
package com.android.server.wm.flicker
import android.tools.common.PlatformConsts
+import android.tools.common.Position
+import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.flicker.subject.region.RegionSubject
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentNameMatcher
@@ -168,14 +170,7 @@ fun LegacyFlickerTest.statusBarLayerIsVisibleAtStartAndEnd() {
* the SF trace
*/
fun LegacyFlickerTest.navBarLayerPositionAtStart() {
- assertLayersStart {
- val display =
- this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!")
- this.visibleRegion(ComponentNameMatcher.NAV_BAR)
- .coversExactly(
- WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
- )
- }
+ assertLayersStart { assertNavBarPosition(this, scenario.isGesturalNavigation) }
}
/**
@@ -183,14 +178,39 @@ fun LegacyFlickerTest.navBarLayerPositionAtStart() {
* the SF trace
*/
fun LegacyFlickerTest.navBarLayerPositionAtEnd() {
- assertLayersEnd {
- val display =
- this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
- this.visibleRegion(ComponentNameMatcher.NAV_BAR)
- .coversExactly(
- WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
- )
+ assertLayersEnd { assertNavBarPosition(this, scenario.isGesturalNavigation) }
+}
+
+private fun assertNavBarPosition(sfState: LayerTraceEntrySubject, isGesturalNavigation: Boolean) {
+ val display =
+ sfState.entry.displays.filterNot { it.isOff }.minByOrNull { it.id }
+ ?: error("There is no display!")
+ val displayArea = display.layerStackSpace
+ val navBarPosition = display.navBarPosition(isGesturalNavigation)
+ val navBarRegion = sfState.visibleRegion(ComponentNameMatcher.NAV_BAR)
+
+ when (navBarPosition) {
+ Position.TOP ->
+ navBarRegion
+ .hasSameTopPosition(displayArea)
+ .hasSameLeftPosition(displayArea)
+ .hasSameRightPosition(displayArea)
+ Position.BOTTOM ->
+ navBarRegion
+ .hasSameBottomPosition(displayArea)
+ .hasSameLeftPosition(displayArea)
+ .hasSameRightPosition(displayArea)
+ Position.LEFT ->
+ navBarRegion
+ .hasSameLeftPosition(displayArea)
+ .hasSameTopPosition(displayArea)
+ .hasSameBottomPosition(displayArea)
+ Position.RIGHT ->
+ navBarRegion
+ .hasSameRightPosition(displayArea)
+ .hasSameTopPosition(displayArea)
+ .hasSameBottomPosition(displayArea)
+ else -> error("Unknown position $navBarPosition")
}
}
@@ -262,10 +282,10 @@ fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(
snapshotLayers
.mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
.toTypedArray()
- val snapshotRegion = RegionSubject(visibleAreas, timestamp)
+ val snapshotRegion = RegionSubject(visibleAreas, it.timestamp)
+ val appVisibleRegion = it.visibleRegion(component)
// Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
- if (snapshotRegion.region.isNotEmpty) {
- val appVisibleRegion = it.visibleRegion(component)
+ if (snapshotRegion.region.isNotEmpty && appVisibleRegion.region.isNotEmpty) {
snapshotRegion.coversExactly(appVisibleRegion.region)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
deleted file mode 100644
index 9ad3eddd1c4f..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.activityembedding.pip
-
-import android.platform.test.annotations.Presubmit
-import android.tools.common.datatypes.Rect
-import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
-import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test launching a secondary Activity into Picture-In-Picture mode.
- *
- * Setup: Start from a split A|B.
- * Transition: B enters PIP, observe the window shrink to the bottom right corner on screen.
- *
- * To run this test: `atest FlickerTestsOther:SecondaryActivityEnterPipTest`
- *
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SecondaryActivityEnterPipTest (flicker: LegacyFlickerTest) :
- ActivityEmbeddingTestBase(flicker) {
- override val transition: FlickerBuilder.() -> Unit = {
- setup {
- tapl.setExpectedRotationCheckEnabled(false)
- testApp.launchViaIntent(wmHelper)
- testApp.launchSecondaryActivity(wmHelper)
- startDisplayBounds =
- wmHelper.currentState.layerState.physicalDisplayBounds
- ?: error("Can't get display bounds")
- }
- transitions {
- testApp.secondaryActivityEnterPip(wmHelper)
- }
- teardown {
- tapl.goHome()
- testApp.exit(wmHelper)
- }
- }
-
- /**
- * Main and secondary activity start from a split each taking half of the screen.
- */
- @Presubmit
- @Test
- fun layersStartFromEqualSplit() {
- flicker.assertLayersStart {
- val leftLayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- val rightLayerRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- // Compare dimensions of two splits, given we're using default split attributes,
- // both activities take up the same visible size on the display.
- check { "height" }
- .that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height)
- check { "width" }
- .that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width)
- leftLayerRegion.notOverlaps(rightLayerRegion.region)
- leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
- }
- flicker.assertLayersEnd {
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- .coversExactly(startDisplayBounds)
- }
- }
-
- /**
- * Main Activity is visible throughout the transition and becomes fullscreen.
- */
- @Presubmit
- @Test
- fun mainActivityWindowBecomesFullScreen() {
- flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
- flicker.assertWmEnd {
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- .coversExactly(startDisplayBounds)
- }
- }
-
- /**
- * Main Activity is visible throughout the transition and becomes fullscreen.
- */
- @Presubmit
- @Test
- fun mainActivityLayerBecomesFullScreen() {
- flicker.assertLayers {
- isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- .then()
- .isVisible(TRANSITION_SNAPSHOT)
- .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- .then()
- .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- }
- flicker.assertLayersEnd {
- visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
- .coversExactly(startDisplayBounds)
- }
- }
-
- /**
- * Secondary Activity is visible throughout the transition and shrinks to the bottom right
- * corner.
- */
- @Presubmit
- @Test
- fun secondaryWindowShrinks() {
- flicker.assertWm {
- isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- }
- flicker.assertWmEnd {
- val pipWindowRegion =
- visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- check{"height"}
- .that(pipWindowRegion.region.height)
- .isLower(startDisplayBounds.height / 2)
- check{"width"}
- .that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
- }
- }
-
- /**
- * During the transition Secondary Activity shrinks to the bottom right corner.
- */
- @Presubmit
- @Test
- fun secondaryLayerShrinks() {
- flicker.assertLayers {
- val pipLayerList = layers {
- ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
- }
- pipLayerList.zipWithNext { previous, current ->
- // TODO(b/290987990): Add checks for visibleRegion.
- current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
- current.screenBounds.notBiggerThan(previous.screenBounds.region)
- }
- }
- flicker.assertLayersEnd {
- val pipRegion = visibleRegion(
- ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- check { "height" }
- .that(pipRegion.region.height)
- .isLower(startDisplayBounds.height / 2)
- check { "width" }
- .that(pipRegion.region.width).isLower(startDisplayBounds.width)
- }
- }
-
- companion object {
- /** {@inheritDoc} */
- private var startDisplayBounds = Rect.EMPTY
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
deleted file mode 100644
index 566f393efaea..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.close
-
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppBackButtonTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
deleted file mode 100644
index 49ed183c2ccf..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.close
-
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppHomeButtonTest(flicker) {
- companion object {
- /** Creates the test configurations. */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
deleted file mode 100644
index d87a1daebd79..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeOnDismissPopupDialogTestCfArm(flicker: LegacyFlickerTest) :
- CloseImeOnDismissPopupDialogTest(flicker) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
deleted file mode 100644
index 58411cc63004..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: LegacyFlickerTest) :
- CloseImeShownOnAppStartOnGoHomeTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
deleted file mode 100644
index 41b06c0b4bd3..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) :
- CloseImeShownOnAppStartToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
deleted file mode 100644
index 104af225ee2e..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) :
- CloseImeToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
deleted file mode 100644
index 0e1838570bab..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: LegacyFlickerTest) :
- CloseImeToHomeOnFinishActivityTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
deleted file mode 100644
index db80001ccd78..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: LegacyFlickerTest) :
- ShowImeOnAppStartWhenLaunchingAppTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
deleted file mode 100644
index 6d9ea2209d91..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
deleted file mode 100644
index 92b3968216e5..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker) {
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
deleted file mode 100644
index 09bfacc582e5..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Presubmit
-open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
deleted file mode 100644
index f8c814988200..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeWhenFocusingOnInputFieldTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
deleted file mode 100644
index ad41b81a6664..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeWhileDismissingThemedPopupDialogTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
deleted file mode 100644
index 90fbbf9bb240..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.ime
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhileEnteringOverviewTestCfArm(flicker: LegacyFlickerTest) :
- ShowImeWhileEnteringOverviewTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
deleted file mode 100644
index 6bbf40ea10f9..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivityTransitionTestCfArm(flicker: LegacyFlickerTest) : ActivityTransitionTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
deleted file mode 100644
index 4fd4a61e4adc..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.platform.test.annotations.FlakyTest
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/** Some assertions will fail because of b/264415996 */
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromIconColdTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromIconColdTest(flicker) {
- @Test
- @FlakyTest
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
deleted file mode 100644
index 117cff203ff7..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromIntentColdAfterCameraTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
deleted file mode 100644
index 6d0b6f48d7c6..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.platform.test.annotations.FlakyTest
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromIntentColdTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromIntentColdTest(flicker) {
- @FlakyTest(bugId = 273696733)
- @Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
deleted file mode 100644
index 3e0958a27aaf..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromIntentWarmTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromIntentWarmTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
deleted file mode 100644
index ab6a1ea36222..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import android.tools.common.flicker.annotation.FlickerServiceCompatible
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/** Some assertions will fail because of b/264415996 */
-@FlickerServiceCompatible
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromOverviewTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt
deleted file mode 100644
index a147171ed936..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.notification
-
-import android.platform.test.annotations.Postsubmit
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Postsubmit
-class OpenAppFromNotificationColdTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromNotificationColdTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt
deleted file mode 100644
index 98356d7d1e87..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.notification
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromNotificationWarmTestCfArm(flicker: LegacyFlickerTest) :
- OpenAppFromNotificationWarmTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
deleted file mode 100644
index f68cd5c4293c..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.quickswitch
-
-import android.tools.common.NavBar
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: LegacyFlickerTest) :
- QuickSwitchBetweenTwoAppsBackTest(flicker) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
deleted file mode 100644
index 3de58acccd79..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.quickswitch
-
-import android.tools.common.NavBar
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: LegacyFlickerTest) :
- QuickSwitchBetweenTwoAppsForwardTest(flicker) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
deleted file mode 100644
index 84fc75445175..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.quickswitch
-
-import android.tools.common.NavBar
-import android.tools.common.Rotation
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchFromLauncherTestCfArm(flicker: LegacyFlickerTest) :
- QuickSwitchFromLauncherTest(flicker) {
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
- // TODO: Test with 90 rotation
- supportedRotations = listOf(Rotation.ROTATION_0)
- )
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
deleted file mode 100644
index 1ab5c5ad77b5..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.rotation
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ChangeAppRotationTestCfArm(flicker: LegacyFlickerTest) : ChangeAppRotationTest(flicker) {
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
- * navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() = LegacyFlickerTestFactory.rotationTests()
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
deleted file mode 100644
index 592be05c8182..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.rotation
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
-import com.android.server.wm.flicker.testapp.ActivityOptions
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/** This test should fail because of b/264518826 */
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SeamlessAppRotationTestCfArm(flicker: LegacyFlickerTest) :
- SeamlessAppRotationTest(flicker) {
- companion object {
- /**
- * Creates the test configurations for seamless rotation based on the default rotation tests
- * from [LegacyFlickerTestFactory.rotationTests], but adding a flag (
- * [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should
- * starve the UI thread of not
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams() =
- LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg ->
- val legacyCfg = sourceCfg as LegacyFlickerTest
- val defaultRun = createConfig(legacyCfg, starveUiThread = false)
- val busyUiRun = createConfig(legacyCfg, starveUiThread = true)
- listOf(defaultRun, busyUiRun)
- }
- }
-}
diff --git a/tests/FlickerTests/test-apps/app-helpers/Android.bp b/tests/FlickerTests/test-apps/app-helpers/Android.bp
new file mode 100644
index 000000000000..fc4d71c652d5
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/Android.bp
@@ -0,0 +1,42 @@
+//
+// Copyright (C) 2018 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_library {
+ name: "wm-flicker-common-app-helpers",
+ platform_apis: true,
+ optimize: {
+ enabled: false,
+ },
+ srcs: ["src/**/*"],
+ static_libs: [
+ "flickertestapplib",
+ "flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
+ "truth",
+ "app-helpers-core",
+ "wm-flicker-window-extensions",
+ ],
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index 883c7e6d5785..11e6bbe4eb13 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -56,9 +56,7 @@ constructor(
launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_rtl_button")
}
- /**
- * Clicks the button to launch the secondary activity in a horizontal split.
- */
+ /** Clicks the button to launch the secondary activity in a horizontal split. */
fun launchSecondaryActivityHorizontally(wmHelper: WindowManagerStateHelper) {
launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_horizontally_button")
}
@@ -67,7 +65,7 @@ constructor(
fun launchThirdActivity(wmHelper: WindowManagerStateHelper) {
val launchButton =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_third_activity_button")),
+ Until.findObject(By.res(packageName, "launch_third_activity_button")),
FIND_TIMEOUT
)
require(launchButton != null) { "Can't find launch third activity button on screen." }
@@ -86,17 +84,17 @@ constructor(
*/
fun launchTrampolineActivity(wmHelper: WindowManagerStateHelper) {
val launchButton =
- uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_trampoline_button")),
- FIND_TIMEOUT
- )
+ uiDevice.wait(
+ Until.findObject(By.res(packageName, "launch_trampoline_button")),
+ FIND_TIMEOUT
+ )
require(launchButton != null) { "Can't find launch trampoline activity button on screen." }
launchButton.click()
wmHelper
- .StateSyncBuilder()
- .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
- .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT)
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT)
+ .waitForAndVerify()
}
/**
@@ -105,53 +103,45 @@ constructor(
*/
fun finishSecondaryActivity(wmHelper: WindowManagerStateHelper) {
val finishButton =
- uiDevice.wait(
- Until.findObject(By.res(getPackage(), "finish_secondary_activity_button")),
- FIND_TIMEOUT
- )
+ uiDevice.wait(
+ Until.findObject(By.res(packageName, "finish_secondary_activity_button")),
+ FIND_TIMEOUT
+ )
require(finishButton != null) { "Can't find finish secondary activity button on screen." }
finishButton.click()
wmHelper
- .StateSyncBuilder()
- .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT)
- .waitForAndVerify()
- }
+ .StateSyncBuilder()
+ .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT)
+ .waitForAndVerify()
+ }
- /**
- * Clicks the button to toggle the split ratio of secondary activity.
- */
+ /** Clicks the button to toggle the split ratio of secondary activity. */
fun changeSecondaryActivityRatio(wmHelper: WindowManagerStateHelper) {
val launchButton =
- uiDevice.wait(
- Until.findObject(
- By.res(getPackage(),
- "toggle_split_ratio_button")),
- FIND_TIMEOUT
- )
+ uiDevice.wait(
+ Until.findObject(By.res(packageName, "toggle_split_ratio_button")),
+ FIND_TIMEOUT
+ )
require(launchButton != null) {
"Can't find toggle ratio for secondary activity button on screen."
}
launchButton.click()
wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withTransitionSnapshotGone()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withTransitionSnapshotGone()
+ .waitForAndVerify()
}
fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) {
val pipButton =
- uiDevice.wait(
- Until.findObject(By.res(getPackage(), "secondary_enter_pip_button")),
- FIND_TIMEOUT
- )
+ uiDevice.wait(
+ Until.findObject(By.res(packageName, "secondary_enter_pip_button")),
+ FIND_TIMEOUT
+ )
require(pipButton != null) { "Can't find enter pip button on screen." }
pipButton.click()
- wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withPipShown()
- .waitForAndVerify()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().withPipShown().waitForAndVerify()
}
/**
@@ -161,7 +151,7 @@ constructor(
fun launchAlwaysExpandActivity(wmHelper: WindowManagerStateHelper) {
val launchButton =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_always_expand_activity_button")),
+ Until.findObject(By.res(packageName, "launch_always_expand_activity_button")),
FIND_TIMEOUT
)
require(launchButton != null) {
@@ -171,23 +161,29 @@ constructor(
wmHelper
.StateSyncBuilder()
.withActivityState(ALWAYS_EXPAND_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
- .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_PAUSED)
+ .withActivityState(
+ MAIN_ACTIVITY_COMPONENT,
+ PlatformConsts.STATE_PAUSED,
+ PlatformConsts.STATE_STOPPED
+ )
.waitForAndVerify()
}
private fun launchSecondaryActivityFromButton(
- wmHelper: WindowManagerStateHelper, buttonName: String) {
+ wmHelper: WindowManagerStateHelper,
+ buttonName: String
+ ) {
val launchButton =
- uiDevice.wait(Until.findObject(By.res(getPackage(), buttonName)), FIND_TIMEOUT)
+ uiDevice.wait(Until.findObject(By.res(packageName, buttonName)), FIND_TIMEOUT)
require(launchButton != null) {
"Can't find launch secondary activity button : " + buttonName + "on screen."
}
launchButton.click()
wmHelper
- .StateSyncBuilder()
- .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
- .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .waitForAndVerify()
}
/**
@@ -197,7 +193,7 @@ constructor(
fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) {
val launchButton =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")),
+ Until.findObject(By.res(packageName, "launch_placeholder_split_button")),
FIND_TIMEOUT
)
require(launchButton != null) { "Can't find launch placeholder split button on screen." }
@@ -216,7 +212,7 @@ constructor(
fun launchPlaceholderSplitRTL(wmHelper: WindowManagerStateHelper) {
val launchButton =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_placeholder_split_rtl_button")),
+ Until.findObject(By.res(packageName, "launch_placeholder_split_rtl_button")),
FIND_TIMEOUT
)
require(launchButton != null) { "Can't find launch placeholder split button on screen." }
@@ -252,7 +248,7 @@ constructor(
.toFlickerComponent()
val TRAMPOLINE_ACTIVITY_COMPONENT =
- ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent()
+ ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent()
@JvmStatic
fun getWindowExtensions(): WindowExtensions? {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
index 94ac1a6e1e02..94ac1a6e1e02 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
index fde098199042..fde098199042 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index c6fa1bb89220..c6fa1bb89220 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 5c8cbe49d7cf..5c8cbe49d7cf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index 747cf3742bf7..3146139757c1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -41,7 +41,7 @@ constructor(
*/
fun swipeDown(): Boolean {
val gameView =
- uiDevice.wait(Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS)
+ uiDevice.wait(Until.findObject(By.res(packageName, GAME_APP_VIEW_RES)), WAIT_TIME_MS)
require(gameView != null) { "Mock game app view not found." }
val bound = gameView.getVisibleBounds()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
index eeee7b4dfc6b..eeee7b4dfc6b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index d1722521bba8..252f7d3e1bed 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -40,7 +40,7 @@ constructor(
*/
open fun openIME(wmHelper: WindowManagerStateHelper) {
val editText =
- uiDevice.wait(Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT)
+ uiDevice.wait(Until.findObject(By.res(packageName, "plain_text_input")), FIND_TIMEOUT)
requireNotNull(editText) {
"Text field not found, this usually happens when the device " +
@@ -67,7 +67,7 @@ constructor(
open fun finishActivity(wmHelper: WindowManagerStateHelper) {
val finishButton =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "finish_activity_btn")),
+ Until.findObject(By.res(packageName, "finish_activity_btn")),
FIND_TIMEOUT
)
requireNotNull(finishButton) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index 7a8d780c3d9f..7a8d780c3d9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
index 83a41abbd4bd..d3cee645cd99 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -18,8 +18,6 @@ package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.tools.common.Rotation
-import android.tools.common.traces.Condition
-import android.tools.common.traces.DeviceStateDump
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.helpers.FIND_TIMEOUT
@@ -54,14 +52,14 @@ constructor(
launchedAppComponentMatcherOverride: IComponentMatcher?,
action: String?,
stringExtras: Map<String, String>,
- waitConditions: Array<Condition<DeviceStateDump>>
+ waitConditionsBuilder: WindowManagerStateHelper.StateSyncBuilder
) {
super.launchViaIntent(
wmHelper,
launchedAppComponentMatcherOverride,
action,
stringExtras,
- waitConditions
+ waitConditionsBuilder
)
waitIMEShown(wmHelper)
}
@@ -71,7 +69,7 @@ constructor(
if (rotation.isRotated()) {
imePackageName
} else {
- getPackage()
+ packageName
}
open(expectedPackage)
}
@@ -79,7 +77,7 @@ constructor(
fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
val button =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "start_dialog_themed_activity_btn")),
+ Until.findObject(By.res(packageName, "start_dialog_themed_activity_btn")),
FIND_TIMEOUT
)
@@ -123,7 +121,7 @@ constructor(
else -> null
}
if (matcher != null && matcher.find()) {
- return matcher.group(1).equals("VISIBLE")
+ return matcher.group(1) == "VISIBLE"
}
}
return false
@@ -132,7 +130,7 @@ constructor(
fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) {
val button =
uiDevice.wait(
- Until.findObject(By.res(getPackage(), "toggle_fixed_portrait_btn")),
+ Until.findObject(By.res(packageName, "toggle_fixed_portrait_btn")),
FIND_TIMEOUT
)
require(button != null) {
@@ -140,7 +138,7 @@ constructor(
"was left in an unknown state (e.g. Screen turned off)"
}
button.click()
- mInstrumentation.waitForIdleSync()
+ instrumentation.waitForIdleSync()
// Ensure app relaunching transition finish and the IME has shown
waitIMEShown(wmHelper)
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index b2aeb14aaf78..b2aeb14aaf78 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
index b95d86b72f34..b95d86b72f34 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
index d83b6d39fcd8..9b539c8641d4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -38,7 +38,7 @@ constructor(
ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
) : StandardAppHelper(instr, launcherName, component) {
- private val gestureHelper: GestureHelper = GestureHelper(mInstrumentation)
+ private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
fun clickRestart(wmHelper: WindowManagerStateHelper) {
val restartButton =
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
index c98f1c4b4d29..9895bda7f590 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -37,7 +37,7 @@ constructor(
fun openMail(rowIdx: Int) {
val rowSel =
- By.res(getPackage(), "mail_row_item_text").textEndsWith(String.format("%04d", rowIdx))
+ By.res(packageName, "mail_row_item_text").textEndsWith(String.format("%04d", rowIdx))
var row: UiObject2? = null
for (i in 1..1000) {
row = uiDevice.wait(Until.findObject(rowSel), SHORT_WAIT_TIME_MS)
@@ -46,7 +46,7 @@ constructor(
}
require(row != null) { "" }
row.click()
- uiDevice.wait(Until.gone(By.res(getPackage(), MAIL_LIST_RES_ID)), FIND_TIMEOUT)
+ uiDevice.wait(Until.gone(By.res(packageName, MAIL_LIST_RES_ID)), FIND_TIMEOUT)
}
fun scrollDown() {
@@ -55,7 +55,7 @@ constructor(
}
fun waitForMailList(): UiObject2 {
- val sel = By.res(getPackage(), MAIL_LIST_RES_ID).scrollable(true)
+ val sel = By.res(packageName, MAIL_LIST_RES_ID).scrollable(true)
val ret = uiDevice.wait(Until.findObject(sel), FIND_TIMEOUT)
requireNotNull(ret) { "Unable to find $MAIL_LIST_RES_ID object" }
return ret
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
index 65175ef33afb..65175ef33afb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index 5b3d3083fe1c..b2f8d4748c07 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -36,7 +36,7 @@ constructor(
) : StandardAppHelper(instr, launcherName, component) {
fun openNewTask(device: UiDevice, wmHelper: WindowManagerStateHelper) {
val button =
- device.wait(Until.findObject(By.res(getPackage(), "launch_new_task")), FIND_TIMEOUT)
+ device.wait(Until.findObject(By.res(packageName, "launch_new_task")), FIND_TIMEOUT)
requireNotNull(button) {
"Button not found, this usually happens when the device " +
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index ee65004e9e78..ee65004e9e78 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index 7665690a3122..e60c20df9967 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -35,7 +35,7 @@ constructor(
) : StandardAppHelper(instr, launcherName, component) {
fun postNotification(wmHelper: WindowManagerStateHelper) {
val button =
- uiDevice.wait(Until.findObject(By.res(getPackage(), "post_notification")), FIND_TIMEOUT)
+ uiDevice.wait(Until.findObject(By.res(packageName, "post_notification")), FIND_TIMEOUT)
requireNotNull(button) {
"Post notification button not found, this usually happens when the device " +
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index c6b86f2689f0..73cc2f2b4d18 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.content.Intent
import android.media.session.MediaController
import android.media.session.MediaSessionManager
import android.tools.common.datatypes.Rect
@@ -46,12 +47,14 @@ open class PipAppHelper(instrumentation: Instrumentation) :
private val mediaController: MediaController?
get() =
- mediaSessionManager.getActiveSessions(null).firstOrNull { it.packageName == `package` }
+ mediaSessionManager.getActiveSessions(null).firstOrNull {
+ it.packageName == packageName
+ }
- private val gestureHelper: GestureHelper = GestureHelper(mInstrumentation)
+ private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
open fun clickObject(resId: String) {
- val selector = By.res(`package`, resId)
+ val selector = By.res(packageName, resId)
val obj = uiDevice.findObject(selector) ?: error("Could not find `$resId` object")
obj.click()
@@ -242,27 +245,33 @@ open class PipAppHelper(instrumentation: Instrumentation) :
action: String? = null,
stringExtras: Map<String, String>
) {
- launchViaIntentAndWaitShown(
+ launchViaIntent(
wmHelper,
launchedAppComponentMatcherOverride,
action,
stringExtras,
- waitConditions = arrayOf(ConditionsFactory.hasPipWindow())
+ waitConditionsBuilder =
+ wmHelper
+ .StateSyncBuilder()
+ .add(ConditionsFactory.isWMStateComplete())
+ .withAppTransitionIdle()
+ .add(ConditionsFactory.hasPipWindow())
)
- val windowRegion = wmHelper.getWindowRegion(this)
-
wmHelper
.StateSyncBuilder()
.withWindowSurfaceAppeared(this)
.withPipShown()
- .withSurfaceVisibleRegion(this, windowRegion)
.waitForAndVerify()
}
/** Expand the PIP window back to full screen via intent and wait until the app is visible */
- fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
- launchViaIntentAndWaitShown(wmHelper)
+ fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
+
+ fun changeAspectRatio() {
+ val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
+ context.sendBroadcast(intent)
+ }
fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
clickObject(ENTER_PIP_BUTTON_ID)
@@ -289,7 +298,7 @@ open class PipAppHelper(instrumentation: Instrumentation) :
fun checkWithCustomActionsCheckbox() =
uiDevice
- .findObject(By.res(`package`, WITH_CUSTOM_ACTIONS_BUTTON_ID))
+ .findObject(By.res(packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID))
?.takeIf { it.isCheckable }
?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
?: error("'With custom actions' checkbox not found")
@@ -305,7 +314,7 @@ open class PipAppHelper(instrumentation: Instrumentation) :
ReplaceWith("closePipWindow(wmHelper)")
)
open fun closePipWindow() {
- closePipWindow(WindowManagerStateHelper(mInstrumentation))
+ closePipWindow(WindowManagerStateHelper(instrumentation))
}
/** Returns the pip window bounds. */
@@ -389,8 +398,10 @@ open class PipAppHelper(instrumentation: Instrumentation) :
Log.d(TAG, "window " + pipAppWindow)
if (pipAppWindow == null) return@add false
val pipRegion = pipAppWindow.frameRegion
- Log.d(TAG, "region " + pipRegion +
- " covers " + windowRect.coversMoreThan(pipRegion))
+ Log.d(
+ TAG,
+ "region " + pipRegion + " covers " + windowRect.coversMoreThan(pipRegion)
+ )
return@add windowRect.coversMoreThan(pipRegion)
}
.waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index cac3530399de..cac3530399de 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index 8366a7a1fe41..8366a7a1fe41 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index 89c6c35af47d..89c6c35af47d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
new file mode 100644
index 000000000000..6311678f1a04
--- /dev/null
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TransferSplashscreenAppHelper.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class TransferSplashscreenAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.TransferSplashscreenActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.TransferSplashscreenActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 895725c1efef..8be5769f47cf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -40,7 +40,7 @@ constructor(
ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
+ val launchActivityButton = By.res(packageName, LAUNCH_SECOND_ACTIVITY)
val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
requireNotNull(button) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index 75e35ee9c765..e3b23b986c83 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -24,6 +24,9 @@ package {
android_test {
name: "FlickerTestApp",
srcs: ["**/*.java"],
+ resource_dirs: [
+ "res",
+ ],
sdk_version: "current",
test_suites: ["device-tests"],
static_libs: [
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index ff9799a1c710..9198ae184b18 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -70,7 +70,7 @@
<activity android:name=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
android:label="SeamlessActivity"
android:exported="true">
<intent-filter>
@@ -102,6 +102,20 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".PortraitImmersiveActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitImmersiveActivity"
+ android:immersive="true"
+ android:resizeableActivity="true"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="PortraitImmersiveActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<activity android:name=".LaunchTransparentActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait"
@@ -347,6 +361,17 @@
android:exported="false"
android:theme="@style/CutoutShortEdges"
android:resizeableActivity="true"/>
+ <activity
+ android:name=".TransferSplashscreenActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.TransferSplashscreenActivity"
+ android:label="TransferSplashscreenActivity"
+ android:theme="@style/SplashscreenAppTheme"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<service
android:name=".AssistantInteractionSessionService"
android:exported="true"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/drawable/avd_anim.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/avd_anim.xml
new file mode 100644
index 000000000000..19205d4d1c14
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/avd_anim.xml
@@ -0,0 +1,94 @@
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="432dp" android:width="432dp" android:viewportHeight="432" android:viewportWidth="432">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_5_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <path android:name="_R_G_L_5_G_D_0_P_0" android:fillColor="#555555" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M53.82 -56.7 C53.82,-56.7 43.64,-49.06 43.64,-49.06 C43.64,-49.06 0,-16.34 0,-16.34 C0,-16.34 -43.64,-49.06 -43.64,-49.06 C-43.64,-49.06 -53.82,-56.7 -53.82,-56.7 C-64.6,-64.79 -80,-57.09 -80,-43.61 C-80,-43.61 -80,-29.07 -80,-29.07 C-80,-29.07 -80,49.09 -80,49.09 C-80,55.12 -75.12,60 -69.09,60 C-69.09,60 -43.64,60 -43.64,60 C-43.64,60 -43.64,-1.8 -43.64,-1.8 C-43.64,-1.8 0,30.92 0,30.92 C0,30.92 43.64,-1.8 43.64,-1.8 C43.64,-1.8 43.64,60 43.64,60 C43.64,60 69.09,60 69.09,60 C75.12,60 80,55.12 80,49.09 C80,49.09 80,-29.07 80,-29.07 C80,-29.07 80,-43.61 80,-43.61 C80,-57.09 64.61,-64.79 53.82,-56.7c "/>
+ </group>
+ <group android:name="_R_G_L_4_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <clip-path android:name="mask_x" android:pathData="M53.82 -56.7 C53.82,-56.7 43.64,-49.06 43.64,-49.06 C43.64,-49.06 0,-16.34 0,-16.34 C0,-16.34 -43.64,-49.06 -43.64,-49.06 C-43.64,-49.06 -53.82,-56.7 -53.82,-56.7 C-64.6,-64.79 -80,-57.09 -80,-43.61 C-80,-43.61 -80,-29.07 -80,-29.07 C-80,-29.07 -80,49.09 -80,49.09 C-80,55.12 -75.12,60 -69.09,60 C-69.09,60 -43.64,60 -43.64,60 C-43.64,60 -43.64,-1.8 -43.64,-1.8 C-43.64,-1.8 0,30.92 0,30.92 C0,30.92 43.64,-1.8 43.64,-1.8 C43.64,-1.8 43.64,60 43.64,60 C43.64,60 69.09,60 69.09,60 C75.12,60 80,55.12 80,49.09 C80,49.09 80,-29.07 80,-29.07 C80,-29.07 80,-43.61 80,-43.61 C80,-57.09 64.61,-64.79 53.82,-56.7c"/>
+ <path android:name="_R_G_L_4_G_D_0_P_0" android:fillColor="#2684fc" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-80.39 60 C-80.39,60 -105.84,60 -105.84,60 C-111.87,60 -116.75,55.12 -116.75,49.09 C-116.75,49.09 -116.75,-60 -116.75,-60 C-116.75,-60 -80.39,-60 -80.39,-60 C-80.39,-60 -80.39,60 -80.39,60c "/>
+ </group>
+ <group android:name="_R_G_L_3_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <clip-path android:name="mask_x" android:pathData="M53.82 -56.7 C53.82,-56.7 43.64,-49.06 43.64,-49.06 C43.64,-49.06 0,-16.34 0,-16.34 C0,-16.34 -43.64,-49.06 -43.64,-49.06 C-43.64,-49.06 -53.82,-56.7 -53.82,-56.7 C-64.6,-64.79 -80,-57.09 -80,-43.61 C-80,-43.61 -80,-29.07 -80,-29.07 C-80,-29.07 -80,49.09 -80,49.09 C-80,55.12 -75.12,60 -69.09,60 C-69.09,60 -43.64,60 -43.64,60 C-43.64,60 -43.64,-1.8 -43.64,-1.8 C-43.64,-1.8 0,30.92 0,30.92 C0,30.92 43.64,-1.8 43.64,-1.8 C43.64,-1.8 43.64,60 43.64,60 C43.64,60 69.09,60 69.09,60 C75.12,60 80,55.12 80,49.09 C80,49.09 80,-29.07 80,-29.07 C80,-29.07 80,-43.61 80,-43.61 C80,-57.09 64.61,-64.79 53.82,-56.7c"/>
+ <path android:name="_R_G_L_3_G_D_0_P_0" android:fillColor="#00ac47" android:fillAlpha="1" android:fillType="nonZero" android:pathData="M80.64 60 C80.64,60 106.09,60 106.09,60 C112.12,60 117,55.12 117,49.09 C117,49.09 117,-60 117,-60 C117,-60 80.64,-60 80.64,-60 C80.64,-60 80.64,60 80.64,60c "/>
+ </group>
+ <group android:name="_R_G_L_2_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <clip-path android:name="mask_x" android:pathData="M53.82 -56.7 C53.82,-56.7 43.64,-49.06 43.64,-49.06 C43.64,-49.06 0,-16.34 0,-16.34 C0,-16.34 -43.64,-49.06 -43.64,-49.06 C-43.64,-49.06 -53.82,-56.7 -53.82,-56.7 C-64.6,-64.79 -80,-57.09 -80,-43.61 C-80,-43.61 -80,-29.07 -80,-29.07 C-80,-29.07 -80,49.09 -80,49.09 C-80,55.12 -75.12,60 -69.09,60 C-69.09,60 -43.64,60 -43.64,60 C-43.64,60 -43.64,-1.8 -43.64,-1.8 C-43.64,-1.8 0,30.92 0,30.92 C0,30.92 43.64,-1.8 43.64,-1.8 C43.64,-1.8 43.64,60 43.64,60 C43.64,60 69.09,60 69.09,60 C75.12,60 80,55.12 80,49.09 C80,49.09 80,-29.07 80,-29.07 C80,-29.07 80,-43.61 80,-43.61 C80,-57.09 64.61,-64.79 53.82,-56.7c"/>
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#fe2c25" android:fillAlpha="1" android:fillType="nonZero" android:pathData="M53.82 -104.7 C53.82,-104.7 0,-64.34 0,-64.34 C0,-64.34 -53.82,-104.7 -53.82,-104.7 C-64.6,-112.79 -80,-105.09 -80,-91.61 C-80,-91.61 -80,-77.07 -80,-77.07 C-80,-77.07 0,-17.08 0,-17.08 C0,-17.08 80,-77.07 80,-77.07 C80,-77.07 80,-91.61 80,-91.61 C80,-105.09 64.61,-112.79 53.82,-104.7c "/>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <clip-path android:name="mask_x" android:pathData="M43.64 -1.8 C43.64,-1.8 43.64,-49.06 43.64,-49.06 C43.64,-49.06 53.82,-56.7 53.82,-56.7 C64.61,-64.79 80,-57.09 80,-43.61 C80,-43.61 80,-1.8 80,-1.8 C80,-1.8 43.64,-1.8 43.64,-1.8c"/>
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffba00" android:fillAlpha="1" android:fillType="nonZero" android:pathData="M80.64 -135 C80.64,-135 117,-135 117,-135 C117,-135 117,-104.07 117,-104.07 C117,-104.07 80.64,-76.8 80.64,-76.8 C80.64,-76.8 80.64,-135 80.64,-135c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="216" android:translateY="216" android:scaleX="1.5" android:scaleY="1.5">
+ <clip-path android:name="mask_x" android:pathData="M-43.64 -1.8 C-43.64,-1.8 -80,-1.8 -80,-1.8 C-80,-1.8 -80,-43.61 -80,-43.61 C-80,-57.09 -64.6,-64.79 -53.82,-56.7 C-53.82,-56.7 -43.64,-49.06 -43.64,-49.06 C-43.64,-49.06 -43.64,-1.8 -43.64,-1.8c"/>
+ <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#d70007" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-117 -104.07 C-117,-104.07 -117,-135 -117,-135 C-117,-135 -80.64,-135 -80.64,-135 C-80.64,-135 -80.64,-76.8 -80.64,-76.8 C-80.64,-76.8 -117,-104.07 -117,-104.07c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_4_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="1567" android:startOffset="233" android:valueFrom="M-80.39 60 C-80.39,60 -105.84,60 -105.84,60 C-111.87,60 -116.75,55.12 -116.75,49.09 C-116.75,49.09 -116.75,-60 -116.75,-60 C-116.75,-60 -80.39,-60 -80.39,-60 C-80.39,-60 -80.39,60 -80.39,60c " android:valueTo=" M-43.64 60 C-43.64,60 -69.09,60 -69.09,60 C-75.12,60 -80,55.12 -80,49.09 C-80,49.09 -80,-60 -80,-60 C-80,-60 -43.64,-60 -43.64,-60 C-43.64,-60 -43.64,60 -43.64,60c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_3_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="1567" android:startOffset="233" android:valueFrom=" M80.64 60 C80.64,60 106.09,60 106.09,60 C112.12,60 117,55.12 117,49.09 C117,49.09 117,-60 117,-60 C117,-60 80.64,-60 80.64,-60 C80.64,-60 80.64,60 80.64,60c " android:valueTo=" M43.64 60 C43.64,60 69.09,60 69.09,60 C75.12,60 80,55.12 80,49.09 C80,49.09 80,-60 80,-60 C80,-60 43.64,-60 43.64,-60 C43.64,-60 43.64,60 43.64,60c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="1567" android:startOffset="233" android:valueFrom="M53.82 -104.7 C53.82,-104.7 0,-64.34 0,-64.34 C0,-64.34 -53.82,-104.7 -53.82,-104.7 C-64.6,-112.79 -80,-105.09 -80,-91.61 C-80,-91.61 -80,-77.07 -80,-77.07 C-80,-77.07 0,-17.08 0,-17.08 C0,-17.08 80,-77.07 80,-77.07 C80,-77.07 80,-91.61 80,-91.61 C80,-105.09 64.61,-112.79 53.82,-104.7c" android:valueTo="M53.82 -56.7 C53.82,-56.7 0,-16.34 0,-16.34 C0,-16.34 -53.82,-56.7 -53.82,-56.7 C-64.6,-64.79 -80,-57.09 -80,-43.61 C-80,-43.61 -80,-29.07 -80,-29.07 C-80,-29.07 0,30.92 0,30.92 C0,30.92 80,-29.07 80,-29.07 C80,-29.07 80,-43.61 80,-43.61 C80,-57.09 64.61,-64.79 53.82,-56.7c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="1567" android:startOffset="233" android:valueFrom=" M80.64 -135 C80.64,-135 117,-135 117,-135 C117,-135 117,-104.07 117,-104.07 C117,-104.07 80.64,-76.8 80.64,-76.8 C80.64,-76.8 80.64,-135 80.64,-135c " android:valueTo=" M43.64 -60 C43.64,-60 80,-60 80,-60 C80,-60 80,-29.07 80,-29.07 C80,-29.07 43.64,-1.8 43.64,-1.8 C43.64,-1.8 43.64,-60 43.64,-60c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData" android:duration="1567" android:startOffset="233" android:valueFrom=" M-117 -104.07 C-117,-104.07 -117,-135 -117,-135 C-117,-135 -80.64,-135 -80.64,-135 C-80.64,-135 -80.64,-76.8 -80.64,-76.8 C-80.64,-76.8 -117,-104.07 -117,-104.07c " android:valueTo=" M-80 -29.07 C-80,-29.07 -80,-60 -80,-60 C-80,-60 -43.64,-60 -43.64,-60 C-43.64,-60 -43.64,-1.8 -43.64,-1.8 C-43.64,-1.8 -80,-29.07 -80,-29.07c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX" android:duration="2017" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index e51ed29adebf..9b742d96e35b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -53,4 +53,11 @@
<style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
<item name="android:windowDisablePreview">true</item>
</style>
+
+ <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault">
+ <!-- Splashscreen Attributes -->
+ <item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item>
+ <!-- Here we want to match the duration of our AVD -->
+ <item name="android:windowSplashScreenAnimationDuration">900</item>
+ </style>
</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 2795a6c43015..8b334c0a8588 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -73,6 +73,12 @@ public class ActivityOptions {
FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
}
+ public static class PortraitImmersiveActivity {
+ public static final String LABEL = "PortraitImmersiveActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PortraitImmersiveActivity");
+ }
+
public static class TransparentActivity {
public static final String LABEL = "TransparentActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
@@ -178,6 +184,12 @@ public class ActivityOptions {
FLICKER_APP_PACKAGE + ".LaunchNewActivity");
}
+ public static class TransferSplashscreenActivity {
+ public static final String LABEL = "TransferSplashscreenActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".TransferSplashscreenActivity");
+ }
+
public static class Pip {
// Test App > Pip Activity
public static final String LABEL = "PipActivity";
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 cdb1d42bd4f2..12eaad108fc6 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
@@ -82,6 +82,8 @@ public class PipActivity extends Activity {
"com.android.wm.shell.flicker.testapp.SWITCH_OFF";
private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON";
private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR";
+ private static final String ACTION_ASPECT_RATIO =
+ "com.android.wm.shell.flicker.testapp.ASPECT_RATIO";
private final PictureInPictureParams.Builder mPipParamsBuilder =
new PictureInPictureParams.Builder()
@@ -109,6 +111,9 @@ public class PipActivity extends Activity {
case ACTION_CLEAR:
mPipParamsBuilder.setActions(Collections.emptyList());
break;
+ case ACTION_ASPECT_RATIO:
+ mPipParamsBuilder.setAspectRatio(RATIO_TALL);
+ break;
case ACTION_NO_OP:
return;
default:
@@ -190,6 +195,7 @@ public class PipActivity extends Activity {
filter.addAction(ACTION_CLEAR);
filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
filter.addAction(ACTION_ENTER_PIP);
+ filter.addAction(ACTION_ASPECT_RATIO);
registerReceiver(mBroadcastReceiver, filter);
handleIntentExtra(getIntent());
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java
index a7bd771400c0..0a7f81cba747 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/src/com/android/apkverity/DummyActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitImmersiveActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.apkverity;
+package com.android.server.wm.flicker.testapp;
-import android.app.Activity;
-
-/** Placeholder class just to generate some dex */
-public class DummyActivity extends Activity {}
+public class PortraitImmersiveActivity extends GameActivity {
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransferSplashscreenActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransferSplashscreenActivity.java
new file mode 100644
index 000000000000..0323286a0e21
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransferSplashscreenActivity.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.window.SplashScreen;
+import android.window.SplashScreenView;
+
+public class TransferSplashscreenActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final SplashScreen splashScreen = getSplashScreen();
+ // Register setOnExitAnimationListener to transfer the splash screen window to client.
+ splashScreen.setOnExitAnimationListener(this::onSplashScreenExit);
+ final View content = findViewById(android.R.id.content);
+ // By register preDrawListener to defer app window draw signal about 500ms, which to ensure
+ // the splash screen must show when cold launch.
+ content.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ final long mCreateTime = SystemClock.uptimeMillis();
+ @Override
+ public boolean onPreDraw() {
+ return SystemClock.uptimeMillis() - mCreateTime > 500;
+ }
+ }
+ );
+ }
+
+ private void onSplashScreenExit(SplashScreenView view) {
+ view.remove();
+ }
+}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/FsVerityTest/Android.bp
index f026bea80470..53606a32b185 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/FsVerityTest/Android.bp
@@ -22,7 +22,7 @@ package {
}
java_test_host {
- name: "ApkVerityTest",
+ name: "FsVerityTest",
srcs: ["src/**/*.java"],
libs: [
"tradefed",
@@ -30,8 +30,10 @@ java_test_host {
"compatibility-host-util",
],
static_libs: [
+ "android.security.flags-aconfig-java-host",
"block_device_writer_jar",
"frameworks-base-hostutils",
+ "flag-junit-host",
],
test_suites: [
"general-tests",
@@ -41,14 +43,6 @@ java_test_host {
"block_device_writer",
],
data: [
- ":ApkVerityTestCertDer",
- ":ApkVerityTestApp",
- ":ApkVerityTestAppFsvSig",
- ":ApkVerityTestAppDm",
- ":ApkVerityTestAppDmFsvSig",
- ":ApkVerityTestAppSplit",
- ":ApkVerityTestAppSplitFsvSig",
- ":ApkVerityTestAppSplitDm",
- ":ApkVerityTestAppSplitDmFsvSig",
+ ":FsVerityTestApp",
],
}
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/FsVerityTest/AndroidTest.xml
index 4487cefb4f9c..d2537f6410e8 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/FsVerityTest/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="APK fs-verity integration/regression test">
+<configuration description="fs-verity end-to-end test">
<option name="test-suite-tag" value="apct" />
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
@@ -24,19 +24,9 @@
<!-- This test requires root to write against block device. -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
- <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
- <!-- Disable package verifier prevents it holding the target APK's fd that prevents cache
- eviction. -->
- <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
- <option name="restore-settings" value="true" />
-
- <!-- Skip in order to prevent reboot that confuses the test flow. -->
- <option name="force-skip-system-props" value="true" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="FsVerityTestApp.apk"/>
+ <option name="cleanup-apks" value="true"/>
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
@@ -48,9 +38,7 @@
<option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
</target_preparer>
- <!-- Skip on HWASan. TODO(b/232288278): Re-enable -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
- <option name="jar" value="ApkVerityTest.jar" />
+ <option name="jar" value="FsVerityTest.jar" />
</test>
</configuration>
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp b/tests/FsVerityTest/FsVerityTestApp/Android.bp
index adf8f9f9d321..43da3ff9fec1 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/Android.bp
+++ b/tests/FsVerityTest/FsVerityTestApp/Android.bp
@@ -22,17 +22,8 @@ package {
}
android_test_helper_app {
- name: "ApkVerityTestApp",
- manifest: "AndroidManifest.xml",
- srcs: ["src/**/*.java"],
-}
-
-android_test_helper_app {
- name: "ApkVerityTestAppSplit",
- manifest: "feature_split/AndroidManifest.xml",
- srcs: ["src/**/*.java"],
- aaptflags: [
- "--custom-package com.android.apkverity.feature_x",
- "--package-id 0x80",
- ],
+ name: "FsVerityTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ static_libs: ["compatibility-device-util-axt"],
}
diff --git a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml b/tests/FsVerityTest/FsVerityTestApp/AndroidManifest.xml
index 0b3ff77c2cdf..42fe49be66d9 100644
--- a/tests/ApkVerityTest/ApkVerityTestApp/AndroidManifest.xml
+++ b/tests/FsVerityTest/FsVerityTestApp/AndroidManifest.xml
@@ -16,8 +16,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.apkverity">
+ package="com.android.fsverity">
<application>
<activity android:name=".DummyActivity"/>
</application>
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.fsverity"
+ android:label="Helper app of fs-verity test">
+ </instrumentation>/>
</manifest>
diff --git a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
new file mode 100644
index 000000000000..2ed4fec4a93c
--- /dev/null
+++ b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+import android.security.FileIntegrityManager;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Test helper that works with the host-side test to set up a test file, and to verify fs-verity
+ * verification is done expectedly.
+ */
+public class Helper {
+ private static final String TAG = "FsVerityTest";
+
+ private static final String FILENAME = "test.file";
+
+ private static final long BLOCK_SIZE = 4096;
+
+ @Test
+ public void prepareTest() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+ android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+
+ String basename = testArgs.getString("basename");
+ context.deleteFile(basename);
+
+ assertThat(testArgs).isNotNull();
+ int fileSize = Integer.parseInt(testArgs.getString("fileSize"));
+ Log.d(TAG, "Preparing test file with size " + fileSize);
+
+ byte[] bytes = new byte[8192];
+ Arrays.fill(bytes, (byte) '1');
+ try (FileOutputStream os = context.openFileOutput(basename, Context.MODE_PRIVATE)) {
+ for (int i = 0; i < fileSize; i += bytes.length) {
+ if (i + bytes.length > fileSize) {
+ os.write(bytes, 0, fileSize % bytes.length);
+ } else {
+ os.write(bytes);
+ }
+ }
+ }
+
+ // Enable fs-verity
+ FileIntegrityManager fim = context.getSystemService(FileIntegrityManager.class);
+ fim.setupFsVerity(context.getFileStreamPath(basename));
+ }
+
+ @Test
+ public void verifyFileRead() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+
+ // Collect indices that the backing blocks are supposed to be corrupted.
+ android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+ assertThat(testArgs).isNotNull();
+ String filePath = testArgs.getString("filePath");
+ String csv = testArgs.getString("brokenBlockIndicesCsv");
+ Log.d(TAG, "brokenBlockIndicesCsv: " + csv);
+ String[] strings = csv.split(",");
+ var corrupted = new ArrayList(strings.length);
+ for (int i = 0; i < strings.length; i++) {
+ corrupted.add(Integer.parseInt(strings[i]));
+ }
+
+ // Expect the read to succeed or fail per the prior.
+ try (var file = new RandomAccessFile(filePath, "r")) {
+ long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE;
+ for (int i = 0; i < (int) total_blocks; i++) {
+ file.seek(i * BLOCK_SIZE);
+ if (corrupted.contains(i)) {
+ Log.d(TAG, "Expecting read at block #" + i + " to fail");
+ assertThrows(IOException.class, () -> file.read());
+ } else {
+ assertThat(file.readByte()).isEqualTo('1');
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ApkVerityTest/OWNERS b/tests/FsVerityTest/OWNERS
index d67285ede44a..d67285ede44a 100644
--- a/tests/ApkVerityTest/OWNERS
+++ b/tests/FsVerityTest/OWNERS
diff --git a/tests/FsVerityTest/TEST_MAPPING b/tests/FsVerityTest/TEST_MAPPING
new file mode 100644
index 000000000000..7d59d7765183
--- /dev/null
+++ b/tests/FsVerityTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "FsVerityTest"
+ }
+ ]
+}
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/FsVerityTest/block_device_writer/Android.bp
index 0002447d17f2..0002447d17f2 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/FsVerityTest/block_device_writer/Android.bp
diff --git a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp b/tests/FsVerityTest/block_device_writer/block_device_writer.cpp
index 02dfd732a716..02dfd732a716 100644
--- a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
+++ b/tests/FsVerityTest/block_device_writer/block_device_writer.cpp
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/FsVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 9be02ec3be86..9be02ec3be86 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/FsVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
new file mode 100644
index 000000000000..be479f205ff2
--- /dev/null
+++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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.fsverity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
+import android.security.Flags;
+
+import com.android.blockdevicewriter.BlockDeviceWriter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This test verifies fs-verity works end-to-end. There is a corresponding helper app.
+ *
+ * <p>The helper app uses a FileIntegrityManager API to enable fs-verity to a file. The host test
+ * here * tampers with the file's backing storage, then tells the helper app to read and expect
+ * success/failure on read.
+ *
+ * <p>In order to make sure a block of the file is readable only if the underlying block on disk
+ * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
+ * address against the block device.
+ */
+@RootPermissionTest
+@RunWith(DeviceJUnit4ClassRunner.class)
+@RequiresFlagsEnabled(Flags.FLAG_FSVERITY_API)
+public class FsVerityHostTest extends BaseHostJUnit4Test {
+ private static final String TARGET_PACKAGE = "com.android.fsverity";
+
+ private static final String BASENAME = "test.file";
+ private static final String TARGET_PATH = "/data/data/" + TARGET_PACKAGE + "/files/" + BASENAME;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
+ @Test
+ public void testFsVeritySmallFile() throws Exception {
+ prepareTest(10000);
+
+ ITestDevice device = getDevice();
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 0);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 8192);
+ BlockDeviceWriter.dropCaches(device);
+
+ verifyRead(TARGET_PATH, "0,2");
+ }
+
+ @Test
+ public void testFsVerityLargerFileWithOneMoreMerkleTreeLevel() throws Exception {
+ prepareTest(128 * 4096 + 1);
+
+ ITestDevice device = getDevice();
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 100 * 4096);
+ BlockDeviceWriter.damageFileAgainstBlockDevice(device, TARGET_PATH, 128 * 4096 + 1);
+ BlockDeviceWriter.dropCaches(device);
+
+ verifyRead(TARGET_PATH, "1,100,128");
+ }
+
+ private void prepareTest(int fileSize) throws Exception {
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+ options.setTestClassName(TARGET_PACKAGE + ".Helper");
+ options.setTestMethodName("prepareTest");
+ options.addInstrumentationArg("basename", BASENAME);
+ options.addInstrumentationArg("fileSize", String.valueOf(fileSize));
+ assertThat(runDeviceTests(options)).isTrue();
+ }
+
+ private void verifyRead(String path, String indicesCsv) throws Exception {
+ DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
+ options.setTestClassName(TARGET_PACKAGE + ".Helper");
+ options.setTestMethodName("verifyFileRead");
+ options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
+ options.addInstrumentationArg("filePath", TARGET_PATH);
+ assertThat(runDeviceTests(options)).isTrue();
+ }
+}
diff --git a/tests/ApkVerityTest/testdata/Android.bp b/tests/FsVerityTest/testdata/Android.bp
index ccfc4c99a347..2d578d36423d 100644
--- a/tests/ApkVerityTest/testdata/Android.bp
+++ b/tests/FsVerityTest/testdata/Android.bp
@@ -37,51 +37,3 @@ filegroup {
name: "ApkVerityTestCertDer",
srcs: ["ApkVerityTestCert.der"],
}
-
-filegroup {
- name: "ApkVerityTestAppDm",
- srcs: ["ApkVerityTestApp.dm"],
-}
-
-filegroup {
- name: "ApkVerityTestAppSplitDm",
- srcs: ["ApkVerityTestAppSplit.dm"],
-}
-
-genrule_defaults {
- name: "apk_verity_sig_gen_default",
- tools: ["fsverity"],
- tool_files: [":ApkVerityTestKeyPem", ":ApkVerityTestCertPem"],
- cmd: "$(location fsverity) sign $(in) $(out) " +
- "--key=$(location :ApkVerityTestKeyPem) " +
- "--cert=$(location :ApkVerityTestCertPem) " +
- "> /dev/null",
-}
-
-genrule {
- name: "ApkVerityTestAppFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestApp"],
- out: ["ApkVerityTestApp.apk.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppDmFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppDm"],
- out: ["ApkVerityTestApp.dm.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppSplitFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppSplit"],
- out: ["ApkVerityTestAppSplit.apk.fsv_sig"],
-}
-
-genrule {
- name: "ApkVerityTestAppSplitDmFsvSig",
- defaults: ["apk_verity_sig_gen_default"],
- srcs: [":ApkVerityTestAppSplitDm"],
- out: ["ApkVerityTestAppSplit.dm.fsv_sig"],
-}
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestCert.der b/tests/FsVerityTest/testdata/ApkVerityTestCert.der
index fe9029b53aa1..fe9029b53aa1 100644
--- a/tests/ApkVerityTest/testdata/ApkVerityTestCert.der
+++ b/tests/FsVerityTest/testdata/ApkVerityTestCert.der
Binary files differ
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestCert.pem b/tests/FsVerityTest/testdata/ApkVerityTestCert.pem
index 6c0b7b1f635a..6c0b7b1f635a 100644
--- a/tests/ApkVerityTest/testdata/ApkVerityTestCert.pem
+++ b/tests/FsVerityTest/testdata/ApkVerityTestCert.pem
diff --git a/tests/ApkVerityTest/testdata/ApkVerityTestKey.pem b/tests/FsVerityTest/testdata/ApkVerityTestKey.pem
index f0746c162421..f0746c162421 100644
--- a/tests/ApkVerityTest/testdata/ApkVerityTestKey.pem
+++ b/tests/FsVerityTest/testdata/ApkVerityTestKey.pem
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
index 8380dcf4b4a4..d939d91cd527 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -20,6 +20,8 @@ import android.annotation.Nullable;
import android.graphics.PointF;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
+import android.os.CancellationSignal;
+import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -31,7 +33,9 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.InsertModeGesture;
import android.view.inputmethod.JoinOrSplitGesture;
+import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
import android.widget.AdapterView;
@@ -47,11 +51,14 @@ import java.util.function.IntConsumer;
public class HandwritingIme extends InputMethodService {
private static final int OP_NONE = 0;
+ // ------- PreviewableHandwritingGesture BEGIN -----
private static final int OP_SELECT = 1;
private static final int OP_DELETE = 2;
+ // ------- PreviewableHandwritingGesture END -----
private static final int OP_INSERT = 3;
private static final int OP_REMOVE_SPACE = 4;
private static final int OP_JOIN_OR_SPLIT = 5;
+ private static final int OP_INSERT_MODE = 6;
private InkView mInk;
@@ -70,6 +77,10 @@ public class HandwritingIme extends InputMethodService {
private final IntConsumer mResultConsumer = value -> Log.d(TAG, "Gesture result: " + value);
+ private CancellationSignal mCancellationSignal = new CancellationSignal();
+ private boolean mUsePreview;
+ private CheckBox mGesturePreviewCheckbox;
+
interface HandwritingFinisher {
void finish();
}
@@ -98,73 +109,107 @@ public class HandwritingIme extends InputMethodService {
private void onStylusEvent(@Nullable MotionEvent event) {
// TODO Hookup recognizer here
+ HandwritingGesture gesture;
switch (event.getAction()) {
- case MotionEvent.ACTION_UP: {
- if (areRichGesturesEnabled()) {
- HandwritingGesture gesture = null;
- switch (mRichGestureMode) {
- case OP_SELECT:
- gesture = new SelectGesture.Builder()
- .setGranularity(mRichGestureGranularity)
- .setSelectionArea(getSanitizedRectF(mRichGestureStartPoint.x,
- mRichGestureStartPoint.y, event.getX(), event.getY()))
- .setFallbackText("fallback text")
- .build();
- break;
- case OP_DELETE:
- gesture = new DeleteGesture.Builder()
- .setGranularity(mRichGestureGranularity)
- .setDeletionArea(getSanitizedRectF(mRichGestureStartPoint.x,
- mRichGestureStartPoint.y, event.getX(), event.getY()))
- .setFallbackText("fallback text")
- .build();
- break;
- case OP_INSERT:
- gesture = new InsertGesture.Builder()
- .setInsertionPoint(new PointF(
- mRichGestureStartPoint.x, mRichGestureStartPoint.y))
- .setTextToInsert(" ")
- .setFallbackText("fallback text")
- .build();
- break;
- case OP_REMOVE_SPACE:
- gesture = new RemoveSpaceGesture.Builder()
- .setPoints(
- new PointF(mRichGestureStartPoint.x,
- mRichGestureStartPoint.y),
- new PointF(event.getX(), event.getY()))
- .setFallbackText("fallback text")
- .build();
- break;
- case OP_JOIN_OR_SPLIT:
- gesture = new JoinOrSplitGesture.Builder()
- .setJoinOrSplitPoint(new PointF(
- mRichGestureStartPoint.x, mRichGestureStartPoint.y))
- .setFallbackText("fallback text")
- .build();
- break;
+ case MotionEvent.ACTION_MOVE:
+ if (mUsePreview && areRichGesturesEnabled()) {
+ gesture = computeGesture(event, true /* isPreview */);
+ if (gesture == null) {
+ Log.e(TAG, "Preview not supported for gesture: " + mRichGestureMode);
+ return;
}
+ performGesture(gesture, true /* isPreview */);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (areRichGesturesEnabled()) {
+ gesture = computeGesture(event, false /* isPreview */);
if (gesture == null) {
// This shouldn't happen
Log.e(TAG, "Unrecognized gesture mode: " + mRichGestureMode);
return;
}
- performGesture(gesture);
+ performGesture(gesture, false /* isPreview */);
} else {
// insert random ASCII char
sendKeyChar((char) (56 + new Random().nextInt(66)));
}
return;
- }
case MotionEvent.ACTION_DOWN: {
if (areRichGesturesEnabled()) {
mRichGestureStartPoint = new PointF(event.getX(), event.getY());
}
- return;
}
}
}
+ private HandwritingGesture computeGesture(MotionEvent event, boolean isPreview) {
+ HandwritingGesture gesture = null;
+ switch (mRichGestureMode) {
+ case OP_SELECT:
+ gesture = new SelectGesture.Builder()
+ .setGranularity(mRichGestureGranularity)
+ .setSelectionArea(getSanitizedRectF(mRichGestureStartPoint.x,
+ mRichGestureStartPoint.y, event.getX(), event.getY()))
+ .setFallbackText("fallback text")
+ .build();
+ break;
+ case OP_DELETE:
+ gesture = new DeleteGesture.Builder()
+ .setGranularity(mRichGestureGranularity)
+ .setDeletionArea(getSanitizedRectF(mRichGestureStartPoint.x,
+ mRichGestureStartPoint.y, event.getX(), event.getY()))
+ .setFallbackText("fallback text")
+ .build();
+ break;
+ case OP_INSERT:
+ gesture = new InsertGesture.Builder()
+ .setInsertionPoint(new PointF(
+ mRichGestureStartPoint.x, mRichGestureStartPoint.y))
+ .setTextToInsert(" ")
+ .setFallbackText("fallback text")
+ .build();
+ break;
+ case OP_REMOVE_SPACE:
+ if (isPreview) {
+ break;
+ }
+ gesture = new RemoveSpaceGesture.Builder()
+ .setPoints(
+ new PointF(mRichGestureStartPoint.x,
+ mRichGestureStartPoint.y),
+ new PointF(event.getX(), event.getY()))
+ .setFallbackText("fallback text")
+ .build();
+ break;
+ case OP_JOIN_OR_SPLIT:
+ if (isPreview) {
+ break;
+ }
+ gesture = new JoinOrSplitGesture.Builder()
+ .setJoinOrSplitPoint(new PointF(
+ mRichGestureStartPoint.x, mRichGestureStartPoint.y))
+ .setFallbackText("fallback text")
+ .build();
+ break;
+ case OP_INSERT_MODE:
+ if (isPreview) {
+ break;
+ }
+ mCancellationSignal = new CancellationSignal();
+ InsertModeGesture img = new InsertModeGesture.Builder()
+ .setInsertionPoint(new PointF(
+ mRichGestureStartPoint.x, mRichGestureStartPoint.y))
+ .setFallbackText("fallback text")
+ .setCancellationSignal(mCancellationSignal)
+ .build();
+ gesture = img;
+ new Handler().postDelayed(() -> img.getCancellationSignal().cancel(), 5000);
+ break;
+ }
+ return gesture;
+ }
+
/**
* sanitize values to support rectangles in all cases.
*/
@@ -193,10 +238,14 @@ public class HandwritingIme extends InputMethodService {
return rectF;
}
- private void performGesture(HandwritingGesture gesture) {
+ private void performGesture(HandwritingGesture gesture, boolean isPreview) {
InputConnection ic = getCurrentInputConnection();
if (getCurrentInputStarted() && ic != null) {
- ic.performHandwritingGesture(gesture, Runnable::run, mResultConsumer);
+ if (isPreview) {
+ ic.previewHandwritingGesture((PreviewableHandwritingGesture) gesture, null);
+ } else {
+ ic.performHandwritingGesture(gesture, Runnable::run, mResultConsumer);
+ }
} else {
// This shouldn't happen
Log.e(TAG, "No active InputConnection");
@@ -216,12 +265,21 @@ public class HandwritingIme extends InputMethodService {
layout.addView(getRichGestureActionsSpinner());
layout.addView(getRichGestureGranularitySpinner());
layout.addView(getBoundsInfoCheckBoxes());
+ layout.addView(getPreviewCheckBox());
layout.setBackgroundColor(getColor(R.color.holo_green_light));
view.addView(layout);
return view;
}
+ private View getPreviewCheckBox() {
+ mGesturePreviewCheckbox = new CheckBox(this);
+ mGesturePreviewCheckbox.setText("Use Gesture Previews (for Previewable Gestures)");
+ mGesturePreviewCheckbox.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> mUsePreview = isChecked);
+ return mGesturePreviewCheckbox;
+ }
+
private View getRichGestureActionsSpinner() {
if (mRichGestureModeSpinner != null) {
return mRichGestureModeSpinner;
@@ -236,6 +294,7 @@ public class HandwritingIme extends InputMethodService {
"Rich gesture INSERT",
"Rich gesture REMOVE SPACE",
"Rich gesture JOIN OR SPLIT",
+ "Rich gesture INSERT MODE",
};
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_dropdown_item, items);
@@ -245,8 +304,13 @@ public class HandwritingIme extends InputMethodService {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mRichGestureMode = position;
- mRichGestureGranularitySpinner.setEnabled(
- mRichGestureMode == OP_SELECT || mRichGestureMode == OP_DELETE);
+ boolean supportsGranularityAndPreview =
+ mRichGestureMode == OP_SELECT || mRichGestureMode == OP_DELETE;
+ mRichGestureGranularitySpinner.setEnabled(supportsGranularityAndPreview);
+ mGesturePreviewCheckbox.setEnabled(supportsGranularityAndPreview);
+ if (!supportsGranularityAndPreview) {
+ mUsePreview = false;
+ }
Log.d(TAG, "Setting RichGesture Mode " + mRichGestureMode);
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 80c7a21dc11b..db3a992b9c7b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -789,6 +789,15 @@
</intent-filter>
</activity>
+ <activity android:name="BackdropBlurActivity"
+ android:label="RenderEffect/BackdropBlur"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="BlurActivity"
android:label="RenderEffect/Blur"
android:exported="true">
diff --git a/tests/HwAccelerationTest/res/drawable/robot_repeated.xml b/tests/HwAccelerationTest/res/drawable/robot_repeated.xml
new file mode 100644
index 000000000000..bbb15b71c3a5
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/robot_repeated.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/robot"
+ android:tileMode="repeat" android:gravity="fill" /> \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
new file mode 100644
index 000000000000..8086b29df7cd
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BackdropBlurActivity.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Outline;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ScrollView;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BackdropBlurActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final ScrollView scrollView = new ScrollView(this);
+ final FrameLayout innerFrame = new FrameLayout(this);
+ final View backgroundView = new View(this);
+ backgroundView.setBackgroundResource(R.drawable.robot_repeated);
+ innerFrame.addView(backgroundView, ViewGroup.LayoutParams.MATCH_PARENT, 10000);
+ scrollView.addView(innerFrame,
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ final FrameLayout contentView = new FrameLayout(this);
+ contentView.addView(scrollView,
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ contentView.addView(new BackdropBlurView(this), 300, 300);
+ setContentView(contentView);
+ }
+
+ private static class BackdropBlurView extends View {
+ private final float mBlurRadius = 60f;
+ private final float mSaturation = 1.8f;
+
+ private float mDownOffsetX;
+ private float mDownOffsetY;
+
+ BackdropBlurView(Context c) {
+ super(c);
+
+ // init RenderEffect.
+ final RenderEffect blurEffect = RenderEffect.createBlurEffect(
+ mBlurRadius, mBlurRadius,
+ null, Shader.TileMode.MIRROR // TileMode.MIRROR is better for blur.
+ );
+
+ final ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(mSaturation);
+ final RenderEffect effect = RenderEffect.createColorFilterEffect(
+ new ColorMatrixColorFilter(colorMatrix), blurEffect
+ );
+ setBackdropRenderEffect(effect);
+
+ // clip to a round outline.
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View v, Outline outline) {
+ outline.setOval(0, 0, v.getWidth(), v.getHeight());
+ }
+ });
+ setClipToOutline(true);
+
+ animate().setInterpolator(new DecelerateInterpolator(2.0f));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawColor(0x99F0F0F0);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownOffsetX = event.getRawX() - getTranslationX();
+ mDownOffsetY = event.getRawY() - getTranslationY();
+ animate().scaleX(1.5f).scaleY(1.5f).start();
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ animate().scaleX(1f).scaleY(1f).start();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ setTranslationX(event.getRawX() - mDownOffsetX);
+ setTranslationY(event.getRawY() - mDownOffsetY);
+ break;
+ }
+ return true;
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
index ef49c7fd00a9..cb16191423ce 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapsAlphaActivity.java
@@ -48,15 +48,15 @@ public class BitmapsAlphaActivity extends Activity {
BitmapsView(Context c) {
super(c);
- Log.d("OpenGLRenderer", "Loading sunset1, default options");
+ Log.d("HWUI", "Loading sunset1, default options");
mBitmap1 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset1);
- Log.d("OpenGLRenderer", "Loading sunset2, default options");
+ Log.d("HWUI", "Loading sunset2, default options");
mBitmap2 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset2);
- Log.d("OpenGLRenderer", "Loading sunset3, forcing ARGB-8888");
+ Log.d("HWUI", "Loading sunset3, forcing ARGB-8888");
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
mBitmap3 = BitmapFactory.decodeResource(c.getResources(), R.drawable.sunset3, opts);
- Log.d("OpenGLRenderer", " has bitmap alpha? " + mBitmap3.hasAlpha());
+ Log.d("HWUI", " has bitmap alpha? " + mBitmap3.hasAlpha());
mBitmapPaint = new Paint();
}
@@ -65,7 +65,7 @@ public class BitmapsAlphaActivity extends Activity {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Log.d("OpenGLRenderer", "================= Draw");
+ Log.d("HWUI", "================= Draw");
canvas.translate(120.0f, 50.0f);
canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBitmapPaint);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
index 1c82e9bbdf9b..dbfb4ca7c8fe 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClearActivity.java
@@ -77,7 +77,7 @@ public class ClearActivity extends Activity {
canvas.drawPath(mPath, mClearPaint);
}
canvas.restore();
- canvas.drawText("OpenGLRenderer", 50.0f, 50.0f, mClearPaint);
+ canvas.drawText("HWUI", 50.0f, 50.0f, mClearPaint);
mClearPaint.setColor(0xff000000);
canvas.drawRect(800.0f, 100.0f, 900.0f, 200.0f, mClearPaint);
mClearPaint.setColor(0x0000ff00);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
index e2d17cdbe9e6..1f4c6c53b260 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
@@ -17,6 +17,7 @@
package com.android.test.hwui;
import android.app.Activity;
+import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorSpace;
@@ -72,6 +73,10 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb
private int[] mGradientEndColors = {0xFFFFFFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF};
private String[] mGradientColorNames = {"Grayscale", "Red", "Green", "Blue"};
+ private int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+ private int[] mColorModes = {ActivityInfo.COLOR_MODE_DEFAULT, ActivityInfo.COLOR_MODE_HDR};
+ private String[] mColorModeNames = {"DEFAULT", "HDR"};
+
private final ExecutorService mBufferFenceExecutor = Executors.newFixedThreadPool(1);
private final ExecutorService mBufferExecutor = Executors.newFixedThreadPool(1);
@@ -139,6 +144,15 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb
gradientColorSpinner
.setOnItemSelectedListener(new GradientColorOnItemSelectedListener());
+ ArrayAdapter<String> colorModeAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_item, mColorModeNames);
+
+ colorModeAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Spinner colorModeSpinner = new Spinner(this);
+ colorModeSpinner.setAdapter(colorModeAdapter);
+ colorModeSpinner.setOnItemSelectedListener(new ColorModeOnItemSelectedListener());
+
mGradientBuffer = getGradientBuffer().get();
LinearLayout linearLayout = new LinearLayout(this);
@@ -169,6 +183,10 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
+ spinnerLayout.addView(colorModeSpinner, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
linearLayout.addView(spinnerLayout, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
@@ -187,6 +205,8 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb
linearLayout.addView(mSurfaceView, new LinearLayout.LayoutParams(WIDTH, HEIGHT));
setContentView(linearLayout);
+
+ getWindow().setColorMode(mColorMode);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -312,4 +332,22 @@ public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callb
}
}
+
+ private final class ColorModeOnItemSelectedListener
+ implements AdapterView.OnItemSelectedListener {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ ColorBitmapActivity.this.mColorMode = ColorBitmapActivity.this.mColorModes[position];
+ ColorBitmapActivity.this.getMainExecutor()
+ .execute(() -> {
+ ColorBitmapActivity.this.getWindow().setColorMode(mColorMode);
+ });
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ }
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
index 5192bfe84fef..11a2a4161a8b 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/QuickRejectActivity.java
@@ -53,30 +53,30 @@ public class QuickRejectActivity extends Activity {
super.onDraw(canvas);
int count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "count=" + count);
+ Log.d("HWUI", "count=" + count);
count = canvas.save();
- Log.d("OpenGLRenderer", "count after save=" + count);
+ Log.d("HWUI", "count after save=" + count);
count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "getSaveCount after save=" + count);
+ Log.d("HWUI", "getSaveCount after save=" + count);
canvas.restore();
count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "count after restore=" + count);
+ Log.d("HWUI", "count after restore=" + count);
canvas.save();
- Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ Log.d("HWUI", "count after save=" + canvas.getSaveCount());
canvas.save();
- Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ Log.d("HWUI", "count after save=" + canvas.getSaveCount());
canvas.save();
- Log.d("OpenGLRenderer", "count after save=" + canvas.getSaveCount());
+ Log.d("HWUI", "count after save=" + canvas.getSaveCount());
canvas.restoreToCount(count);
count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "count after restoreToCount=" + count);
+ Log.d("HWUI", "count after restoreToCount=" + count);
count = canvas.saveLayer(0, 0, 10, 10, mBitmapPaint, Canvas.ALL_SAVE_FLAG);
- Log.d("OpenGLRenderer", "count after saveLayer=" + count);
+ Log.d("HWUI", "count after saveLayer=" + count);
count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "getSaveCount after saveLayer=" + count);
+ Log.d("HWUI", "getSaveCount after saveLayer=" + count);
canvas.restore();
count = canvas.getSaveCount();
- Log.d("OpenGLRenderer", "count after restore=" + count);
+ Log.d("HWUI", "count after restore=" + count);
canvas.save();
canvas.clipRect(0.0f, 0.0f, 40.0f, 40.0f);
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 292fbcb0031e..cf2d5d69552f 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,6 +9,10 @@ package {
android_test {
name: "InputTests",
+ defaults: [
+ // For ExtendedMockito dependencies.
+ "modules-utils-testable-device-config-defaults",
+ ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -19,13 +23,27 @@ android_test {
platform_apis: true,
certificate: "platform",
static_libs: [
+ "androidx.test.core",
"androidx.test.ext.junit",
+ "androidx.test.ext.truth",
"androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.uiautomator_uiautomator",
+ "servicestests-utils",
+ "flag-junit",
+ "frameworks-base-testutils",
+ "hamcrest-library",
+ "kotlin-test",
"mockito-target-minus-junit4",
+ "platform-test-annotations",
"services.core.unboosted",
"testables",
+ "testng",
"truth",
- "androidx.test.uiautomator_uiautomator",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
],
test_suites: ["device-tests"],
}
diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml
index 20f564e80b3d..3b723ddf811f 100644
--- a/tests/Input/AndroidManifest.xml
+++ b/tests/Input/AndroidManifest.xml
@@ -16,11 +16,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.input">
+
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<uses-permission android:name="android.permission.MONITOR_INPUT"/>
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
- <uses-permission android:name="android.permission.INJECT_EVENTS"/>
- <application android:label="InputTest">
+ <application android:label="InputTest" android:debuggable="true">
<activity android:name=".UnresponsiveGestureMonitorActivity"
android:label="Unresponsive gesture monitor"
diff --git a/tests/Input/res/raw/dummy_keyboard_layout.kcm b/tests/Input/res/raw/dummy_keyboard_layout.kcm
new file mode 100644
index 000000000000..ea6bc980b7b6
--- /dev/null
+++ b/tests/Input/res/raw/dummy_keyboard_layout.kcm
@@ -0,0 +1,311 @@
+# Copyright (C) 2023 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.
+
+#
+# English (US) keyboard layout.
+# Unlike the default (generic) keyboard layout, English (US) does not contain any
+# special ALT characters.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+ label: '`'
+ base: '`'
+ shift: '~'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+}
+
+### ROW 2
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+}
+
+key LEFT_BRACKET {
+ label: '['
+ base: '['
+ shift: '{'
+}
+
+key RIGHT_BRACKET {
+ label: ']'
+ base: ']'
+ shift: '}'
+}
+
+key BACKSLASH {
+ label: '\\'
+ base: '\\'
+ shift: '|'
+}
+
+### ROW 3
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+### ROW 4
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
diff --git a/tests/Input/res/raw/input_port_associations.xml b/tests/Input/res/raw/input_port_associations.xml
new file mode 100644
index 000000000000..b10d541f942c
--- /dev/null
+++ b/tests/Input/res/raw/input_port_associations.xml
@@ -0,0 +1,4 @@
+<ports>
+ <port display="0" input="USB1" />
+ <port display="1" input="USB2" />
+</ports> \ No newline at end of file
diff --git a/tests/Input/res/raw/input_port_associations_bad_displayport.xml b/tests/Input/res/raw/input_port_associations_bad_displayport.xml
new file mode 100644
index 000000000000..8eeb1f58ef9e
--- /dev/null
+++ b/tests/Input/res/raw/input_port_associations_bad_displayport.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port display="a" input="USB1" />
+</ports> \ No newline at end of file
diff --git a/tests/Input/res/raw/input_port_associations_bad_xml.xml b/tests/Input/res/raw/input_port_associations_bad_xml.xml
new file mode 100644
index 000000000000..cf6e12486239
--- /dev/null
+++ b/tests/Input/res/raw/input_port_associations_bad_xml.xml
@@ -0,0 +1,3 @@
+<ports>
+ <port Garbage data inside xml>
+</ports> \ No newline at end of file
diff --git a/tests/Input/res/xml/keyboard_layouts.xml b/tests/Input/res/xml/keyboard_layouts.xml
new file mode 100644
index 000000000000..5f3fcd6eaed0
--- /dev/null
+++ b/tests/Input/res/xml/keyboard_layouts.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <keyboard-layout
+ android:name="keyboard_layout_english_uk"
+ android:label="English (UK)"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="en-Latn-GB"
+ android:keyboardLayoutType="qwerty" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_english_us"
+ android:label="English (US)"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="en-Latn,en-Latn-US"
+ android:keyboardLayoutType="qwerty" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_english_us_intl"
+ android:label="English (International)"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="en-Latn-US"
+ android:keyboardLayoutType="extended" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_english_us_dvorak"
+ android:label="English (Dvorak)"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="en-Latn-US"
+ android:keyboardLayoutType="dvorak" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_german"
+ android:label="German"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="de-Latn"
+ android:keyboardLayoutType="qwertz" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_french"
+ android:label="French"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="fr-Latn-FR"
+ android:keyboardLayoutType="azerty" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_russian_qwerty"
+ android:label="Russian"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="ru-Cyrl"
+ android:keyboardLayoutType="qwerty" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_russian"
+ android:label="Russian"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="ru-Cyrl" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_english_without_script_code"
+ android:label="English(No script code)"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ android:keyboardLocale="en"
+ android:keyboardLayoutType="qwerty" />
+
+ <keyboard-layout
+ android:name="keyboard_layout_vendorId:1,productId:1"
+ android:label="vendorId:1,productId:1"
+ android:keyboardLayout="@raw/dummy_keyboard_layout"
+ androidprv:vendorId="1"
+ androidprv:productId="1" />
+
+</keyboard-layouts>
diff --git a/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
new file mode 100644
index 000000000000..90dff47ab706
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 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.hardware.BatteryState
+import android.os.Handler
+import android.os.HandlerExecutor
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.testutils.any
+import java.util.concurrent.Executor
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+import kotlin.test.fail
+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.anyInt
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for [InputManager.InputDeviceBatteryListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDeviceBatteryListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class InputDeviceBatteryListenerTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var testLooper: TestLooper
+ private var registeredListener: IInputDeviceBatteryListener? = null
+ private val monitoredDevices = mutableListOf<Int>()
+ private lateinit var executor: Executor
+ private lateinit var context: Context
+ private lateinit var inputManager: InputManager
+
+ @Mock
+ private lateinit var iInputManagerMock: IInputManager
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ @Before
+ fun setUp() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ testLooper = TestLooper()
+ executor = HandlerExecutor(Handler(testLooper.looper))
+ registeredListener = null
+ monitoredDevices.clear()
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
+ inputManager = InputManager(context)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+
+ // Handle battery listener registration.
+ doAnswer {
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ // There can only be one registered battery listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ if (monitoredDevices.contains(deviceId)) {
+ fail("Trying to start monitoring a device that was already being monitored")
+ }
+ monitoredDevices.add(deviceId)
+ registeredListener = listener
+ null
+ }.`when`(iInputManagerMock).registerBatteryListener(anyInt(), any())
+
+ // Handle battery listener being unregistered.
+ doAnswer {
+ val deviceId = it.getArgument(0) as Int
+ val listener = it.getArgument(1) as IInputDeviceBatteryListener
+ if (registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ if (!monitoredDevices.remove(deviceId)) {
+ fail("Trying to stop monitoring a device that is not being monitored")
+ }
+ if (monitoredDevices.isEmpty()) {
+ registeredListener = null
+ }
+ }.`when`(iInputManagerMock).unregisterBatteryListener(anyInt(), any())
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun notifyBatteryStateChanged(
+ deviceId: Int,
+ isPresent: Boolean = true,
+ status: Int = BatteryState.STATUS_FULL,
+ capacity: Float = 1.0f,
+ eventTime: Long = 12345L
+ ) {
+ registeredListener!!.onBatteryStateChanged(IInputDeviceBatteryState().apply {
+ this.deviceId = deviceId
+ this.updateTime = eventTime
+ this.isPresent = isPresent
+ this.status = status
+ this.capacity = capacity
+ })
+ }
+
+ @Test
+ fun testListenerIsNotifiedCorrectly() {
+ var callbackCount = 0
+
+ // Add a battery listener to monitor battery changes.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor) {
+ deviceId: Int, eventTime: Long, batteryState: BatteryState ->
+ callbackCount++
+ assertEquals(1, deviceId)
+ assertEquals(true, batteryState.isPresent)
+ assertEquals(BatteryState.STATUS_DISCHARGING, batteryState.status)
+ assertEquals(0.5f, batteryState.capacity)
+ assertEquals(8675309L, eventTime)
+ }
+
+ // Adding the listener should register the callback with InputManagerService.
+ assertNotNull(registeredListener)
+ assertTrue(monitoredDevices.contains(1))
+
+ // Notifying battery change for a different device should not trigger the listener.
+ notifyBatteryStateChanged(deviceId = 2)
+ testLooper.dispatchAll()
+ assertEquals(0, callbackCount)
+
+ // Notifying battery change for the registered device will notify the listener.
+ notifyBatteryStateChanged(1 /*deviceId*/, true /*isPresent*/,
+ BatteryState.STATUS_DISCHARGING, 0.5f /*capacity*/, 8675309L /*eventTime*/)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount)
+ }
+
+ @Test
+ fun testMultipleListeners() {
+ // Set up two callbacks.
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount1++ }
+ val callback2 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount2++ }
+
+ // Monitor battery changes for three devices. The first callback monitors devices 1 and 3,
+ // while the second callback monitors devices 2 and 3.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback1)
+ assertEquals(1, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(2 /*deviceId*/, executor, callback2)
+ assertEquals(2, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(3 /*deviceId*/, executor, callback1)
+ assertEquals(3, monitoredDevices.size)
+ inputManager.addInputDeviceBatteryListener(3 /*deviceId*/, executor, callback2)
+ assertEquals(3, monitoredDevices.size)
+
+ // Notifying battery change for each of the devices should trigger the registered callbacks.
+ notifyBatteryStateChanged(deviceId = 1)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+ assertEquals(0, callbackCount2)
+
+ notifyBatteryStateChanged(deviceId = 2)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+ assertEquals(1, callbackCount2)
+
+ notifyBatteryStateChanged(deviceId = 3)
+ testLooper.dispatchNext()
+ testLooper.dispatchNext()
+ assertEquals(2, callbackCount1)
+ assertEquals(2, callbackCount2)
+
+ // Stop monitoring devices 1 and 2.
+ inputManager.removeInputDeviceBatteryListener(1 /*deviceId*/, callback1)
+ assertEquals(2, monitoredDevices.size)
+ inputManager.removeInputDeviceBatteryListener(2 /*deviceId*/, callback2)
+ assertEquals(1, monitoredDevices.size)
+
+ // Ensure device 3 continues to be monitored.
+ notifyBatteryStateChanged(deviceId = 3)
+ testLooper.dispatchNext()
+ testLooper.dispatchNext()
+ assertEquals(3, callbackCount1)
+ assertEquals(3, callbackCount2)
+
+ // Stop monitoring all devices.
+ inputManager.removeInputDeviceBatteryListener(3 /*deviceId*/, callback1)
+ assertEquals(1, monitoredDevices.size)
+ inputManager.removeInputDeviceBatteryListener(3 /*deviceId*/, callback2)
+ assertEquals(0, monitoredDevices.size)
+ }
+
+ @Test
+ fun testAdditionalListenersNotifiedImmediately() {
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount1++ }
+ val callback2 = InputManager.InputDeviceBatteryListener { _, _, _ -> callbackCount2++ }
+
+ // Add a battery listener and send the latest battery state.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback1)
+ assertEquals(1, monitoredDevices.size)
+ notifyBatteryStateChanged(deviceId = 1)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount1)
+
+ // Add a second listener for the same device that already has the latest battery state.
+ inputManager.addInputDeviceBatteryListener(1 /*deviceId*/, executor, callback2)
+ assertEquals(1, monitoredDevices.size)
+
+ // Ensure that this listener is notified immediately.
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount2)
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
new file mode 100644
index 000000000000..080186e4a2c1
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2021 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 static android.hardware.lights.LightsRequest.Builder;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.view.InputDevice;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link InputDeviceLightsManager}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDeviceLightsManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class InputDeviceLightsManagerTest {
+ private static final String TAG = "InputDeviceLightsManagerTest";
+
+ private static final int DEVICE_ID = 1000;
+ private static final int PLAYER_ID = 3;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private InputManager mInputManager;
+
+ @Mock private IInputManager mIInputManagerMock;
+ private InputManagerGlobal.TestSession mInputManagerGlobalSession;
+
+ @Before
+ public void setUp() throws Exception {
+ final Context context = spy(
+ new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+
+ when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createInputDevice(DEVICE_ID));
+
+ mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
+ mInputManager = new InputManager(context);
+ when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
+
+ ArrayMap<Integer, LightState> lightStatesById = new ArrayMap<>();
+ doAnswer(invocation -> {
+ final int[] lightIds = (int[]) invocation.getArguments()[1];
+ final LightState[] lightStates =
+ (LightState[]) invocation.getArguments()[2];
+ for (int i = 0; i < lightIds.length; i++) {
+ lightStatesById.put(lightIds[i], lightStates[i]);
+ }
+ return null;
+ }).when(mIInputManagerMock).setLightStates(eq(DEVICE_ID),
+ any(int[].class), any(LightState[].class), any(IBinder.class));
+
+ doAnswer(invocation -> {
+ int lightId = (int) invocation.getArguments()[1];
+ if (lightStatesById.containsKey(lightId)) {
+ return lightStatesById.get(lightId);
+ }
+ return new LightState(0);
+ }).when(mIInputManagerMock).getLightState(eq(DEVICE_ID), anyInt());
+ }
+
+ @After
+ public void tearDown() {
+ if (mInputManagerGlobalSession != null) {
+ mInputManagerGlobalSession.close();
+ }
+ }
+
+ private InputDevice createInputDevice(int id) {
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("Test Device " + id)
+ .build();
+ }
+
+ private void mockLights(Light[] lights) throws Exception {
+ // Mock the Lights returned form InputManagerService
+ when(mIInputManagerMock.getLights(eq(DEVICE_ID))).thenReturn(
+ new ArrayList(Arrays.asList(lights)));
+ }
+
+ @Test
+ public void testGetInputDeviceLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_BRIGHTNESS),
+ new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
+ new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ 0 /* capabilities */)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lights = lightsManager.getLights();
+ verify(mIInputManagerMock).getLights(eq(DEVICE_ID));
+ assertEquals(lights, Arrays.asList(mockedLights));
+ }
+
+ @Test
+ public void testControlMultipleLights() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
+ new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
+ new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
+ new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1), new LightState(0xf2),
+ new LightState(0xf3)};
+ // Open a session to request turn 3/4 lights on:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .addLight(lightsManager.getLights().get(1), states[1])
+ .addLight(lightsManager.getLights().get(2), states[2])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1, 2, 3}),
+ eq(states), eq(token));
+
+ // Then all 3 should turn on.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(1)).getColor())
+ .isEqualTo(0xf2);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(2)).getColor())
+ .isEqualTo(0xf3);
+
+ // And the 4th should remain off.
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(3)).getColor())
+ .isEqualTo(0x00);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+
+ @Test
+ public void testControlPlayerIdLight() throws Exception {
+ InputDevice device = mInputManager.getInputDevice(DEVICE_ID);
+ assertNotNull(device);
+
+ Light[] mockedLights = {
+ new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
+ 0 /* capabilities */),
+ new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
+ new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_BRIGHTNESS)
+ };
+ mockLights(mockedLights);
+
+ LightsManager lightsManager = device.getLightsManager();
+ List<Light> lightList = lightsManager.getLights();
+ LightState[] states = new LightState[]{new LightState(0xf1, PLAYER_ID)};
+ // Open a session to request set Player ID light:
+ LightsManager.LightsSession session = lightsManager.openSession();
+ session.requestLights(new Builder()
+ .addLight(lightsManager.getLights().get(0), states[0])
+ .build());
+ IBinder token = session.getToken();
+
+ verify(mIInputManagerMock).openLightSession(eq(DEVICE_ID),
+ any(String.class), eq(token));
+ verify(mIInputManagerMock).setLightStates(eq(DEVICE_ID), eq(new int[]{1}),
+ eq(states), eq(token));
+
+ // Verify the light state
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getColor())
+ .isEqualTo(0xf1);
+ assertThat(lightsManager.getLightState(lightsManager.getLights().get(0)).getPlayerId())
+ .isEqualTo(PLAYER_ID);
+
+ // close session
+ session.close();
+ verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
+ }
+
+ @Test
+ public void testLightCapabilities() throws Exception {
+ Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+ assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
+ assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
+ | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+ assertTrue(light.hasBrightnessControl());
+ assertTrue(light.hasRgbControl());
+ }
+
+ @Test
+ public void testLightsRequest() throws Exception {
+ Light light1 = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
+ 0 /* capabilities */);
+ Light light2 = new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
+ 0 /* capabilities */);
+ LightState state1 = new LightState(0xf1);
+ LightState state2 = new LightState(0xf2, PLAYER_ID);
+ LightsRequest request = new Builder().addLight(light1, state1)
+ .addLight(light2, state2).build();
+
+ // Covers the LightsRequest.getLights
+ assertThat(request.getLights().size()).isEqualTo(2);
+ assertThat(request.getLights().get(0)).isEqualTo(1);
+ assertThat(request.getLights().get(1)).isEqualTo(2);
+
+ // Covers the LightsRequest.getLightStates
+ assertThat(request.getLightStates().size()).isEqualTo(2);
+ assertThat(request.getLightStates().get(0)).isEqualTo(state1);
+ assertThat(request.getLightStates().get(1)).isEqualTo(state2);
+
+ // Covers the LightsRequest.getLightsAndStates
+ assertThat(request.getLightsAndStates().size()).isEqualTo(2);
+ assertThat(request.getLightsAndStates().containsKey(light1)).isTrue();
+ assertThat(request.getLightsAndStates().get(light1)).isEqualTo(state1);
+ assertThat(request.getLightsAndStates().get(light2)).isEqualTo(state2);
+ }
+
+}
diff --git a/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
new file mode 100644
index 000000000000..0e3c200699d2
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2020 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 static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.platform.test.annotations.Presubmit;
+import android.view.InputDevice;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.annotations.GuardedBy;
+
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for {@link InputDeviceSensorManager}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDeviceSensorManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class InputDeviceSensorManagerTest {
+ private static final String TAG = "InputDeviceSensorManagerTest";
+
+ private static final int DEVICE_ID = 1000;
+
+ @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private InputManager mInputManager;
+ private IInputSensorEventListener mIInputSensorEventListener;
+ private final Object mLock = new Object();
+
+ @Mock private IInputManager mIInputManagerMock;
+ private InputManagerGlobal.TestSession mInputManagerGlobalSession;
+
+ @Before
+ public void setUp() throws Exception {
+ final Context context = spy(
+ new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()));
+ mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock);
+ mInputManager = new InputManager(context);
+ when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager);
+
+ when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID});
+
+ when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn(
+ createInputDeviceWithSensor(DEVICE_ID));
+
+ when(mIInputManagerMock.getSensorList(eq(DEVICE_ID))).thenReturn(new InputSensorInfo[] {
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_ACCELEROMETER),
+ createInputSensorInfo(DEVICE_ID, Sensor.TYPE_GYROSCOPE)});
+
+ when(mIInputManagerMock.enableSensor(eq(DEVICE_ID), anyInt(), anyInt(), anyInt()))
+ .thenReturn(true);
+
+ when(mIInputManagerMock.registerSensorListener(any())).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() {
+ if (mInputManagerGlobalSession != null) {
+ mInputManagerGlobalSession.close();
+ }
+ }
+
+ private class InputTestSensorEventListener implements SensorEventListener {
+ @GuardedBy("mLock")
+ private final BlockingQueue<SensorEvent> mEvents = new LinkedBlockingQueue<>();
+ InputTestSensorEventListener() {
+ super();
+ }
+
+ public SensorEvent waitForSensorEvent() {
+ try {
+ return mEvents.poll(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ fail("unexpectedly interrupted while waiting for SensorEvent");
+ return null;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (mLock) {
+ try {
+ mEvents.put(event);
+ } catch (InterruptedException ex) {
+ fail("interrupted while adding a SensorEvent to the queue");
+ }
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
+ private InputDevice createInputDeviceWithSensor(int id) {
+ return new InputDevice.Builder()
+ .setId(id)
+ .setName("Test Device " + id)
+ .setHasSensor(true)
+ .build();
+ }
+
+ private InputSensorInfo createInputSensorInfo(int id, int type) {
+ InputSensorInfo info = new InputSensorInfo("name", "vendor", 0 /* version */,
+ 0 /* handle */, type, 100.0f /*maxRange */, 0.02f /* resolution */,
+ 0.8f /* power */, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
+ 0 /* fifoMaxEventCount */, "" /* stringType */, "" /* requiredPermission */,
+ 0 /* maxDelay */, 0 /* flags */, id);
+ return info;
+ }
+
+ private InputDevice getSensorDevice(int[] deviceIds) {
+ for (int deviceId : deviceIds) {
+ InputDevice device = mInputManager.getInputDevice(deviceId);
+ if (device.hasSensor()) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ @Test
+ public void getInputDeviceSensors_withExpectedType() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+ assertNotNull(device);
+
+ SensorManager sensorManager = device.getSensorManager();
+ List<Sensor> accelList = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(1, accelList.size());
+ assertEquals(DEVICE_ID, accelList.get(0).getId());
+ assertEquals(Sensor.TYPE_ACCELEROMETER, accelList.get(0).getType());
+
+ List<Sensor> gyroList = sensorManager.getSensorList(Sensor.TYPE_GYROSCOPE);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(1, gyroList.size());
+ assertEquals(DEVICE_ID, gyroList.get(0).getId());
+ assertEquals(Sensor.TYPE_GYROSCOPE, gyroList.get(0).getType());
+
+ }
+
+ @Test
+ public void getInputDeviceSensors_withUnexpectedType() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+
+ assertNotNull(device);
+ SensorManager sensorManager = device.getSensorManager();
+
+ List<Sensor> gameRotationList = sensorManager.getSensorList(
+ Sensor.TYPE_GAME_ROTATION_VECTOR);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(0, gameRotationList.size());
+
+ List<Sensor> gravityList = sensorManager.getSensorList(Sensor.TYPE_GRAVITY);
+ verify(mIInputManagerMock).getSensorList(eq(DEVICE_ID));
+ assertEquals(0, gravityList.size());
+ }
+
+ @Test
+ public void testInputDeviceSensorListener() throws Exception {
+ InputDevice device = getSensorDevice(mInputManager.getInputDeviceIds());
+ assertNotNull(device);
+
+ SensorManager sensorManager = device.getSensorManager();
+ Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ assertEquals(Sensor.TYPE_ACCELEROMETER, sensor.getType());
+
+ doAnswer(invocation -> {
+ mIInputSensorEventListener = invocation.getArgument(0);
+ assertNotNull(mIInputSensorEventListener);
+ return true;
+ }).when(mIInputManagerMock).registerSensorListener(any());
+
+ InputTestSensorEventListener listener = new InputTestSensorEventListener();
+ assertTrue(sensorManager.registerListener(listener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL));
+ verify(mIInputManagerMock).registerSensorListener(any());
+ verify(mIInputManagerMock).enableSensor(eq(DEVICE_ID), eq(sensor.getType()),
+ anyInt(), anyInt());
+
+ float[] values = new float[] {0.12f, 9.8f, 0.2f};
+ mIInputSensorEventListener.onInputSensorChanged(DEVICE_ID, Sensor.TYPE_ACCELEROMETER,
+ SensorManager.SENSOR_STATUS_ACCURACY_HIGH, /* timestamp */ 0x1234abcd, values);
+
+ SensorEvent event = listener.waitForSensorEvent();
+ assertNotNull(event);
+ assertEquals(0x1234abcd, event.timestamp);
+ assertEquals(values.length, event.values.length);
+ for (int i = 0; i < values.length; i++) {
+ assertEquals(values[i], event.values[i], 0.001f);
+ }
+
+ sensorManager.unregisterListener(listener);
+ verify(mIInputManagerMock).disableSensor(eq(DEVICE_ID), eq(sensor.getType()));
+ }
+
+}
diff --git a/tests/Input/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt
new file mode 100644
index 000000000000..152dde94f006
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 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.content.res.Resources
+import android.platform.test.annotations.Presubmit
+import android.view.Display
+import android.view.DisplayInfo
+import android.view.InputDevice
+import androidx.test.core.app.ApplicationProvider
+import org.junit.After
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertEquals
+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.eq
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for [InputManager].
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputManagerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class InputManagerTest {
+
+ companion object {
+ const val DEVICE_ID = 42
+ const val SECOND_DEVICE_ID = 96
+ const val THIRD_DEVICE_ID = 99
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var devicesChangedListener: IInputDevicesChangedListener
+ private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
+ private lateinit var context: Context
+ private lateinit var inputManager: InputManager
+
+ @Mock
+ private lateinit var iInputManager: IInputManager
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ @Before
+ fun setUp() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ inputManager = InputManager(context)
+ `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
+ `when`(iInputManager.inputDeviceIds).then {
+ deviceGenerationMap.keys.toIntArray()
+ }
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun notifyDeviceChanged(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion?,
+ ) {
+ val generation = deviceGenerationMap[deviceId]?.plus(1)
+ ?: throw IllegalArgumentException("Device $deviceId was never added!")
+ deviceGenerationMap[deviceId] = generation
+
+ `when`(iInputManager.getInputDevice(deviceId))
+ .thenReturn(createInputDevice(deviceId, associatedDisplayId, usiVersion, generation))
+ val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
+ if (::devicesChangedListener.isInitialized) {
+ devicesChangedListener.onInputDevicesChanged(list.toIntArray())
+ }
+ }
+
+ private fun addInputDevice(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion?,
+ ) {
+ deviceGenerationMap[deviceId] = 0
+ notifyDeviceChanged(deviceId, associatedDisplayId, usiVersion)
+ }
+
+ @Test
+ fun testUsiVersionDisplayAssociation() {
+ addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
+ addInputDevice(SECOND_DEVICE_ID, Display.INVALID_DISPLAY, HostUsiVersion(9, 8))
+ addInputDevice(THIRD_DEVICE_ID, 42, HostUsiVersion(3, 1))
+
+ val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
+ assertNotNull(usiVersion)
+ assertEquals(3, usiVersion!!.majorVersion)
+ assertEquals(1, usiVersion.minorVersion)
+ }
+
+ @Test
+ fun testUsiVersionFallBackToDisplayConfig() {
+ addInputDevice(DEVICE_ID, Display.DEFAULT_DISPLAY, null)
+
+ `when`(iInputManager.getHostUsiVersionFromDisplayConfig(eq(42)))
+ .thenReturn(HostUsiVersion(9, 8))
+ val usiVersion = inputManager.getHostUsiVersion(createDisplay(42))
+ assertEquals(HostUsiVersion(9, 8), usiVersion)
+ }
+}
+
+private fun createInputDevice(
+ deviceId: Int,
+ associatedDisplayId: Int,
+ usiVersion: HostUsiVersion? = null,
+ generation: Int = -1,
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setAssociatedDisplayId(associatedDisplayId)
+ .setUsiVersion(usiVersion)
+ .setGeneration(generation)
+ .build()
+
+private fun createDisplay(displayId: Int): Display {
+ val res: Resources? = null
+ return Display(null /* global */, displayId, DisplayInfo(), res)
+}
diff --git a/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
new file mode 100644
index 000000000000..23135b2550b0
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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 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.MockitoJUnit
+import org.mockito.junit.MockitoJUnitRunner
+import java.util.concurrent.Executor
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+import kotlin.test.fail
+
+/**
+ * Tests for [InputManager.KeyboardBacklightListener].
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardBacklightListenerTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyboardBacklightListenerTest {
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var testLooper: TestLooper
+ private var registeredListener: IKeyboardBacklightListener? = null
+ private lateinit var executor: Executor
+ 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)
+ testLooper = TestLooper()
+ executor = HandlerExecutor(Handler(testLooper.looper))
+ registeredListener = null
+ inputManager = InputManager(context)
+ `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+
+ // Handle keyboard backlight listener registration.
+ doAnswer {
+ val listener = it.getArgument(0) as IKeyboardBacklightListener
+ if (registeredListener != null &&
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ // There can only be one registered keyboard backlight listener per process.
+ fail("Trying to register a new listener when one already exists")
+ }
+ registeredListener = listener
+ null
+ }.`when`(iInputManagerMock).registerKeyboardBacklightListener(any())
+
+ // Handle keyboard backlight listener being unregistered.
+ doAnswer {
+ val listener = it.getArgument(0) as IKeyboardBacklightListener
+ if (registeredListener == null ||
+ registeredListener!!.asBinder() != listener.asBinder()) {
+ fail("Trying to unregister a listener that is not registered")
+ }
+ registeredListener = null
+ null
+ }.`when`(iInputManagerMock).unregisterKeyboardBacklightListener(any())
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun notifyKeyboardBacklightChanged(
+ deviceId: Int,
+ brightnessLevel: Int,
+ maxBrightnessLevel: Int = 10,
+ isTriggeredByKeyPress: Boolean = true
+ ) {
+ registeredListener!!.onBrightnessChanged(deviceId, IKeyboardBacklightState().apply {
+ this.brightnessLevel = brightnessLevel
+ this.maxBrightnessLevel = maxBrightnessLevel
+ }, isTriggeredByKeyPress)
+ }
+
+ @Test
+ fun testListenerIsNotifiedCorrectly() {
+ var callbackCount = 0
+
+ // Add a keyboard backlight listener
+ inputManager.registerKeyboardBacklightListener(executor) {
+ deviceId: Int,
+ keyboardBacklightState: KeyboardBacklightState,
+ isTriggeredByKeyPress: Boolean ->
+ callbackCount++
+ assertEquals(1, deviceId)
+ assertEquals(2, keyboardBacklightState.brightnessLevel)
+ assertEquals(10, keyboardBacklightState.maxBrightnessLevel)
+ assertEquals(true, isTriggeredByKeyPress)
+ }
+
+ // Adding the listener should register the callback with InputManagerService.
+ assertNotNull(registeredListener)
+
+ // Notifying keyboard backlight change will notify the listener.
+ notifyKeyboardBacklightChanged(1 /*deviceId*/, 2 /* brightnessLevel */)
+ testLooper.dispatchNext()
+ assertEquals(1, callbackCount)
+ }
+
+ @Test
+ fun testMultipleListeners() {
+ // Set up two callbacks.
+ var callbackCount1 = 0
+ var callbackCount2 = 0
+ val callback1 = InputManager.KeyboardBacklightListener { _, _, _ -> callbackCount1++ }
+ val callback2 = InputManager.KeyboardBacklightListener { _, _, _ -> callbackCount2++ }
+
+ // Add both keyboard backlight listeners
+ inputManager.registerKeyboardBacklightListener(executor, callback1)
+ inputManager.registerKeyboardBacklightListener(executor, callback2)
+
+ // Adding the listeners should register the callback with InputManagerService.
+ assertNotNull(registeredListener)
+
+ // Notifying keyboard backlight change trigger the both callbacks.
+ notifyKeyboardBacklightChanged(1 /*deviceId*/, 1 /* brightnessLevel */)
+ testLooper.dispatchAll()
+ assertEquals(1, callbackCount1)
+ assertEquals(1, callbackCount2)
+
+ inputManager.unregisterKeyboardBacklightListener(callback2)
+ // Notifying keyboard backlight change should still trigger callback1.
+ notifyKeyboardBacklightChanged(1 /*deviceId*/, 2 /* brightnessLevel */)
+ testLooper.dispatchAll()
+ assertEquals(2, callbackCount1)
+
+ // Unregister all listeners, should remove registered listener from InputManagerService
+ inputManager.unregisterKeyboardBacklightListener(callback1)
+ assertNull(registeredListener)
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
new file mode 100644
index 000000000000..3a2a3be0690d
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2023 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.ContextWrapper
+import android.graphics.drawable.Drawable
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.hardware.input.Flags
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnitRunner
+
+/**
+ * Tests for Keyboard layout preview
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardLayoutPreviewTests
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner::class)
+class KeyboardLayoutPreviewTests {
+
+ companion object {
+ const val WIDTH = 100
+ const val HEIGHT = 100
+ }
+
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
+ private fun createDrawable(): Drawable? {
+ val context = ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())
+ val inputManager = context.getSystemService(InputManager::class.java)!!
+ return inputManager.getKeyboardLayoutPreview(null, WIDTH, HEIGHT)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ val drawable = createDrawable()!!
+ assertEquals(WIDTH, drawable.intrinsicWidth)
+ assertEquals(HEIGHT, drawable.intrinsicHeight)
+ }
+
+ @Test
+ fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
+ setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ assertNull(createDrawable())
+ }
+} \ No newline at end of file
diff --git a/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java
new file mode 100644
index 000000000000..37cc9b70dd1b
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import android.view.KeyEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualKeyEventTest {
+
+ @Test
+ public void keyEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualKeyEvent.Builder().build());
+ }
+
+ @Test
+ public void keyEvent_noKeyCode() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualKeyEvent.Builder().setAction(VirtualKeyEvent.ACTION_DOWN).build());
+ }
+
+ @Test
+ public void keyEvent_noAction() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualKeyEvent.Builder().setKeyCode(KeyEvent.KEYCODE_A).build());
+ }
+
+ @Test
+ public void keyEvent_created() {
+ final VirtualKeyEvent event = new VirtualKeyEvent.Builder()
+ .setAction(VirtualKeyEvent.ACTION_DOWN)
+ .setKeyCode(KeyEvent.KEYCODE_A).build();
+ assertWithMessage("Incorrect key code").that(event.getKeyCode()).isEqualTo(
+ KeyEvent.KEYCODE_A);
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualKeyEvent.ACTION_DOWN);
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java
new file mode 100644
index 000000000000..789e0bb2ff56
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseButtonEventTest {
+
+ @Test
+ public void buttonEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new VirtualMouseButtonEvent.Builder().build());
+ }
+
+ @Test
+ public void buttonEvent_noButtonCode() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_RELEASE).build());
+ }
+
+ @Test
+ public void buttonEvent_noAction() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseButtonEvent.Builder()
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build());
+ }
+
+ @Test
+ public void buttonEvent_created() {
+ final VirtualMouseButtonEvent event = new VirtualMouseButtonEvent.Builder()
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK).build();
+ assertWithMessage("Incorrect button code").that(event.getButtonCode()).isEqualTo(
+ VirtualMouseButtonEvent.BUTTON_BACK);
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualMouseButtonEvent.ACTION_BUTTON_PRESS);
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java
new file mode 100644
index 000000000000..c0508162869b
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseRelativeEventTest {
+
+ @Test
+ public void relativeEvent_created() {
+ final VirtualMouseRelativeEvent event = new VirtualMouseRelativeEvent.Builder()
+ .setRelativeX(-5f)
+ .setRelativeY(8f).build();
+ assertWithMessage("Incorrect x value").that(event.getRelativeX()).isEqualTo(-5f);
+ assertWithMessage("Incorrect y value").that(event.getRelativeY()).isEqualTo(8f);
+ }
+}
diff --git a/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java
new file mode 100644
index 000000000000..2259c740da7e
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualMouseScrollEventTest {
+
+ @Test
+ public void scrollEvent_xOutOfRange() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(1.5f)
+ .setYAxisMovement(1.0f));
+ }
+
+ @Test
+ public void scrollEvent_yOutOfRange() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(0.5f)
+ .setYAxisMovement(1.1f));
+ }
+
+ @Test
+ public void scrollEvent_created() {
+ final VirtualMouseScrollEvent event = new VirtualMouseScrollEvent.Builder()
+ .setXAxisMovement(-1f)
+ .setYAxisMovement(1f).build();
+ assertWithMessage("Incorrect x value").that(event.getXAxisMovement()).isEqualTo(-1f);
+ assertWithMessage("Incorrect y value").that(event.getYAxisMovement()).isEqualTo(1f);
+ }
+}
+
diff --git a/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java
new file mode 100644
index 000000000000..100aba5ab362
--- /dev/null
+++ b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class VirtualTouchEventTest {
+
+ @Test
+ public void touchEvent_emptyBuilder() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder().build());
+ }
+
+ @Test
+ public void touchEvent_noAction() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noPointerId() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noToolType() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_noX() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+
+ @Test
+ public void touchEvent_noY() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_created() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_DOWN);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_FINGER);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ }
+
+ @Test
+ public void touchEvent_created_withPressureAndAxis() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_DOWN)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .setPressure(0.5f)
+ .setMajorAxisSize(10f)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_DOWN);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_FINGER);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+ assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+ 10f);
+ }
+
+ @Test
+ public void touchEvent_cancelUsedImproperly() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_CANCEL)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ /**
+ * The combination of TOOL_TYPE_PALM with anything else than ACTION_CANCEL should throw an
+ * exception. This is due to an underlying implementation detail. See documentation of {@link
+ * VirtualTouchEvent}
+ * for details.
+ */
+ @Test
+ public void touchEvent_palmUsedImproperly() {
+ assertThrows(IllegalArgumentException.class, () -> new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_MOVE)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .build());
+ }
+
+ @Test
+ public void touchEvent_palmAndCancelUsedProperly() {
+ final VirtualTouchEvent event = new VirtualTouchEvent.Builder()
+ .setAction(VirtualTouchEvent.ACTION_CANCEL)
+ .setToolType(VirtualTouchEvent.TOOL_TYPE_PALM)
+ .setX(0f)
+ .setY(1f)
+ .setPointerId(1)
+ .setPressure(0.5f)
+ .setMajorAxisSize(10f)
+ .build();
+ assertWithMessage("Incorrect action").that(event.getAction()).isEqualTo(
+ VirtualTouchEvent.ACTION_CANCEL);
+ assertWithMessage("Incorrect tool type").that(event.getToolType()).isEqualTo(
+ VirtualTouchEvent.TOOL_TYPE_PALM);
+ assertWithMessage("Incorrect x").that(event.getX()).isEqualTo(0f);
+ assertWithMessage("Incorrect y").that(event.getY()).isEqualTo(1f);
+ assertWithMessage("Incorrect pointer id").that(event.getPointerId()).isEqualTo(1);
+ assertWithMessage("Incorrect pressure").that(event.getPressure()).isEqualTo(0.5f);
+ assertWithMessage("Incorrect major axis size").that(event.getMajorAxisSize()).isEqualTo(
+ 10f);
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt
new file mode 100644
index 000000000000..ad481dff810c
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2023 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.content.res.Resources
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.hardware.display.DisplayManagerInternal
+import android.hardware.input.InputSensorInfo
+import android.os.Handler
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.util.TypedValue
+import android.view.Display
+import android.view.DisplayInfo
+import androidx.test.core.app.ApplicationProvider
+import com.android.internal.R
+import com.android.server.LocalServices
+import com.android.server.input.AmbientKeyboardBacklightController.HYSTERESIS_THRESHOLD
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+
+/**
+ * Tests for {@link AmbientKeyboardBacklightController}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:AmbientKeyboardBacklightControllerTests
+ */
+@Presubmit
+class AmbientKeyboardBacklightControllerTests {
+
+ companion object {
+ const val DEFAULT_DISPLAY_UNIQUE_ID = "uniqueId_1"
+ const val SENSOR_NAME = "test_sensor_name"
+ const val SENSOR_TYPE = "test_sensor_type"
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ private lateinit var context: Context
+ private lateinit var testLooper: TestLooper
+ private lateinit var ambientController: AmbientKeyboardBacklightController
+
+ @Mock
+ private lateinit var resources: Resources
+
+ @Mock
+ private lateinit var lightSensorInfo: InputSensorInfo
+
+ @Mock
+ private lateinit var sensorManager: SensorManager
+
+ @Mock
+ private lateinit var displayManagerInternal: DisplayManagerInternal
+ private lateinit var lightSensor: Sensor
+
+ private var currentDisplayInfo = DisplayInfo()
+ private var lastBrightnessCallback: Int = 0
+ private var listenerRegistered: Boolean = false
+ private var listenerRegistrationCount: Int = 0
+
+ @Before
+ fun setup() {
+ context = spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ `when`(context.resources).thenReturn(resources)
+ setupBrightnessSteps()
+ setupSensor()
+ testLooper = TestLooper()
+ ambientController = AmbientKeyboardBacklightController(context, testLooper.looper)
+ ambientController.systemRunning()
+ testLooper.dispatchAll()
+ }
+
+ private fun setupBrightnessSteps() {
+ val brightnessValues = intArrayOf(100, 200, 0)
+ val decreaseThresholds = intArrayOf(-1, 900, 1900)
+ val increaseThresholds = intArrayOf(1000, 2000, -1)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues))
+ .thenReturn(brightnessValues)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold))
+ .thenReturn(decreaseThresholds)
+ `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold))
+ .thenReturn(increaseThresholds)
+ `when`(
+ resources.getValue(
+ eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
+ any(TypedValue::class.java),
+ anyBoolean()
+ )
+ ).then {
+ val args = it.arguments
+ val outValue = args[1] as TypedValue
+ outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
+ Unit
+ }
+ }
+
+ private fun setupSensor() {
+ LocalServices.removeServiceForTest(DisplayManagerInternal::class.java)
+ LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal)
+ currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID
+ `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(
+ currentDisplayInfo
+ )
+ val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE)
+ `when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY))
+ .thenReturn(sensorData)
+
+ `when`(lightSensorInfo.name).thenReturn(SENSOR_NAME)
+ `when`(lightSensorInfo.stringType).thenReturn(SENSOR_TYPE)
+ lightSensor = Sensor(lightSensorInfo)
+ `when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager)
+ `when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor))
+ `when`(
+ sensorManager.registerListener(
+ any(),
+ eq(lightSensor),
+ anyInt(),
+ any(Handler::class.java)
+ )
+ ).then {
+ listenerRegistered = true
+ listenerRegistrationCount++
+ true
+ }
+ `when`(
+ sensorManager.unregisterListener(
+ any(SensorEventListener::class.java),
+ eq(lightSensor)
+ )
+ ).then {
+ listenerRegistered = false
+ Unit
+ }
+ }
+
+ private fun setupSensorWithInitialLux(luxValue: Float) {
+ ambientController.registerAmbientBacklightListener { brightnessValue: Int ->
+ lastBrightnessCallback = brightnessValue
+ }
+ sendAmbientLuxValue(luxValue)
+ testLooper.dispatchAll()
+ }
+
+ @Test
+ fun testInitialAmbientLux_sendsCallbackImmediately() {
+ setupSensorWithInitialLux(500F)
+
+ assertEquals(
+ "Should receive immediate callback for first lux change",
+ 100,
+ lastBrightnessCallback
+ )
+ }
+
+ @Test
+ fun testBrightnessIncrease_afterInitialLuxChanges() {
+ setupSensorWithInitialLux(500F)
+
+ // Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
+ repeat(HYSTERESIS_THRESHOLD) {
+ sendAmbientLuxValue(1500F)
+ }
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should receive brightness change callback for increasing lux change",
+ 200,
+ lastBrightnessCallback
+ )
+ }
+
+ @Test
+ fun testBrightnessDecrease_afterInitialLuxChanges() {
+ setupSensorWithInitialLux(1500F)
+
+ // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
+ repeat(HYSTERESIS_THRESHOLD) {
+ sendAmbientLuxValue(500F)
+ }
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should receive brightness change callback for decreasing lux change",
+ 100,
+ lastBrightnessCallback
+ )
+ }
+
+ @Test
+ fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() {
+ val ambientListener =
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ ambientController.registerAmbientBacklightListener(ambientListener)
+
+ assertThrows(IllegalStateException::class.java) {
+ ambientController.registerAmbientBacklightListener(
+ ambientListener
+ )
+ }
+ }
+
+ @Test
+ fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() {
+ val ambientListener =
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ assertThrows(IllegalStateException::class.java) {
+ ambientController.unregisterAmbientBacklightListener(
+ ambientListener
+ )
+ }
+ }
+
+ @Test
+ fun testSensorListenerRegistered_onRegisterUnregisterAmbientListener() {
+ assertEquals(
+ "Should not have a sensor listener registered at init",
+ 0,
+ listenerRegistrationCount
+ )
+ assertFalse("Should not have a sensor listener registered at init", listenerRegistered)
+
+ val ambientListener1 =
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ ambientController.registerAmbientBacklightListener(ambientListener1)
+ assertEquals(
+ "Should register a new sensor listener", 1, listenerRegistrationCount
+ )
+ assertTrue("Should have sensor listener registered", listenerRegistered)
+
+ val ambientListener2 =
+ AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
+ ambientController.registerAmbientBacklightListener(ambientListener2)
+ assertEquals(
+ "Should not register a new sensor listener when adding a second ambient listener",
+ 1,
+ listenerRegistrationCount
+ )
+ assertTrue("Should have sensor listener registered", listenerRegistered)
+
+ ambientController.unregisterAmbientBacklightListener(ambientListener1)
+ assertTrue("Should have sensor listener registered", listenerRegistered)
+
+ ambientController.unregisterAmbientBacklightListener(ambientListener2)
+ assertFalse(
+ "Should not have sensor listener registered if there are no ambient listeners",
+ listenerRegistered
+ )
+ }
+
+ @Test
+ fun testDisplayChange_shouldNotReRegisterListener_ifUniqueIdSame() {
+ setupSensorWithInitialLux(0F)
+
+ val count = listenerRegistrationCount
+ ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY)
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should not re-register listener on display change if unique is same",
+ count,
+ listenerRegistrationCount
+ )
+ }
+
+ @Test
+ fun testDisplayChange_shouldReRegisterListener_ifUniqueIdChanges() {
+ setupSensorWithInitialLux(0F)
+
+ val count = listenerRegistrationCount
+ currentDisplayInfo.uniqueId = "xyz"
+ ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY)
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should re-register listener on display change if unique id changed",
+ count + 1,
+ listenerRegistrationCount
+ )
+ }
+
+ @Test
+ fun testBrightnessDoesntChange_betweenIncreaseAndDecreaseThresholds() {
+ setupSensorWithInitialLux(1001F)
+
+ // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
+ // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
+ lastBrightnessCallback = -1
+ repeat(HYSTERESIS_THRESHOLD) {
+ sendAmbientLuxValue(999F)
+ }
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should not receive any callback for brightness change",
+ -1,
+ lastBrightnessCallback
+ )
+ }
+
+ @Test
+ fun testBrightnessDoesntChange_onChangeOccurringLessThanHysteresisThreshold() {
+ setupSensorWithInitialLux(1001F)
+
+ // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
+ // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
+ lastBrightnessCallback = -1
+ repeat(HYSTERESIS_THRESHOLD - 1) {
+ sendAmbientLuxValue(2001F)
+ }
+ testLooper.dispatchAll()
+
+ assertEquals(
+ "Should not receive any callback for brightness change",
+ -1,
+ lastBrightnessCallback
+ )
+ }
+
+ private fun sendAmbientLuxValue(luxValue: Float) {
+ ambientController.onSensorChanged(SensorEvent(lightSensor, 0, 0, floatArrayOf(luxValue)))
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
new file mode 100644
index 000000000000..f2724e605553
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2022 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.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothManager
+import android.hardware.BatteryState.STATUS_CHARGING
+import android.hardware.BatteryState.STATUS_DISCHARGING
+import android.hardware.BatteryState.STATUS_FULL
+import android.hardware.BatteryState.STATUS_UNKNOWN
+import android.hardware.input.HostUsiVersion
+import android.hardware.input.IInputDeviceBatteryListener
+import android.hardware.input.IInputDeviceBatteryState
+import android.hardware.input.IInputDevicesChangedListener
+import android.hardware.input.IInputManager
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
+import android.os.Binder
+import android.os.IBinder
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.testing.TestableContext
+import android.view.InputDevice
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.input.BatteryController.BluetoothBatteryManager
+import com.android.server.input.BatteryController.BluetoothBatteryManager.BluetoothBatteryListener
+import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
+import com.android.server.input.BatteryController.UEventBatteryListener
+import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
+import org.hamcrest.Description
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers
+import org.hamcrest.TypeSafeMatcher
+import org.hamcrest.core.IsEqual.equalTo
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.notNull
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.hamcrest.MockitoHamcrest
+import org.mockito.junit.MockitoJUnit
+import org.mockito.verification.VerificationMode
+
+private fun createInputDevice(
+ deviceId: Int,
+ hasBattery: Boolean = true,
+ supportsUsi: Boolean = false,
+ generation: Int = -1,
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setExternal(true)
+ .setHasBattery(hasBattery)
+ .setUsiVersion(if (supportsUsi) HostUsiVersion(1, 0) else null)
+ .setGeneration(generation)
+ .build()
+
+// Returns a matcher that helps match member variables of a class.
+private fun <T, U> memberMatcher(
+ member: String,
+ memberProvider: (T) -> U,
+ match: Matcher<U>
+): TypeSafeMatcher<T> =
+ object : TypeSafeMatcher<T>() {
+
+ override fun matchesSafely(item: T?): Boolean {
+ return match.matches(memberProvider(item!!))
+ }
+
+ override fun describeMismatchSafely(item: T?, mismatchDescription: Description?) {
+ match.describeMismatch(item, mismatchDescription)
+ }
+
+ override fun describeTo(description: Description?) {
+ match.describeTo(description?.appendText("matches member $member"))
+ }
+ }
+
+// Returns a matcher for IInputDeviceBatteryState that optionally matches some arguments.
+private fun matchesState(
+ deviceId: Int,
+ isPresent: Boolean = true,
+ status: Int? = null,
+ capacity: Float? = null,
+ eventTime: Long? = null
+): Matcher<IInputDeviceBatteryState> {
+ val batteryStateMatchers = mutableListOf<Matcher<IInputDeviceBatteryState>>(
+ memberMatcher("deviceId", { it.deviceId }, equalTo(deviceId)),
+ memberMatcher("isPresent", { it.isPresent }, equalTo(isPresent))
+ )
+ if (eventTime != null) {
+ batteryStateMatchers.add(memberMatcher("updateTime", { it.updateTime }, equalTo(eventTime)))
+ }
+ if (status != null) {
+ batteryStateMatchers.add(memberMatcher("status", { it.status }, equalTo(status)))
+ }
+ if (capacity != null) {
+ batteryStateMatchers.add(memberMatcher("capacity", { it.capacity }, equalTo(capacity)))
+ }
+ return Matchers.allOf(batteryStateMatchers)
+}
+
+private fun isInvalidBatteryState(deviceId: Int): Matcher<IInputDeviceBatteryState> =
+ matchesState(deviceId, isPresent = false, status = STATUS_UNKNOWN, capacity = Float.NaN)
+
+// Helpers used to verify interactions with a mocked battery listener.
+private fun IInputDeviceBatteryListener.verifyNotified(
+ deviceId: Int,
+ mode: VerificationMode = times(1),
+ isPresent: Boolean = true,
+ status: Int? = null,
+ capacity: Float? = null,
+ eventTime: Long? = null
+) {
+ verifyNotified(matchesState(deviceId, isPresent, status, capacity, eventTime), mode)
+}
+
+private fun IInputDeviceBatteryListener.verifyNotified(
+ matcher: Matcher<IInputDeviceBatteryState>,
+ mode: VerificationMode = times(1)
+) {
+ verify(this, mode).onBatteryStateChanged(MockitoHamcrest.argThat(matcher))
+}
+
+private fun createMockListener(): IInputDeviceBatteryListener {
+ val listener = mock(IInputDeviceBatteryListener::class.java)
+ val binder = mock(Binder::class.java)
+ `when`(listener.asBinder()).thenReturn(binder)
+ return listener
+}
+
+/**
+ * Tests for {@link InputDeviceBatteryController}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputDeviceBatteryControllerTests
+ */
+@Presubmit
+class BatteryControllerTests {
+ companion object {
+ const val PID = 42
+ const val DEVICE_ID = 13
+ const val SECOND_DEVICE_ID = 11
+ const val USI_DEVICE_ID = 101
+ const val SECOND_USI_DEVICE_ID = 102
+ const val BT_DEVICE_ID = 100001
+ const val SECOND_BT_DEVICE_ID = 100002
+ const val TIMESTAMP = 123456789L
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ @Mock
+ private lateinit var native: NativeInputManagerService
+ @Mock
+ private lateinit var iInputManager: IInputManager
+ @Mock
+ private lateinit var uEventManager: UEventManager
+ @Mock
+ private lateinit var bluetoothBatteryManager: BluetoothBatteryManager
+
+ private lateinit var batteryController: BatteryController
+ private lateinit var context: TestableContext
+ private lateinit var testLooper: TestLooper
+ private lateinit var devicesChangedListener: IInputDevicesChangedListener
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+ private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
+
+ @Before
+ fun setup() {
+ context = TestableContext(ApplicationProvider.getApplicationContext())
+ testLooper = TestLooper()
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ val inputManager = InputManager(context)
+ context.addMockSystemService(InputManager::class.java, inputManager)
+ `when`(iInputManager.inputDeviceIds).then {
+ deviceGenerationMap.keys.toIntArray()
+ }
+ addInputDevice(DEVICE_ID)
+ addInputDevice(SECOND_DEVICE_ID)
+
+ batteryController = BatteryController(context, native, testLooper.looper, uEventManager,
+ bluetoothBatteryManager)
+ batteryController.systemRunning()
+ val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
+ verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
+ devicesChangedListener = listenerCaptor.value
+ testLooper.dispatchAll()
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun notifyDeviceChanged(
+ deviceId: Int,
+ hasBattery: Boolean = true,
+ supportsUsi: Boolean = false
+ ) {
+ val generation = deviceGenerationMap[deviceId]?.plus(1)
+ ?: throw IllegalArgumentException("Device $deviceId was never added!")
+ deviceGenerationMap[deviceId] = generation
+
+ `when`(iInputManager.getInputDevice(deviceId))
+ .thenReturn(createInputDevice(deviceId, hasBattery, supportsUsi, generation))
+ val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
+ if (::devicesChangedListener.isInitialized) {
+ devicesChangedListener.onInputDevicesChanged(list.toIntArray())
+ }
+ }
+
+ private fun addInputDevice(
+ deviceId: Int,
+ hasBattery: Boolean = true,
+ supportsUsi: Boolean = false,
+ ) {
+ deviceGenerationMap[deviceId] = 0
+ notifyDeviceChanged(deviceId, hasBattery, supportsUsi)
+ }
+
+ private fun createBluetoothDevice(address: String): BluetoothDevice {
+ return context.getSystemService(BluetoothManager::class.java)!!
+ .adapter.getRemoteDevice(address)
+ }
+
+ @Test
+ fun testRegisterAndUnregisterBinderLifecycle() {
+ val listener = createMockListener()
+ // Ensure the binder lifecycle is tracked when registering a listener.
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).linkToDeath(notNull(), anyInt())
+ batteryController.registerBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), times(1)).linkToDeath(notNull(), anyInt())
+
+ // Ensure the binder lifecycle stops being tracked when all devices stopped being monitored.
+ batteryController.unregisterBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), never()).unlinkToDeath(notNull(), anyInt())
+ batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).unlinkToDeath(notNull(), anyInt())
+ }
+
+ @Test
+ fun testOneListenerPerProcess() {
+ val listener1 = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener1, PID)
+ verify(listener1.asBinder()).linkToDeath(notNull(), anyInt())
+
+ // If a second listener is added for the same process, a security exception is thrown.
+ val listener2 = createMockListener()
+ try {
+ batteryController.registerBatteryListener(DEVICE_ID, listener2, PID)
+ fail("Expected security exception when registering more than one listener per process")
+ } catch (ignored: SecurityException) {
+ }
+ }
+
+ @Test
+ fun testProcessDeathRemovesListener() {
+ val deathRecipient = ArgumentCaptor.forClass(IBinder.DeathRecipient::class.java)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder()).linkToDeath(deathRecipient.capture(), anyInt())
+
+ // When the binder dies, the callback is unregistered.
+ deathRecipient.value!!.binderDied(listener.asBinder())
+ verify(listener.asBinder()).unlinkToDeath(notNull(), anyInt())
+
+ // It is now possible to register the same listener again.
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener.asBinder(), times(2)).linkToDeath(notNull(), anyInt())
+ }
+
+ @Test
+ fun testRegisteringListenerNotifiesStateImmediately() {
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_FULL)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(100)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ listener.verifyNotified(DEVICE_ID, status = STATUS_FULL, capacity = 1.0f)
+
+ `when`(native.getBatteryStatus(SECOND_DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(SECOND_DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(SECOND_DEVICE_ID, listener, PID)
+ listener.verifyNotified(SECOND_DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
+ }
+
+ @Test
+ fun testListenersNotifiedOnUEventNotification() {
+ `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/sys/dev/test/device1")
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ // The device paths for UEvent notifications do not include the "/sys" prefix, so verify
+ // that the added listener is configured to match the path without that prefix.
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/test/device1"))
+ listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
+
+ // If the battery state has changed when an UEvent is sent, the listeners are notified.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f,
+ eventTime = TIMESTAMP)
+
+ // If the battery state has not changed when an UEvent is sent, the listeners are not
+ // notified.
+ clearInvocations(listener)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP + 1)
+ verifyNoMoreInteractions(listener)
+
+ batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
+ verify(uEventManager).removeListener(uEventListener.capture())
+ assertEquals("The same observer must be registered and unregistered",
+ uEventListener.allValues[0], uEventListener.allValues[1])
+ }
+
+ @Test
+ fun testBatteryPresenceChanged() {
+ `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
+
+ // If the battery presence for the InputDevice changes, the listener is notified.
+ notifyDeviceChanged(DEVICE_ID, hasBattery = false)
+ testLooper.dispatchNext()
+ listener.verifyNotified(isInvalidBatteryState(DEVICE_ID))
+ // Since the battery is no longer present, the UEventListener should be removed.
+ verify(uEventManager).removeListener(uEventListener.value)
+
+ // If the battery becomes present again, the listener is notified.
+ notifyDeviceChanged(DEVICE_ID, hasBattery = true)
+ testLooper.dispatchNext()
+ listener.verifyNotified(DEVICE_ID, mode = times(2), status = STATUS_CHARGING,
+ capacity = 0.78f)
+ // Ensure that a new UEventListener was added.
+ verify(uEventManager, times(2))
+ .addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ }
+
+ @Test
+ fun testStartPollingWhenListenerIsRegistered() {
+ val listener = createMockListener()
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ listener.verifyNotified(DEVICE_ID, capacity = 0.78f)
+
+ // Assume there is a change in the battery state. Ensure the listener is not notified
+ // while the polling period has not elapsed.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ testLooper.moveTimeForward(1)
+ testLooper.dispatchAll()
+ listener.verifyNotified(DEVICE_ID, mode = never(), capacity = 0.80f)
+
+ // Move the time forward so that the polling period has elapsed.
+ // The listener should be notified.
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS - 1)
+ assertTrue("There should be a polling callbacks posted to the handler", testLooper.isIdle)
+ testLooper.dispatchNext()
+ listener.verifyNotified(DEVICE_ID, capacity = 0.80f)
+
+ // Move the time forward so that another polling period has elapsed.
+ // The battery should still be polled, but there is no change so listeners are not notified.
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
+ assertTrue("There should be a polling callbacks posted to the handler", testLooper.isIdle)
+ testLooper.dispatchNext()
+ listener.verifyNotified(DEVICE_ID, mode = times(1), capacity = 0.80f)
+ }
+
+ @Test
+ fun testNoPollingWhenTheDeviceIsNotInteractive() {
+ batteryController.onInteractiveChanged(false /*interactive*/)
+
+ val listener = createMockListener()
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ listener.verifyNotified(DEVICE_ID, capacity = 0.78f)
+
+ // The battery state changed, but we should not be polling for battery changes when the
+ // device is not interactive.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
+ assertFalse("There should be no polling callbacks posted to the handler", testLooper.isIdle)
+ testLooper.dispatchAll()
+ listener.verifyNotified(DEVICE_ID, mode = never(), capacity = 0.80f)
+
+ // The device is now interactive. Battery state polling begins immediately.
+ batteryController.onInteractiveChanged(true /*interactive*/)
+ testLooper.dispatchNext()
+ listener.verifyNotified(DEVICE_ID, capacity = 0.80f)
+
+ // Ensure that we continue to poll for battery changes.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(90)
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
+ assertTrue("There should be a polling callbacks posted to the handler", testLooper.isIdle)
+ testLooper.dispatchNext()
+ listener.verifyNotified(DEVICE_ID, capacity = 0.90f)
+ }
+
+ @Test
+ fun testGetBatteryState() {
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ val batteryState = batteryController.getBatteryState(DEVICE_ID)
+ assertThat("battery state matches", batteryState,
+ matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f))
+ }
+
+ @Test
+ fun testGetBatteryStateWithListener() {
+ val listener = createMockListener()
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
+
+ // If getBatteryState() is called when a listener is monitoring the device and there is a
+ // change in the battery state, the listener is also notified.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ val batteryState = batteryController.getBatteryState(DEVICE_ID)
+ assertThat("battery matches state", batteryState,
+ matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f))
+ listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f)
+ }
+
+ @Test
+ fun testUsiDeviceIsMonitoredPersistently() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+
+ // Even though there is no listener added for this device, it is being monitored.
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+ // Add and remove a listener for the device.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ batteryController.unregisterBatteryListener(USI_DEVICE_ID, listener, PID)
+
+ // The device is still being monitored.
+ verify(uEventManager, never()).removeListener(uEventListener.value)
+ }
+
+ @Test
+ fun testNoPollingWhenUsiDevicesAreMonitored() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+ `when`(native.getBatteryDevicePath(SECOND_USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device2")
+ addInputDevice(SECOND_USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
+ assertFalse("There should be no polling callbacks posted to the handler", testLooper.isIdle)
+
+ // Add a listener.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+
+ testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
+ assertFalse("There should be no polling callbacks posted to the handler", testLooper.isIdle)
+ }
+
+ @Test
+ fun testExpectedFlowForUsiBattery() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
+
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+ // A USI device's battery state is not valid until the first UEvent notification.
+ // Add a listener, and ensure it is notified that the battery state is not present.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+
+ // Ensure that querying for battery state also returns the same invalid result.
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // There is a UEvent signaling a battery change. The battery state is now valid.
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+ // There is another UEvent notification. The battery state is now updated.
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(64)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP + 1)
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
+
+ // The battery state is still valid after a millisecond.
+ testLooper.moveTimeForward(1)
+ testLooper.dispatchAll()
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
+
+ // The battery is no longer present after the timeout expires.
+ testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 1)
+ testLooper.dispatchNext()
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID), times(2))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+ }
+
+ @Test
+ fun testStylusPresenceExtendsValidUsiBatteryState() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
+
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+ // There is a UEvent signaling a battery change. The battery state is now valid.
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+ // Stylus presence is detected before the validity timeout expires.
+ testLooper.moveTimeForward(100)
+ testLooper.dispatchAll()
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+ // Ensure that timeout was extended, and the battery state is now valid for longer.
+ testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 100)
+ testLooper.dispatchAll()
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+ // Ensure the validity period expires after the expected amount of time.
+ testLooper.moveTimeForward(100)
+ testLooper.dispatchNext()
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+ }
+
+ @Test
+ fun testStylusPresenceMakesUsiBatteryStateValid() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
+
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+ // The USI battery state is initially invalid.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // A stylus presence is detected. This validates the battery state.
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+ }
+
+ @Test
+ fun testStylusPresenceDoesNotMakeUsiBatteryStateValidAtBoot() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ // At boot, the USI device always reports a capacity value of 0.
+ `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_UNKNOWN)
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(0)
+
+ addInputDevice(USI_DEVICE_ID, supportsUsi = true)
+ testLooper.dispatchNext()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+ verify(uEventManager)
+ .addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
+
+ // The USI battery state is initially invalid.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // Since the capacity reported is 0, stylus presence does not validate the battery state.
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // However, if a UEvent reports a battery capacity of 0, the battery state is now valid.
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f))
+ }
+
+ @Test
+ fun testRegisterBluetoothListenerForMonitoredBluetoothDevices() {
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ .thenReturn("11:22:33:44:55:66")
+ addInputDevice(BT_DEVICE_ID)
+ testLooper.dispatchNext()
+ addInputDevice(SECOND_BT_DEVICE_ID)
+ testLooper.dispatchNext()
+
+ // Listen to a non-Bluetooth device and ensure that the BT battery listener is not added
+ // when there are no monitored BT devices.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager, never()).addBatteryListener(any())
+
+ val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
+
+ // The BT battery listener is added when the first BT input device is monitored.
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+
+ // The BT listener is only added once for all BT devices.
+ batteryController.registerBatteryListener(SECOND_BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager, times(1)).addBatteryListener(any())
+
+ // The BT listener is only removed when there are no monitored BT devices.
+ batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager, never()).removeBatteryListener(any())
+
+ `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ .thenReturn(null)
+ notifyDeviceChanged(SECOND_BT_DEVICE_ID)
+ testLooper.dispatchNext()
+ verify(bluetoothBatteryManager).removeBatteryListener(bluetoothListener.value)
+ }
+
+ @Test
+ fun testNotifiesBluetoothBatteryChanges() {
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
+ addInputDevice(BT_DEVICE_ID)
+ val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
+
+ // When the state has not changed, the listener is not notified again.
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 21)
+ listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
+
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 25)
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f)
+ }
+
+ @Test
+ fun testBluetoothBatteryIsPrioritized() {
+ `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
+ `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
+ addInputDevice(BT_DEVICE_ID)
+ val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+
+ // When the device is first monitored and both native and BT battery is available,
+ // the latter is used.
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+ verify(uEventManager).addListener(uEventListener.capture(), any())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
+ assertThat("battery state matches", batteryController.getBatteryState(BT_DEVICE_ID),
+ matchesState(BT_DEVICE_ID, capacity = 0.21f))
+
+ // If only the native battery state changes the listener is not notified.
+ `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(97)
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
+ assertThat("battery state matches", batteryController.getBatteryState(BT_DEVICE_ID),
+ matchesState(BT_DEVICE_ID, capacity = 0.21f))
+ }
+
+ @Test
+ fun testFallBackToNativeBatteryStateWhenBluetoothStateInvalid() {
+ `when`(native.getBatteryDevicePath(BT_DEVICE_ID)).thenReturn("/sys/dev/bt_device")
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
+ `when`(native.getBatteryCapacity(BT_DEVICE_ID)).thenReturn(98)
+ addInputDevice(BT_DEVICE_ID)
+ val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
+
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+ verify(uEventManager).addListener(uEventListener.capture(), any())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
+
+ // Fall back to the native state when BT is off.
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF)
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.98f)
+
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 22)
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.22f)
+
+ // Fall back to the native state when BT battery is unknown.
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.98f)
+ }
+
+ @Test
+ fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() {
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ .thenReturn("11:22:33:44:55:66")
+ addInputDevice(BT_DEVICE_ID)
+ testLooper.dispatchNext()
+ addInputDevice(SECOND_BT_DEVICE_ID)
+ testLooper.dispatchNext()
+
+ // Listen to a non-Bluetooth device and ensure that the metadata listener is not added when
+ // there are no monitored BT devices.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager, never()).addMetadataListener(any(), any())
+
+ val metadataListener1 = ArgumentCaptor.forClass(
+ BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val metadataListener2 = ArgumentCaptor.forClass(
+ BluetoothAdapter.OnMetadataChangedListener::class.java)
+
+ // The metadata listener is added when the first BT input device is monitored.
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager)
+ .addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener1.capture())
+
+ // There is one metadata listener added for each BT device.
+ batteryController.registerBatteryListener(SECOND_BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager)
+ .addMetadataListener(eq("11:22:33:44:55:66"), metadataListener2.capture())
+
+ // The metadata listener is removed when the device is no longer monitored.
+ batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager)
+ .removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value)
+
+ `when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
+ .thenReturn(null)
+ notifyDeviceChanged(SECOND_BT_DEVICE_ID)
+ testLooper.dispatchNext()
+ verify(bluetoothBatteryManager)
+ .removeMetadataListener("11:22:33:44:55:66", metadataListener2.value)
+ }
+
+ @Test
+ fun testNotifiesBluetoothMetadataBatteryChanges() {
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.METADATA_MAIN_BATTERY))
+ .thenReturn("21".toByteArray())
+ addInputDevice(BT_DEVICE_ID)
+ val metadataListener = ArgumentCaptor.forClass(
+ BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val listener = createMockListener()
+ val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+ verify(bluetoothBatteryManager)
+ .addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener.capture())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f, status = STATUS_UNKNOWN)
+
+ // When the state has not changed, the listener is not notified again.
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "21".toByteArray())
+ listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
+
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "25".toByteArray())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_UNKNOWN)
+
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_CHARGING)
+
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "false".toByteArray())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_DISCHARGING)
+
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, null)
+ listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.25f,
+ status = STATUS_UNKNOWN)
+ }
+
+ @Test
+ fun testBluetoothMetadataBatteryIsPrioritized() {
+ `when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
+ .thenReturn("AA:BB:CC:DD:EE:FF")
+ `when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
+ `when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
+ BluetoothDevice.METADATA_MAIN_BATTERY))
+ .thenReturn("22".toByteArray())
+ addInputDevice(BT_DEVICE_ID)
+ val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
+ val metadataListener = ArgumentCaptor.forClass(
+ BluetoothAdapter.OnMetadataChangedListener::class.java)
+ val listener = createMockListener()
+ val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
+ batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
+
+ verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
+ verify(bluetoothBatteryManager)
+ .addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener.capture())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.22f)
+
+ // A change in the Bluetooth battery level has no effect while there is a valid battery
+ // level obtained through the metadata.
+ bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 23)
+ listener.verifyNotified(BT_DEVICE_ID, mode = never(), capacity = 0.23f)
+
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "24".toByteArray())
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.24f)
+
+ // When the battery level from the metadata is no longer valid, we fall back to using the
+ // Bluetooth battery level.
+ metadataListener.value!!.onMetadataChanged(
+ bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, null)
+ listener.verifyNotified(BT_DEVICE_ID, capacity = 0.23f)
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java
new file mode 100644
index 000000000000..9a49d91b1019
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Build/Install/Run:
+ * atest ConfigurationProcessorTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ConfigurationProcessorTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ }
+
+ @Test
+ public void testGetInputPortAssociations() {
+ final int res = com.android.test.input.R.raw.input_port_associations;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ Map<String, Integer> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(2, associations.size());
+ assertEquals(0, associations.get("USB1").intValue());
+ assertEquals(1, associations.get("USB2").intValue());
+ }
+
+ @Test
+ public void testGetInputPortAssociationsBadDisplayport() {
+ final int res =
+ com.android.test.input.R.raw.input_port_associations_bad_displayport;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ Map<String, Integer> associations = null;
+ try {
+ associations = ConfigurationProcessor.processInputPortAssociations(xml);
+ } catch (Exception e) {
+ fail("Could not process xml file for input associations");
+ }
+ assertNotNull(associations);
+ assertEquals(0, associations.size());
+ }
+
+ @Test
+ public void testGetInputPortAssociationsEmptyConfig() {
+ final int res = com.android.test.input.R.raw.input_port_associations_bad_xml;
+ InputStream xml = mContext.getResources().openRawResource(res);
+ try {
+ ConfigurationProcessor.processInputPortAssociations(xml);
+ fail("Parsing should fail, because xml contains bad data");
+ } catch (Exception e) {
+ // This is expected
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
new file mode 100644
index 000000000000..c1784f3b42e7
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2022 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.display.DisplayViewport
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
+import android.os.IInputConstants
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import android.test.mock.MockContentResolver
+import android.view.Display
+import android.view.PointerIcon
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.util.test.FakeSettingsProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.junit.MockitoJUnit
+import org.mockito.stubbing.OngoingStubbing
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+
+/**
+ * Tests for {@link InputManagerService}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:InputManagerServiceTests
+ */
+@Presubmit
+class InputManagerServiceTests {
+
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()!!
+
+ @get:Rule
+ val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
+
+ @Mock
+ private lateinit var native: NativeInputManagerService
+
+ @Mock
+ private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
+
+ @Mock
+ private lateinit var uEventManager: UEventManager
+
+ private lateinit var service: InputManagerService
+ private lateinit var localService: InputManagerInternal
+ private lateinit var context: Context
+ private lateinit var testLooper: TestLooper
+ private lateinit var contentResolver: MockContentResolver
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ @Before
+ fun setup() {
+ context = spy(ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext()))
+ contentResolver = MockContentResolver(context)
+ contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider())
+ whenever(context.contentResolver).thenReturn(contentResolver)
+ testLooper = TestLooper()
+ service =
+ InputManagerService(object : InputManagerService.Injector(
+ context, testLooper.looper, uEventManager) {
+ override fun getNativeService(
+ service: InputManagerService?
+ ): NativeInputManagerService {
+ return native
+ }
+
+ override fun registerLocalService(service: InputManagerInternal?) {
+ localService = service!!
+ }
+ })
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(service)
+ val inputManager = InputManager(context)
+ whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
+ whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
+
+ assertTrue("Local service must be registered", this::localService.isInitialized)
+ service.setWindowManagerCallbacks(wmCallbacks)
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ @Test
+ fun testStart() {
+ verifyZeroInteractions(native)
+
+ service.start()
+ verify(native).start()
+ }
+
+ @Test
+ fun testInputSettingsUpdatedOnSystemRunning() {
+ verifyZeroInteractions(native)
+
+ service.systemRunning()
+
+ verify(native).setPointerSpeed(anyInt())
+ verify(native).setTouchpadPointerSpeed(anyInt())
+ verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean())
+ verify(native).setTouchpadTapToClickEnabled(anyBoolean())
+ verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
+ verify(native).setShowTouches(anyBoolean())
+ verify(native).reloadPointerIcons()
+ verify(native).setMotionClassifierEnabled(anyBoolean())
+ verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
+ verify(native).setStylusPointerIconEnabled(anyBoolean())
+ // Called twice at boot, since there are individual callbacks to update the
+ // key repeat timeout and the key repeat delay.
+ verify(native, times(2)).setKeyRepeatConfiguration(anyInt(), anyInt())
+ }
+
+ @Test
+ fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() {
+ val displayId = 123
+ whenever(wmCallbacks.pointerDisplayId).thenReturn(displayId)
+ val viewports = listOf<DisplayViewport>()
+ localService.setDisplayViewports(viewports)
+ verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java))
+ verify(native).setPointerDisplayId(displayId)
+
+ val x = 42f
+ val y = 314f
+ service.onPointerDisplayIdChanged(displayId, x, y)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(displayId, x, y)
+ }
+
+ @Test
+ fun testSetVirtualMousePointerDisplayId() {
+ // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
+ // until the native callback happens.
+ var countDownLatch = CountDownLatch(1)
+ val overrideDisplayId = 123
+ Thread {
+ assertTrue("Setting virtual pointer display should succeed",
+ localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+ countDownLatch.countDown()
+ }.start()
+ assertFalse("Setting virtual pointer display should block",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+ val x = 42f
+ val y = 314f
+ service.onPointerDisplayIdChanged(overrideDisplayId, x, y)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, x, y)
+ assertTrue("Native callback unblocks calling thread",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+ verify(native).setPointerDisplayId(overrideDisplayId)
+
+ // Ensure that setting the same override again succeeds immediately.
+ assertTrue("Setting the same virtual mouse pointer displayId again should succeed",
+ localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+
+ // Ensure that we did not query WM for the pointerDisplayId when setting the override
+ verify(wmCallbacks, never()).pointerDisplayId
+
+ // Unset the virtual mouse pointer displayId, and ensure that we query WM for the new
+ // pointer displayId and the calling thread is blocked until the native callback happens.
+ countDownLatch = CountDownLatch(1)
+ val pointerDisplayId = 42
+ `when`(wmCallbacks.pointerDisplayId).thenReturn(pointerDisplayId)
+ Thread {
+ assertTrue("Unsetting virtual mouse pointer displayId should succeed",
+ localService.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY))
+ countDownLatch.countDown()
+ }.start()
+ assertFalse("Unsetting virtual mouse pointer displayId should block",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+ service.onPointerDisplayIdChanged(pointerDisplayId, x, y)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(pointerDisplayId, x, y)
+ assertTrue("Native callback unblocks calling thread",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+ verify(native).setPointerDisplayId(pointerDisplayId)
+ }
+
+ @Test
+ fun testSetVirtualMousePointerDisplayId_unsuccessfulUpdate() {
+ // Set the virtual mouse pointer displayId, and ensure that the calling thread is blocked
+ // until the native callback happens.
+ val countDownLatch = CountDownLatch(1)
+ val overrideDisplayId = 123
+ Thread {
+ assertFalse("Setting virtual pointer display should be unsuccessful",
+ localService.setVirtualMousePointerDisplayId(overrideDisplayId))
+ countDownLatch.countDown()
+ }.start()
+ assertFalse("Setting virtual pointer display should block",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+
+ val x = 42f
+ val y = 314f
+ // Assume the native callback updates the pointerDisplayId to the incorrect value.
+ service.onPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(Display.INVALID_DISPLAY, x, y)
+ assertTrue("Native callback unblocks calling thread",
+ countDownLatch.await(100, TimeUnit.MILLISECONDS))
+ verify(native).setPointerDisplayId(overrideDisplayId)
+ }
+
+ @Test
+ fun testSetVirtualMousePointerDisplayId_competingRequests() {
+ val firstRequestSyncLatch = CountDownLatch(1)
+ doAnswer {
+ firstRequestSyncLatch.countDown()
+ }.`when`(native).setPointerDisplayId(anyInt())
+
+ val firstRequestLatch = CountDownLatch(1)
+ val firstOverride = 123
+ Thread {
+ assertFalse("Setting virtual pointer display from thread 1 should be unsuccessful",
+ localService.setVirtualMousePointerDisplayId(firstOverride))
+ firstRequestLatch.countDown()
+ }.start()
+ assertFalse("Setting virtual pointer display should block",
+ firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
+
+ assertTrue("Wait for first thread's request should succeed",
+ firstRequestSyncLatch.await(100, TimeUnit.MILLISECONDS))
+
+ val secondRequestLatch = CountDownLatch(1)
+ val secondOverride = 42
+ Thread {
+ assertTrue("Setting virtual mouse pointer from thread 2 should be successful",
+ localService.setVirtualMousePointerDisplayId(secondOverride))
+ secondRequestLatch.countDown()
+ }.start()
+ assertFalse("Setting virtual mouse pointer should block",
+ secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
+
+ val x = 42f
+ val y = 314f
+ // Assume the native callback updates directly to the second request.
+ service.onPointerDisplayIdChanged(secondOverride, x, y)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(secondOverride, x, y)
+ assertTrue("Native callback unblocks first thread",
+ firstRequestLatch.await(100, TimeUnit.MILLISECONDS))
+ assertTrue("Native callback unblocks second thread",
+ secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
+ verify(native, times(2)).setPointerDisplayId(anyInt())
+ }
+
+ @Test
+ fun onDisplayRemoved_resetAllAdditionalInputProperties() {
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
+ localService.setPointerIconVisible(false, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ localService.setPointerAcceleration(5f, 10)
+ verify(native).setPointerAcceleration(eq(5f))
+
+ service.onDisplayRemoved(10)
+ verify(native).displayRemoved(eq(10))
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+ verify(native).setPointerAcceleration(
+ eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+ verifyNoMoreInteractions(native)
+
+ // This call should not block because the virtual mouse pointer override was never removed.
+ localService.setVirtualMousePointerDisplayId(10)
+
+ verify(native).setPointerDisplayId(eq(10))
+ verifyNoMoreInteractions(native)
+ }
+
+ @Test
+ fun updateAdditionalInputPropertiesForOverrideDisplay() {
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
+ localService.setPointerIconVisible(false, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ localService.setPointerAcceleration(5f, 10)
+ verify(native).setPointerAcceleration(eq(5f))
+
+ localService.setPointerIconVisible(true, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+ localService.setPointerAcceleration(1f, 10)
+ verify(native).setPointerAcceleration(eq(1f))
+
+ // Verify that setting properties on a different display is not propagated until the
+ // pointer is moved to that display.
+ localService.setPointerIconVisible(false, 20)
+ localService.setPointerAcceleration(6f, 20)
+ verifyNoMoreInteractions(native)
+
+ clearInvocations(native)
+ setVirtualMousePointerDisplayIdAndVerify(20)
+
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ verify(native).setPointerAcceleration(eq(6f))
+ }
+
+ @Test
+ fun setAdditionalInputPropertiesBeforeOverride() {
+ localService.setPointerIconVisible(false, 10)
+ localService.setPointerAcceleration(5f, 10)
+
+ verifyNoMoreInteractions(native)
+
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ verify(native).setPointerAcceleration(eq(5f))
+ }
+
+ @Test
+ fun setDeviceTypeAssociation_setsDeviceTypeAssociation() {
+ val inputPort = "inputPort"
+ val type = "type"
+
+ localService.setTypeAssociation(inputPort, type)
+
+ assertThat(service.getDeviceTypeAssociations()).asList().containsExactly(inputPort, type)
+ .inOrder()
+ }
+
+ @Test
+ fun setAndUnsetDeviceTypeAssociation_deviceTypeAssociationIsMissing() {
+ val inputPort = "inputPort"
+ val type = "type"
+
+ localService.setTypeAssociation(inputPort, type)
+ localService.unsetTypeAssociation(inputPort)
+
+ assertTrue(service.getDeviceTypeAssociations().isEmpty())
+ }
+
+ @Test
+ fun testAddAndRemoveVirtualmKeyboardLayoutAssociation() {
+ val inputPort = "input port"
+ val languageTag = "language"
+ val layoutType = "layoutType"
+ localService.addKeyboardLayoutAssociation(inputPort, languageTag, layoutType)
+ verify(native).changeKeyboardLayoutAssociation()
+
+ localService.removeKeyboardLayoutAssociation(inputPort)
+ verify(native, times(2)).changeKeyboardLayoutAssociation()
+ }
+
+ private fun setVirtualMousePointerDisplayIdAndVerify(overrideDisplayId: Int) {
+ val thread = Thread { localService.setVirtualMousePointerDisplayId(overrideDisplayId) }
+ thread.start()
+
+ // Allow some time for the set override call to park while waiting for the native callback.
+ Thread.sleep(100 /*millis*/)
+ verify(native).setPointerDisplayId(overrideDisplayId)
+
+ service.onPointerDisplayIdChanged(overrideDisplayId, 0f, 0f)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, 0f, 0f)
+ thread.join(100 /*millis*/)
+ }
+}
+
+private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
diff --git a/tests/Input/src/com/android/server/input/InputShellCommandTest.java b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
new file mode 100644
index 000000000000..f4845a518b20
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/InputShellCommandTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2023 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 static android.view.InputDevice.SOURCE_MOUSE;
+import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
+import static android.view.MotionEvent.ACTION_SCROLL;
+import static android.view.MotionEvent.AXIS_HSCROLL;
+import static android.view.MotionEvent.AXIS_SCROLL;
+import static android.view.MotionEvent.AXIS_VSCROLL;
+import static android.view.MotionEvent.AXIS_X;
+import static android.view.MotionEvent.AXIS_Y;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.os.Binder;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+/**
+ * Build/Install/Run:
+ * atest InputShellCommandTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class InputShellCommandTest {
+ private TestInputEventInjector mInputEventInjector = new TestInputEventInjector();
+
+ private InputShellCommand mCommand;
+
+ @Before
+ public void setUp() throws Exception {
+ mCommand = new InputShellCommand(mInputEventInjector);
+ }
+
+ @Test
+ public void testScroll_withPointerSource_noAxisOption() {
+ runCommand("mouse scroll 2 -3");
+
+ MotionEvent event = (MotionEvent) getSingleInjectedInputEvent();
+
+ assertSourceAndAction(event, SOURCE_MOUSE, ACTION_SCROLL);
+ assertAxisValues(event, Map.of(AXIS_X, 2f, AXIS_Y, -3f));
+ }
+
+ @Test
+ public void testScroll_withPointerSource_withScrollAxisOptions() {
+ runCommand("mouse scroll 1 -2 --axis HSCROLL,3 --axis VSCROLL,1.7 --axis SCROLL,-4");
+
+ MotionEvent event = (MotionEvent) getSingleInjectedInputEvent();
+
+ assertSourceAndAction(event, SOURCE_MOUSE, ACTION_SCROLL);
+ assertAxisValues(
+ event,
+ Map.of(
+ AXIS_X, 1f,
+ AXIS_Y, -2f,
+ AXIS_HSCROLL, 3f,
+ AXIS_VSCROLL, 1.7f,
+ AXIS_SCROLL, -4f));
+ }
+
+ @Test
+ public void testScroll_withNonPointerSource_noAxisOption() {
+ runCommand("rotaryencoder scroll");
+
+ MotionEvent event = (MotionEvent) getSingleInjectedInputEvent();
+
+ assertSourceAndAction(event, SOURCE_ROTARY_ENCODER, ACTION_SCROLL);
+ }
+
+ @Test
+ public void testScroll_withNonPointerSource_withScrollAxisOptions() {
+ runCommand("rotaryencoder scroll --axis HSCROLL,3 --axis VSCROLL,1.7 --axis SCROLL,-4");
+
+ MotionEvent event = (MotionEvent) getSingleInjectedInputEvent();
+
+ assertSourceAndAction(event, SOURCE_ROTARY_ENCODER, ACTION_SCROLL);
+ assertAxisValues(event, Map.of(AXIS_HSCROLL, 3f, AXIS_VSCROLL, 1.7f, AXIS_SCROLL, -4f));
+ }
+
+ @Test
+ public void testDefaultScrollSource() {
+ runCommand("scroll --axis SCROLL,-4");
+
+ MotionEvent event = (MotionEvent) getSingleInjectedInputEvent();
+
+ assertSourceAndAction(event, SOURCE_ROTARY_ENCODER, ACTION_SCROLL);
+ assertAxisValues(event, Map.of(AXIS_SCROLL, -4f));
+ }
+
+ @Test
+ public void testInvalidScrollCommands() {
+ runCommand("scroll --sdaxis SCROLL,-4"); // invalid option
+ runCommand("scroll --axis MYAXIS,-4"); // invalid axis
+ runCommand("scroll --AXIS SCROLL,-4"); // invalid axis option key
+ runCommand("scroll --axis SCROLL,-4abc"); // invalid axis value
+
+ assertThat(mInputEventInjector.mInjectedEvents).isEmpty();
+ }
+
+ private InputEvent getSingleInjectedInputEvent() {
+ assertThat(mInputEventInjector.mInjectedEvents).hasSize(1);
+ return mInputEventInjector.mInjectedEvents.get(0);
+ }
+
+ private void assertSourceAndAction(MotionEvent event, int source, int action) {
+ assertThat(event.getSource()).isEqualTo(source);
+ assertThat(event.getAction()).isEqualTo(action);
+ }
+
+ private void assertAxisValues(MotionEvent event, Map<Integer, Float> expectedValues) {
+ for (var entry : expectedValues.entrySet()) {
+ final int axis = entry.getKey();
+ final float expectedValue = entry.getValue();
+ final float axisValue = event.getAxisValue(axis);
+ assertWithMessage(
+ String.format(
+ "Expected [%f], found [%f] for axis %s",
+ expectedValue,
+ axisValue,
+ MotionEvent.axisToString(axis)))
+ .that(axisValue).isEqualTo(expectedValue);
+ }
+ }
+
+ private void runCommand(String cmd) {
+ mCommand.exec(
+ new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
+ cmd.split(" ") /* args */);
+ }
+
+ private static class TestInputEventInjector implements BiConsumer<InputEvent, Integer> {
+ List<InputEvent> mInjectedEvents = new ArrayList<>();
+
+ @Override
+ public void accept(InputEvent event, Integer injectMode) {
+ mInjectedEvents.add(event);
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
new file mode 100644
index 000000000000..f74fd723d540
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 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.IInputManager
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import android.view.InputDevice
+import android.view.KeyEvent
+import androidx.test.core.app.ApplicationProvider
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.junit.MockitoJUnit
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+private fun createKeyboard(deviceId: Int): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .build()
+
+/**
+ * Tests for {@link KeyRemapper}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyRemapperTests
+ */
+@Presubmit
+class KeyRemapperTests {
+
+ companion object {
+ const val DEVICE_ID = 1
+ val REMAPPABLE_KEYS = intArrayOf(
+ KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT,
+ KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT,
+ KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT,
+ KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_SHIFT_RIGHT,
+ KeyEvent.KEYCODE_CAPS_LOCK
+ )
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ @Mock
+ private lateinit var iInputManager: IInputManager
+ @Mock
+ private lateinit var native: NativeInputManagerService
+ private lateinit var mKeyRemapper: KeyRemapper
+ private lateinit var context: Context
+ private lateinit var dataStore: PersistentDataStore
+ private lateinit var testLooper: TestLooper
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ @Before
+ fun setup() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
+ override fun openRead(): InputStream? {
+ throw FileNotFoundException()
+ }
+
+ override fun startWrite(): FileOutputStream? {
+ throw IOException()
+ }
+
+ override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
+ })
+ testLooper = TestLooper()
+ mKeyRemapper = KeyRemapper(
+ context,
+ native,
+ dataStore,
+ testLooper.looper
+ )
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ val inputManager = InputManager(context)
+ Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+ Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ @Test
+ fun testKeyRemapping_whenRemappingEnabled() {
+ ModifierRemappingFlag(true).use {
+ val keyboard = createKeyboard(DEVICE_ID)
+ Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+
+ for (i in REMAPPABLE_KEYS.indices) {
+ val fromKeyCode = REMAPPABLE_KEYS[i]
+ val toKeyCode = REMAPPABLE_KEYS[(i + 1) % REMAPPABLE_KEYS.size]
+ mKeyRemapper.remapKey(fromKeyCode, toKeyCode)
+ testLooper.dispatchNext()
+ }
+
+ val remapping = mKeyRemapper.keyRemapping
+ val expectedSize = REMAPPABLE_KEYS.size
+ assertEquals("Remapping size should be $expectedSize", expectedSize, remapping.size)
+
+ for (i in REMAPPABLE_KEYS.indices) {
+ val fromKeyCode = REMAPPABLE_KEYS[i]
+ val toKeyCode = REMAPPABLE_KEYS[(i + 1) % REMAPPABLE_KEYS.size]
+ assertEquals(
+ "Remapping should include mapping from $fromKeyCode to $toKeyCode",
+ toKeyCode,
+ remapping.getOrDefault(fromKeyCode, -1)
+ )
+ }
+
+ mKeyRemapper.clearAllKeyRemappings()
+ testLooper.dispatchNext()
+
+ assertEquals(
+ "Remapping size should be 0 after clearAllModifierKeyRemappings",
+ 0,
+ mKeyRemapper.keyRemapping.size
+ )
+ }
+ }
+
+ @Test
+ fun testKeyRemapping_whenRemappingDisabled() {
+ ModifierRemappingFlag(false).use {
+ val keyboard = createKeyboard(DEVICE_ID)
+ Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboard)
+
+ mKeyRemapper.remapKey(REMAPPABLE_KEYS[0], REMAPPABLE_KEYS[1])
+ testLooper.dispatchAll()
+
+ val remapping = mKeyRemapper.keyRemapping
+ assertEquals(
+ "Remapping should not be done if modifier key remapping is disabled",
+ 0,
+ remapping.size
+ )
+ }
+ }
+
+ private inner class ModifierRemappingFlag constructor(enabled: Boolean) : AutoCloseable {
+ init {
+ Settings.Global.putString(
+ context.contentResolver,
+ "settings_new_keyboard_modifier_key", enabled.toString()
+ )
+ }
+
+ override fun close() {
+ Settings.Global.putString(
+ context.contentResolver,
+ "settings_new_keyboard_modifier_key",
+ ""
+ )
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
new file mode 100644
index 000000000000..59aa96c46336
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 2022 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.animation.ValueAnimator
+import android.content.Context
+import android.content.ContextWrapper
+import android.graphics.Color
+import android.hardware.input.IInputManager
+import android.hardware.input.IKeyboardBacklightListener
+import android.hardware.input.IKeyboardBacklightState
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
+import android.hardware.lights.Light
+import android.os.UEventObserver
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL
+import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS
+import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+private fun createKeyboard(deviceId: Int): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .build()
+
+private fun createLight(lightId: Int, lightType: Int): Light =
+ createLight(
+ lightId,
+ lightType,
+ null
+ )
+
+private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels: IntArray?): Light =
+ Light(
+ lightId,
+ "Light $lightId",
+ 1,
+ lightType,
+ Light.LIGHT_CAPABILITY_BRIGHTNESS,
+ suggestedBrightnessLevels
+ )
+/**
+ * Tests for {@link KeyboardBacklightController}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardBacklightControllerTests
+ */
+@Presubmit
+class KeyboardBacklightControllerTests {
+ companion object {
+ const val DEVICE_ID = 1
+ const val LIGHT_ID = 2
+ const val SECOND_LIGHT_ID = 3
+ const val MAX_BRIGHTNESS = 255
+ }
+
+ @get:Rule
+ val rule = MockitoJUnit.rule()!!
+
+ @Mock
+ private lateinit var iInputManager: IInputManager
+ @Mock
+ private lateinit var native: NativeInputManagerService
+ @Mock
+ private lateinit var uEventManager: UEventManager
+ private lateinit var keyboardBacklightController: KeyboardBacklightController
+ private lateinit var context: Context
+ private lateinit var dataStore: PersistentDataStore
+ private lateinit var testLooper: TestLooper
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+ private var lightColorMap: HashMap<Int, Int> = HashMap()
+ private var lastBacklightState: KeyboardBacklightState? = null
+ private var sysfsNodeChanges = 0
+ private var lastAnimationValues = IntArray(2)
+
+ @Before
+ fun setup() {
+ context = spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
+ override fun openRead(): InputStream? {
+ throw FileNotFoundException()
+ }
+
+ override fun startWrite(): FileOutputStream? {
+ throw IOException()
+ }
+
+ override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
+ })
+ testLooper = TestLooper()
+ keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
+ testLooper.looper, FakeAnimatorFactory(), uEventManager)
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ val inputManager = InputManager(context)
+ `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
+ `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
+ `when`(native.setLightColor(anyInt(), anyInt(), anyInt())).then {
+ val args = it.arguments
+ lightColorMap.put(args[1] as Int, args[2] as Int)
+ }
+ `when`(native.getLightColor(anyInt(), anyInt())).thenAnswer {
+ val args = it.arguments
+ lightColorMap.getOrDefault(args[1] as Int, 0)
+ }
+ lightColorMap.clear()
+ `when`(native.sysfsNodeChanged(any())).then {
+ sysfsNodeChanges++
+ }
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightIncrementDecrement() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL)
+ }
+ }
+
+ @Test
+ fun testKeyboardWithoutBacklight() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithoutBacklight = createKeyboard(DEVICE_ID)
+ val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ incrementKeyboardBacklight(DEVICE_ID)
+ assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty())
+ }
+ }
+
+ @Test
+ fun testKeyboardWithMultipleLight() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(
+ listOf(
+ keyboardBacklight,
+ keyboardInputLight
+ )
+ )
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ incrementKeyboardBacklight(DEVICE_ID)
+ assertEquals("Only keyboard backlights should change", 1, lightColorMap.size)
+ assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID])
+ assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID])
+ }
+ }
+
+ @Test
+ fun testRestoreBacklightOnInputDeviceAdded() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+
+ for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) {
+ dataStore.setKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor,
+ LIGHT_ID,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[level] - 1
+ )
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchNext()
+ assertEquals(
+ "Keyboard backlight level should be restored to the level saved in the " +
+ "data store",
+ Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+ keyboardBacklightController.onInputDeviceRemoved(DEVICE_ID)
+ }
+ }
+ }
+
+ @Test
+ fun testRestoreBacklightOnInputDeviceChanged() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ dataStore.setKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor,
+ LIGHT_ID,
+ MAX_BRIGHTNESS
+ )
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchNext()
+ assertTrue(
+ "Keyboard backlight should not be changed until its added",
+ lightColorMap.isEmpty()
+ )
+
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceChanged(DEVICE_ID)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchNext()
+ assertEquals(
+ "Keyboard backlight level should be restored to the level saved in the data store",
+ Color.argb(MAX_BRIGHTNESS, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklight_registerUnregisterListener() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ // Register backlight listener
+ val listener = KeyboardBacklightListener()
+ keyboardBacklightController.registerKeyboardBacklightListener(listener, 0)
+
+ lastBacklightState = null
+ keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID)
+ testLooper.dispatchNext()
+
+ assertEquals(
+ "Backlight state device Id should be $DEVICE_ID",
+ DEVICE_ID,
+ lastBacklightState!!.deviceId
+ )
+ assertEquals(
+ "Backlight state brightnessLevel should be 1",
+ 1,
+ lastBacklightState!!.brightnessLevel
+ )
+ assertEquals(
+ "Backlight state maxBrightnessLevel should be $maxLevel",
+ maxLevel,
+ lastBacklightState!!.maxBrightnessLevel
+ )
+ assertEquals(
+ "Backlight state isTriggeredByKeyPress should be true",
+ true,
+ lastBacklightState!!.isTriggeredByKeyPress
+ )
+
+ // Unregister listener
+ keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0)
+
+ lastBacklightState = null
+ incrementKeyboardBacklight(DEVICE_ID)
+
+ assertNull("Listener should not receive any updates", lastBacklightState)
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklight_userActivity() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ dataStore.setKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor,
+ LIGHT_ID,
+ MAX_BRIGHTNESS
+ )
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchNext()
+ assertEquals(
+ "Keyboard backlight level should be restored to the level saved in the data store",
+ Color.argb(MAX_BRIGHTNESS, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+
+ testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000)
+ testLooper.dispatchNext()
+ assertEquals(
+ "Keyboard backlight level should be turned off after inactivity",
+ 0,
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklight_displayOnOff() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ dataStore.setKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor,
+ LIGHT_ID,
+ MAX_BRIGHTNESS
+ )
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */)
+ assertEquals(
+ "Keyboard backlight level should be restored to the level saved in the data " +
+ "store when display turned on",
+ Color.argb(MAX_BRIGHTNESS, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+
+ keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */)
+ assertEquals(
+ "Keyboard backlight level should be turned off after display is turned off",
+ 0,
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightSysfsNodeAdded_AfterInputDeviceAdded() {
+ var counter = sysfsNodeChanges
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::no_backlight\u0000"
+ ))
+ assertEquals(
+ "Should not reload sysfs node if UEvent path doesn't contain kbd_backlight",
+ counter,
+ sysfsNodeChanges
+ )
+
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=power\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ ))
+ assertEquals(
+ "Should not reload sysfs node if UEvent doesn't belong to subsystem LED",
+ counter,
+ sysfsNodeChanges
+ )
+
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=remove\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ ))
+ assertEquals(
+ "Should not reload sysfs node if UEvent doesn't have ACTION(add)",
+ counter,
+ sysfsNodeChanges
+ )
+
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/pqr/abc::kbd_backlight\u0000"
+ ))
+ assertEquals(
+ "Should not reload sysfs node if UEvent path doesn't belong to leds/ directory",
+ counter,
+ sysfsNodeChanges
+ )
+
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc::kbd_backlight\u0000"
+ ))
+ assertEquals(
+ "Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
+ ++counter,
+ sysfsNodeChanges
+ )
+
+ keyboardBacklightController.onKeyboardBacklightUEvent(UEventObserver.UEvent(
+ "ACTION=add\u0000SUBSYSTEM=leds\u0000DEVPATH=/xyz/leds/abc:kbd_backlight:red\u0000"
+ ))
+ assertEquals(
+ "Should reload sysfs node if a valid Keyboard backlight LED UEvent occurs",
+ ++counter,
+ sysfsNodeChanges
+ )
+ }
+
+ @Test
+ @UiThreadTest
+ fun testKeyboardBacklightAnimation_onChangeLevels() {
+ KeyboardBacklightFlags(
+ animationEnabled = true,
+ customLevelsEnabled = false,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ incrementKeyboardBacklight(DEVICE_ID)
+ assertEquals(
+ "Should start animation from level 0",
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[0],
+ lastAnimationValues[0]
+ )
+ assertEquals(
+ "Should start animation to level 1",
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1],
+ lastAnimationValues[1]
+ )
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightPreferredLevels() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = true,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
+ suggestedLevels)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
+ suggestedLevels)
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightPreferredLevels_moreThanMax_shouldUseDefault() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = true,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
+ suggestedLevels)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL)
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightPreferredLevels_mustHaveZeroAndMaxBrightnessAsBounds() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = true,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val suggestedLevels = intArrayOf(22, 63, 135, 196)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
+ suggestedLevels)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ // Framework will add the lowest and maximum levels if not provided via config
+ assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
+ intArrayOf(0, 22, 63, 135, 196, 255))
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklightPreferredLevels_dropsOutOfBoundsLevels() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = true,
+ ambientControlEnabled = false
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
+ suggestedLevels)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+
+ // Framework will drop out of bound levels in the config
+ assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight,
+ intArrayOf(0, 22, 63, 135, 196, 255))
+ }
+ }
+
+ @Test
+ fun testAmbientBacklightControl_doesntRestoreBacklightLevel() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = true
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+
+ dataStore.setKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor,
+ LIGHT_ID,
+ DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1]
+ )
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchNext()
+ assertNull(
+ "Keyboard backlight level should not be restored to the saved level",
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testAmbientBacklightControl_doesntBackupBacklightLevel() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = true
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ incrementKeyboardBacklight(DEVICE_ID)
+ assertFalse(
+ "Light value should not be backed up if ambient control is enabled",
+ dataStore.getKeyboardBacklightBrightness(
+ keyboardWithBacklight.descriptor, LIGHT_ID
+ ).isPresent
+ )
+ }
+ }
+
+ @Test
+ fun testAmbientBacklightControl_incrementLevel_afterAmbientChange() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = true
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ sendAmbientBacklightValue(1)
+ assertEquals(
+ "Light value should be changed to ambient provided value",
+ Color.argb(1, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+
+ incrementKeyboardBacklight(DEVICE_ID)
+
+ assertEquals(
+ "Light value for level after increment post Ambient change is mismatched",
+ Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testAmbientBacklightControl_decrementLevel_afterAmbientChange() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = true
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ sendAmbientBacklightValue(254)
+ assertEquals(
+ "Light value should be changed to ambient provided value",
+ Color.argb(254, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+
+ decrementKeyboardBacklight(DEVICE_ID)
+
+ val numLevels = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size
+ assertEquals(
+ "Light value for level after decrement post Ambient change is mismatched",
+ Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[numLevels - 2], 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ @Test
+ fun testAmbientBacklightControl_ambientChanges_afterManualChange() {
+ KeyboardBacklightFlags(
+ animationEnabled = false,
+ customLevelsEnabled = false,
+ ambientControlEnabled = true
+ ).use {
+ val keyboardWithBacklight = createKeyboard(DEVICE_ID)
+ val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
+ `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
+ `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight))
+ keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
+ incrementKeyboardBacklight(DEVICE_ID)
+ assertEquals(
+ "Light value should be changed to the first level",
+ Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+
+ sendAmbientBacklightValue(100)
+ assertNotEquals(
+ "Light value should not change based on ambient changes after manual changes",
+ Color.argb(100, 0, 0, 0),
+ lightColorMap[LIGHT_ID]
+ )
+ }
+ }
+
+ private fun assertIncrementDecrementForLevels(
+ device: InputDevice,
+ light: Light,
+ expectedLevels: IntArray
+ ) {
+ val deviceId = device.id
+ val lightId = light.id
+ for (level in 1 until expectedLevels.size) {
+ incrementKeyboardBacklight(deviceId)
+ assertEquals(
+ "Light value for level $level mismatched",
+ Color.argb(expectedLevels[level], 0, 0, 0),
+ lightColorMap[lightId]
+ )
+ assertEquals(
+ "Light value for level $level must be correctly stored in the datastore",
+ expectedLevels[level],
+ dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt
+ )
+ }
+
+ // Increment above max level
+ incrementKeyboardBacklight(deviceId)
+ assertEquals(
+ "Light value for max level mismatched",
+ Color.argb(MAX_BRIGHTNESS, 0, 0, 0),
+ lightColorMap[lightId]
+ )
+ assertEquals(
+ "Light value for max level must be correctly stored in the datastore",
+ MAX_BRIGHTNESS,
+ dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt
+ )
+
+ for (level in expectedLevels.size - 2 downTo 0) {
+ decrementKeyboardBacklight(deviceId)
+ assertEquals(
+ "Light value for level $level mismatched",
+ Color.argb(expectedLevels[level], 0, 0, 0),
+ lightColorMap[lightId]
+ )
+ assertEquals(
+ "Light value for level $level must be correctly stored in the datastore",
+ expectedLevels[level],
+ dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt
+ )
+ }
+
+ // Decrement below min level
+ decrementKeyboardBacklight(deviceId)
+ assertEquals(
+ "Light value for min level mismatched",
+ Color.argb(0, 0, 0, 0),
+ lightColorMap[lightId]
+ )
+ assertEquals(
+ "Light value for min level must be correctly stored in the datastore",
+ 0,
+ dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt
+ )
+ }
+
+ inner class KeyboardBacklightListener : IKeyboardBacklightListener.Stub() {
+ override fun onBrightnessChanged(
+ deviceId: Int,
+ state: IKeyboardBacklightState,
+ isTriggeredByKeyPress: Boolean
+ ) {
+ lastBacklightState = KeyboardBacklightState(
+ deviceId,
+ state.brightnessLevel,
+ state.maxBrightnessLevel,
+ isTriggeredByKeyPress
+ )
+ }
+ }
+
+ private fun incrementKeyboardBacklight(deviceId: Int) {
+ keyboardBacklightController.incrementKeyboardBacklight(deviceId)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchAll()
+ }
+
+ private fun decrementKeyboardBacklight(deviceId: Int) {
+ keyboardBacklightController.decrementKeyboardBacklight(deviceId)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchAll()
+ }
+
+ private fun sendAmbientBacklightValue(brightnessValue: Int) {
+ keyboardBacklightController.handleAmbientLightValueChanged(brightnessValue)
+ keyboardBacklightController.notifyUserActivity()
+ testLooper.dispatchAll()
+ }
+
+ class KeyboardBacklightState(
+ val deviceId: Int,
+ val brightnessLevel: Int,
+ val maxBrightnessLevel: Int,
+ val isTriggeredByKeyPress: Boolean
+ )
+
+ private inner class KeyboardBacklightFlags constructor(
+ animationEnabled: Boolean,
+ customLevelsEnabled: Boolean,
+ ambientControlEnabled: Boolean
+ ) : AutoCloseable {
+ init {
+ InputFeatureFlagProvider.setKeyboardBacklightAnimationEnabled(animationEnabled)
+ InputFeatureFlagProvider.setKeyboardBacklightCustomLevelsEnabled(customLevelsEnabled)
+ InputFeatureFlagProvider
+ .setAmbientKeyboardBacklightControlEnabled(ambientControlEnabled)
+ }
+
+ override fun close() {
+ InputFeatureFlagProvider.clearOverrides()
+ }
+ }
+
+ private inner class FakeAnimatorFactory : KeyboardBacklightController.AnimatorFactory {
+ override fun makeIntAnimator(from: Int, to: Int): ValueAnimator {
+ lastAnimationValues[0] = from
+ lastAnimationValues[1] = to
+ return ValueAnimator.ofInt(from, to)
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
new file mode 100644
index 000000000000..44de6a6ecbc3
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (C) 2023 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.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.hardware.input.IInputManager
+import android.hardware.input.InputManager
+import android.hardware.input.InputManagerGlobal
+import android.hardware.input.KeyboardLayout
+import android.icu.util.ULocale
+import android.os.Bundle
+import android.os.test.TestLooper
+import android.platform.test.annotations.Presubmit
+import android.provider.Settings
+import android.util.proto.ProtoOutputStream
+import android.view.InputDevice
+import android.view.inputmethod.InputMethodInfo
+import android.view.inputmethod.InputMethodSubtype
+import androidx.test.core.R
+import androidx.test.core.app.ApplicationProvider
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.os.KeyboardConfiguredProto
+import com.android.internal.util.FrameworkStatsLog
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+private fun createKeyboard(
+ deviceId: Int,
+ vendorId: Int,
+ productId: Int,
+ languageTag: String,
+ layoutType: String
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .setVendorId(vendorId)
+ .setProductId(productId)
+ .setKeyboardLanguageTag(languageTag)
+ .setKeyboardLayoutType(layoutType)
+ .build()
+
+/**
+ * Tests for {@link Default UI} and {@link New UI}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardLayoutManagerTests
+ */
+@Presubmit
+class KeyboardLayoutManagerTests {
+ companion object {
+ const val DEVICE_ID = 1
+ const val VENDOR_SPECIFIC_DEVICE_ID = 2
+ const val ENGLISH_DVORAK_DEVICE_ID = 3
+ const val ENGLISH_QWERTY_DEVICE_ID = 4
+ const val DEFAULT_VENDOR_ID = 123
+ const val DEFAULT_PRODUCT_ID = 456
+ const val USER_ID = 4
+ const val IME_ID = "ime_id"
+ const val PACKAGE_NAME = "KeyboardLayoutManagerTests"
+ const val RECEIVER_NAME = "DummyReceiver"
+ private const val ENGLISH_US_LAYOUT_NAME = "keyboard_layout_english_us"
+ private const val ENGLISH_UK_LAYOUT_NAME = "keyboard_layout_english_uk"
+ private const val GERMAN_LAYOUT_NAME = "keyboard_layout_german"
+ private const val VENDOR_SPECIFIC_LAYOUT_NAME = "keyboard_layout_vendorId:1,productId:1"
+ const val LAYOUT_TYPE_QWERTZ = 2
+ const val LAYOUT_TYPE_QWERTY = 1
+ const val LAYOUT_TYPE_DEFAULT = 0
+ }
+
+ private val ENGLISH_US_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_US_LAYOUT_NAME)
+ private val ENGLISH_UK_LAYOUT_DESCRIPTOR = createLayoutDescriptor(ENGLISH_UK_LAYOUT_NAME)
+ private val GERMAN_LAYOUT_DESCRIPTOR = createLayoutDescriptor(GERMAN_LAYOUT_NAME)
+ private val VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR =
+ createLayoutDescriptor(VENDOR_SPECIFIC_LAYOUT_NAME)
+
+ @JvmField
+ @Rule
+ val extendedMockitoRule = ExtendedMockitoRule.Builder(this)
+ .mockStatic(FrameworkStatsLog::class.java).build()!!
+
+ @Mock
+ private lateinit var iInputManager: IInputManager
+
+ @Mock
+ private lateinit var native: NativeInputManagerService
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+ private lateinit var keyboardLayoutManager: KeyboardLayoutManager
+
+ private lateinit var imeInfo: InputMethodInfo
+ private var nextImeSubtypeId = 0
+ private lateinit var context: Context
+ private lateinit var dataStore: PersistentDataStore
+ private lateinit var testLooper: TestLooper
+ private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
+
+ // Devices
+ private lateinit var keyboardDevice: InputDevice
+ private lateinit var vendorSpecificKeyboardDevice: InputDevice
+ private lateinit var englishDvorakKeyboardDevice: InputDevice
+ private lateinit var englishQwertyKeyboardDevice: InputDevice
+
+ @Before
+ fun setup() {
+ context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ dataStore = PersistentDataStore(object : PersistentDataStore.Injector() {
+ override fun openRead(): InputStream? {
+ throw FileNotFoundException()
+ }
+
+ override fun startWrite(): FileOutputStream? {
+ throw IOException()
+ }
+
+ override fun finishWrite(fos: FileOutputStream?, success: Boolean) {}
+ })
+ testLooper = TestLooper()
+ keyboardLayoutManager = Mockito.spy(
+ KeyboardLayoutManager(context, native, dataStore, testLooper.looper)
+ )
+ setupInputDevices()
+ setupBroadcastReceiver()
+ setupIme()
+ }
+
+ @After
+ fun tearDown() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ }
+
+ private fun setupInputDevices() {
+ val inputManager = InputManager(context)
+ Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+
+ keyboardDevice = createKeyboard(DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, "", "")
+ vendorSpecificKeyboardDevice = createKeyboard(VENDOR_SPECIFIC_DEVICE_ID, 1, 1, "", "")
+ englishDvorakKeyboardDevice = createKeyboard(ENGLISH_DVORAK_DEVICE_ID, DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID, "en", "dvorak")
+ englishQwertyKeyboardDevice = createKeyboard(ENGLISH_QWERTY_DEVICE_ID, DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID, "en", "qwerty")
+ Mockito.`when`(iInputManager.inputDeviceIds)
+ .thenReturn(intArrayOf(
+ DEVICE_ID,
+ VENDOR_SPECIFIC_DEVICE_ID,
+ ENGLISH_DVORAK_DEVICE_ID,
+ ENGLISH_QWERTY_DEVICE_ID
+ ))
+ Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
+ Mockito.`when`(iInputManager.getInputDevice(VENDOR_SPECIFIC_DEVICE_ID))
+ .thenReturn(vendorSpecificKeyboardDevice)
+ Mockito.`when`(iInputManager.getInputDevice(ENGLISH_DVORAK_DEVICE_ID))
+ .thenReturn(englishDvorakKeyboardDevice)
+ Mockito.`when`(iInputManager.getInputDevice(ENGLISH_QWERTY_DEVICE_ID))
+ .thenReturn(englishQwertyKeyboardDevice)
+ }
+
+ private fun setupBroadcastReceiver() {
+ Mockito.`when`(context.packageManager).thenReturn(packageManager)
+
+ val info = createMockReceiver()
+ Mockito.`when`(packageManager.queryBroadcastReceiversAsUser(Mockito.any(), Mockito.anyInt(),
+ Mockito.anyInt())).thenReturn(listOf(info))
+ Mockito.`when`(packageManager.getReceiverInfo(Mockito.any(), Mockito.anyInt()))
+ .thenReturn(info.activityInfo)
+
+ val resources = context.resources
+ Mockito.`when`(
+ packageManager.getResourcesForApplication(
+ Mockito.any(
+ ApplicationInfo::class.java
+ )
+ )
+ ).thenReturn(resources)
+ }
+
+ private fun setupIme() {
+ imeInfo = InputMethodInfo(PACKAGE_NAME, RECEIVER_NAME, "", "", 0)
+ }
+
+ @Test
+ fun testDefaultUi_getKeyboardLayouts() {
+ NewSettingsApiFlag(false).use {
+ val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+ assertNotEquals(
+ "Default UI: Keyboard layout API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "Default UI: Keyboard layout API should provide English(US) layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getKeyboardLayouts() {
+ NewSettingsApiFlag(true).use {
+ val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+ assertNotEquals(
+ "New UI: Keyboard layout API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "New UI: Keyboard layout API should provide English(US) layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getKeyboardLayoutsForInputDevice() {
+ NewSettingsApiFlag(false).use {
+ val keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutsForInputDevice(keyboardDevice.identifier)
+ assertNotEquals(
+ "Default UI: getKeyboardLayoutsForInputDevice API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "Default UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
+ "layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+
+ val vendorSpecificKeyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutsForInputDevice(
+ vendorSpecificKeyboardDevice.identifier
+ )
+ assertEquals(
+ "Default UI: getKeyboardLayoutsForInputDevice API should return only vendor " +
+ "specific layout",
+ 1,
+ vendorSpecificKeyboardLayouts.size
+ )
+ assertEquals(
+ "Default UI: getKeyboardLayoutsForInputDevice API should return vendor specific " +
+ "layout",
+ VENDOR_SPECIFIC_LAYOUT_DESCRIPTOR,
+ vendorSpecificKeyboardLayouts[0].descriptor
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getKeyboardLayoutsForInputDevice() {
+ NewSettingsApiFlag(true).use {
+ val keyboardLayouts = keyboardLayoutManager.keyboardLayouts
+ assertNotEquals(
+ "New UI: getKeyboardLayoutsForInputDevice API should not return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue(
+ "New UI: getKeyboardLayoutsForInputDevice API should provide English(US) " +
+ "layout",
+ hasLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getSetCurrentKeyboardLayoutForInputDevice() {
+ NewSettingsApiFlag(false).use {
+ assertNull(
+ "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null if " +
+ "nothing was set",
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+
+ keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ val keyboardLayout =
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ assertEquals(
+ "Default UI: getCurrentKeyboardLayoutForInputDevice API should return the set " +
+ "layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayout
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getSetCurrentKeyboardLayoutForInputDevice() {
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.setCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertNull(
+ "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null " +
+ "even after setCurrentKeyboardLayoutForInputDevice",
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getEnabledKeyboardLayoutsForInputDevice() {
+ NewSettingsApiFlag(false).use {
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+
+ val keyboardLayouts =
+ keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
+ keyboardDevice.identifier
+ )
+ assertEquals(
+ "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return added " +
+ "layout",
+ 1,
+ keyboardLayouts.size
+ )
+ assertEquals(
+ "Default UI: getEnabledKeyboardLayoutsForInputDevice API should return " +
+ "English(US) layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayouts[0]
+ )
+ assertEquals(
+ "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
+ "English(US) layout (Auto select the first enabled layout)",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+
+ keyboardLayoutManager.removeKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertEquals(
+ "Default UI: getKeyboardLayoutsForInputDevice API should return 0 layouts",
+ 0,
+ keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
+ keyboardDevice.identifier
+ ).size
+ )
+ assertNull(
+ "Default UI: getCurrentKeyboardLayoutForInputDevice API should return null after " +
+ "the enabled layout is removed",
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getEnabledKeyboardLayoutsForInputDevice() {
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+
+ assertEquals(
+ "New UI: getEnabledKeyboardLayoutsForInputDevice API should return always return " +
+ "an empty array",
+ 0,
+ keyboardLayoutManager.getEnabledKeyboardLayoutsForInputDevice(
+ keyboardDevice.identifier
+ ).size
+ )
+ assertNull(
+ "New UI: getCurrentKeyboardLayoutForInputDevice API should always return null",
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_switchKeyboardLayout() {
+ NewSettingsApiFlag(false).use {
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertEquals(
+ "Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
+ "English(US) layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+
+ keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1)
+
+ // Throws null pointer because trying to show toast using TestLooper
+ assertThrows(NullPointerException::class.java) { testLooper.dispatchAll() }
+ assertEquals("Default UI: getCurrentKeyboardLayoutForInputDevice API should return " +
+ "English(UK) layout",
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_switchKeyboardLayout() {
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ keyboardLayoutManager.addKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+
+ keyboardLayoutManager.switchKeyboardLayout(DEVICE_ID, 1)
+ testLooper.dispatchAll()
+
+ assertNull("New UI: getCurrentKeyboardLayoutForInputDevice API should always return " +
+ "null",
+ keyboardLayoutManager.getCurrentKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getKeyboardLayout() {
+ NewSettingsApiFlag(false).use {
+ val keyboardLayout =
+ keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
+ assertEquals("Default UI: getKeyboardLayout API should return correct Layout from " +
+ "available layouts",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayout!!.descriptor
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getKeyboardLayout() {
+ NewSettingsApiFlag(true).use {
+ val keyboardLayout =
+ keyboardLayoutManager.getKeyboardLayout(ENGLISH_US_LAYOUT_DESCRIPTOR)
+ assertEquals("New UI: getKeyboardLayout API should return correct Layout from " +
+ "available layouts",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayout!!.descriptor
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getSetKeyboardLayoutForInputDevice_WithImeInfo() {
+ NewSettingsApiFlag(false).use {
+ val imeSubtype = createImeSubtype()
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ val keyboardLayout =
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ )
+ assertNull(
+ "Default UI: getKeyboardLayoutForInputDevice API should always return null",
+ keyboardLayout
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getSetKeyboardLayoutForInputDevice_withImeInfo() {
+ NewSettingsApiFlag(true).use {
+ val imeSubtype = createImeSubtype()
+
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the set layout",
+ ENGLISH_UK_LAYOUT_DESCRIPTOR,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ )
+ )
+
+ // This should replace previously set layout
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertEquals(
+ "New UI: getKeyboardLayoutForInputDevice API should return the last set layout",
+ ENGLISH_US_LAYOUT_DESCRIPTOR,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testDefaultUi_getKeyboardLayoutListForInputDevice() {
+ NewSettingsApiFlag(false).use {
+ val keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtype()
+ )
+ assertEquals("Default UI: getKeyboardLayoutListForInputDevice API should always " +
+ "return empty array",
+ 0,
+ keyboardLayouts.size
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getKeyboardLayoutListForInputDevice() {
+ NewSettingsApiFlag(true).use {
+ // Check Layouts for "hi-Latn". It should return all 'Latn' keyboard layouts
+ var keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("hi-Latn")
+ )
+ assertNotEquals(
+ "New UI: getKeyboardLayoutListForInputDevice API should return the list of " +
+ "supported layouts with matching script code",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(US) layout for hi-Latn",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(No script code) layout for hi-Latn",
+ containsLayout(
+ keyboardLayouts,
+ createLayoutDescriptor("keyboard_layout_english_without_script_code")
+ )
+ )
+
+ // Check Layouts for "hi" which by default uses 'Deva' script.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("hi")
+ )
+ assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return empty " +
+ "list if no supported layouts available",
+ 0,
+ keyboardLayouts.size
+ )
+
+ // If user manually selected some layout, always provide it in the layout list
+ val imeSubtype = createImeSubtypeForLanguageTag("hi")
+ keyboardLayoutManager.setKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, imeSubtype,
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ imeSubtype
+ )
+ assertEquals("New UI: getKeyboardLayoutListForInputDevice API should return user " +
+ "selected layout even if the script is incompatible with IME",
+ 1,
+ keyboardLayouts.size
+ )
+
+ // Special case Japanese: UScript ignores provided script code for certain language tags
+ // Should manually match provided script codes and then rely on Uscript to derive
+ // script from language tags and match those.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("ja-Latn-JP")
+ )
+ assertNotEquals(
+ "New UI: getKeyboardLayoutListForInputDevice API should return the list of " +
+ "supported layouts with matching script code for ja-Latn-JP",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(US) layout for ja-Latn-JP",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should return a list " +
+ "containing English(No script code) layout for ja-Latn-JP",
+ containsLayout(
+ keyboardLayouts,
+ createLayoutDescriptor("keyboard_layout_english_without_script_code")
+ )
+ )
+
+ // If script code not explicitly provided for Japanese should rely on Uscript to find
+ // derived script code and hence no suitable layout will be found.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("ja-JP")
+ )
+ assertEquals(
+ "New UI: getKeyboardLayoutListForInputDevice API should return empty list of " +
+ "supported layouts with matching script code for ja-JP",
+ 0,
+ keyboardLayouts.size
+ )
+
+ // If IME doesn't have a corresponding language tag, then should show all available
+ // layouts no matter the script code.
+ keyboardLayouts =
+ keyboardLayoutManager.getKeyboardLayoutListForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo, null
+ )
+ assertNotEquals(
+ "New UI: getKeyboardLayoutListForInputDevice API should return all layouts if" +
+ "language tag or subtype not provided",
+ 0,
+ keyboardLayouts.size
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Latin " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(keyboardLayouts, ENGLISH_US_LAYOUT_DESCRIPTOR)
+ )
+ assertTrue("New UI: getKeyboardLayoutListForInputDevice API should contain Cyrillic " +
+ "layouts if language tag or subtype not provided",
+ containsLayout(
+ keyboardLayouts,
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTag() {
+ NewSettingsApiFlag(true).use {
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("en-US"),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("en-GB"),
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("de"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("fr-FR"),
+ createLayoutDescriptor("keyboard_layout_french")
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTag("ru"),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ assertNull(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
+ "layout available",
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("it")
+ )
+ )
+ assertNull(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return null when no " +
+ "layout for script code is available",
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTag("en-Deva")
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withImeLanguageTagAndLayoutType() {
+ NewSettingsApiFlag(true).use {
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", "qwerty"),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", "dvorak"),
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
+ // Try to match layout type even if country doesn't match
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-GB", "dvorak"),
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
+ // Choose layout based on layout type priority, if layout type is not provided by IME
+ // (Qwerty > Dvorak > Extended)
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-US", ""),
+ ENGLISH_US_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("en-GB", "qwerty"),
+ ENGLISH_UK_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("de", "qwertz"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ // Wrong layout type should match with language if provided layout type not available
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("de", "qwerty"),
+ GERMAN_LAYOUT_DESCRIPTOR
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("fr-FR", "azerty"),
+ createLayoutDescriptor("keyboard_layout_french")
+ )
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", "qwerty"),
+ createLayoutDescriptor("keyboard_layout_russian_qwerty")
+ )
+ // If layout type is empty then prioritize KCM with empty layout type
+ assertCorrectLayout(
+ keyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ assertNull("New UI: getDefaultKeyboardLayoutForInputDevice should return null when " +
+ "no layout for script code is available",
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ keyboardDevice.identifier, USER_ID, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("en-Deva-US", "")
+ )
+ )
+ }
+ }
+
+ @Test
+ fun testNewUi_getDefaultKeyboardLayoutForInputDevice_withHwLanguageTagAndLayoutType() {
+ NewSettingsApiFlag(true).use {
+ val frenchSubtype = createImeSubtypeForLanguageTagAndLayoutType("fr", "azerty")
+ // Should return English dvorak even if IME current layout is French, since HW says the
+ // keyboard is a Dvorak keyboard
+ assertCorrectLayout(
+ englishDvorakKeyboardDevice,
+ frenchSubtype,
+ createLayoutDescriptor("keyboard_layout_english_us_dvorak")
+ )
+
+ // Back to back changing HW keyboards with same product and vendor ID but different
+ // language and layout type should configure the layouts correctly.
+ assertCorrectLayout(
+ englishQwertyKeyboardDevice,
+ frenchSubtype,
+ createLayoutDescriptor("keyboard_layout_english_us")
+ )
+
+ // Fallback to IME information if the HW provided layout script is incompatible with the
+ // provided IME subtype
+ assertCorrectLayout(
+ englishDvorakKeyboardDevice,
+ createImeSubtypeForLanguageTagAndLayoutType("ru", ""),
+ createLayoutDescriptor("keyboard_layout_russian")
+ )
+ }
+ }
+
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_VirtualKeyboardBasedSelection() {
+ val imeInfos = listOf(
+ KeyboardLayoutManager.ImeInfo(0, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ GERMAN_LAYOUT_NAME,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_DeviceBasedSelection() {
+ val imeInfos = listOf(
+ KeyboardLayoutManager.ImeInfo(0, imeInfo,
+ createImeSubtypeForLanguageTagAndLayoutType("de-Latn", "qwertz")))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(englishQwertyKeyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.vendorId),
+ ArgumentMatchers.eq(englishQwertyKeyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ "en",
+ LAYOUT_TYPE_QWERTY,
+ ENGLISH_US_LAYOUT_NAME,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ "de-Latn",
+ LAYOUT_TYPE_QWERTZ))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationLogged_onInputDeviceAdded_DefaultSelection() {
+ val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceAdded(keyboardDevice.id)
+ ExtendedMockito.verify {
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.eq(keyboardDevice.vendorId),
+ ArgumentMatchers.eq(keyboardDevice.productId),
+ ArgumentMatchers.eq(createByteArray(
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT,
+ "Default",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEFAULT,
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ LAYOUT_TYPE_DEFAULT))
+ )
+ }
+ }
+ }
+
+ @Test
+ fun testConfigurationNotLogged_onInputDeviceChanged() {
+ val imeInfos = listOf(KeyboardLayoutManager.ImeInfo(0, imeInfo, createImeSubtype()))
+ Mockito.doReturn(imeInfos).`when`(keyboardLayoutManager).imeInfoListForLayoutMapping
+ NewSettingsApiFlag(true).use {
+ keyboardLayoutManager.onInputDeviceChanged(keyboardDevice.id)
+ ExtendedMockito.verify({
+ FrameworkStatsLog.write(
+ ArgumentMatchers.eq(FrameworkStatsLog.KEYBOARD_CONFIGURED),
+ ArgumentMatchers.anyBoolean(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.anyInt(),
+ ArgumentMatchers.any(ByteArray::class.java)
+ )
+ }, Mockito.times(0))
+ }
+ }
+
+ private fun assertCorrectLayout(
+ device: InputDevice,
+ imeSubtype: InputMethodSubtype,
+ expectedLayout: String
+ ) {
+ assertEquals(
+ "New UI: getDefaultKeyboardLayoutForInputDevice should return $expectedLayout",
+ expectedLayout,
+ keyboardLayoutManager.getKeyboardLayoutForInputDevice(
+ device.identifier, USER_ID, imeInfo, imeSubtype
+ )
+ )
+ }
+
+ private fun createImeSubtype(): InputMethodSubtype =
+ createImeSubtypeForLanguageTagAndLayoutType(null, null)
+
+ private fun createImeSubtypeForLanguageTag(languageTag: String): InputMethodSubtype =
+ createImeSubtypeForLanguageTagAndLayoutType(languageTag, null)
+
+ private fun createImeSubtypeForLanguageTagAndLayoutType(
+ languageTag: String?,
+ layoutType: String?
+ ): InputMethodSubtype {
+ val builder = InputMethodSubtype.InputMethodSubtypeBuilder()
+ .setSubtypeId(nextImeSubtypeId++)
+ .setIsAuxiliary(false)
+ .setSubtypeMode("keyboard")
+ if (languageTag != null && layoutType != null) {
+ builder.setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType)
+ } else if (languageTag != null) {
+ builder.setLanguageTag(languageTag)
+ }
+ return builder.build()
+ }
+
+ private fun createByteArray(
+ expectedLanguageTag: String, expectedLayoutType: Int, expectedLayoutName: String,
+ expectedCriteria: Int, expectedImeLanguageTag: String, expectedImeLayoutType: Int): ByteArray {
+ val proto = ProtoOutputStream()
+ val keyboardLayoutConfigToken = proto.start(
+ KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG)
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG,
+ expectedLanguageTag
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE,
+ expectedLayoutType
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME,
+ expectedLayoutName
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA,
+ expectedCriteria
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LANGUAGE_TAG,
+ expectedImeLanguageTag
+ )
+ proto.write(
+ KeyboardConfiguredProto.KeyboardLayoutConfig.IME_LAYOUT_TYPE,
+ expectedImeLayoutType
+ )
+ proto.end(keyboardLayoutConfigToken);
+ return proto.bytes
+ }
+
+ private fun hasLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
+ for (kl in layoutList) {
+ if (kl.descriptor == layoutDesc) {
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun createLayoutDescriptor(keyboardName: String): String =
+ "$PACKAGE_NAME/$RECEIVER_NAME/$keyboardName"
+
+ private fun containsLayout(layoutList: Array<KeyboardLayout>, layoutDesc: String): Boolean {
+ for (kl in layoutList) {
+ if (kl.descriptor.equals(layoutDesc)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun createMockReceiver(): ResolveInfo {
+ val info = ResolveInfo()
+ info.activityInfo = ActivityInfo()
+ info.activityInfo.packageName = PACKAGE_NAME
+ info.activityInfo.name = RECEIVER_NAME
+ info.activityInfo.applicationInfo = ApplicationInfo()
+ info.activityInfo.metaData = Bundle()
+ info.activityInfo.metaData.putInt(
+ InputManager.META_DATA_KEYBOARD_LAYOUTS,
+ R.xml.keyboard_layouts
+ )
+ info.serviceInfo = ServiceInfo()
+ info.serviceInfo.packageName = PACKAGE_NAME
+ info.serviceInfo.name = RECEIVER_NAME
+ return info
+ }
+
+ private inner class NewSettingsApiFlag constructor(enabled: Boolean) : AutoCloseable {
+ init {
+ Settings.Global.putString(
+ context.contentResolver,
+ "settings_new_keyboard_ui", enabled.toString()
+ )
+ }
+
+ override fun close() {
+ Settings.Global.putString(
+ context.contentResolver,
+ "settings_new_keyboard_ui",
+ ""
+ )
+ }
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
new file mode 100644
index 000000000000..33ff09b55534
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2023 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.hardware.input.KeyboardLayout
+import android.icu.util.ULocale
+import android.platform.test.annotations.Presubmit
+import android.view.InputDevice
+import android.view.inputmethod.InputMethodSubtype
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+private fun createKeyboard(
+ deviceId: Int,
+ vendorId: Int,
+ productId: Int,
+ languageTag: String?,
+ layoutType: String?
+): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setSources(InputDevice.SOURCE_KEYBOARD)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC)
+ .setExternal(true)
+ .setVendorId(vendorId)
+ .setProductId(productId)
+ .setKeyboardLanguageTag(languageTag)
+ .setKeyboardLayoutType(layoutType)
+ .build()
+
+private fun createImeSubtype(
+ imeSubtypeId: Int,
+ languageTag: ULocale?,
+ layoutType: String
+): InputMethodSubtype =
+ InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId)
+ .setPhysicalKeyboardHint(languageTag, layoutType).build()
+
+/**
+ * Tests for {@link KeyboardMetricsCollector}.
+ *
+ * Build/Install/Run:
+ * atest InputTests:KeyboardMetricsCollectorTests
+ */
+@Presubmit
+class KeyboardMetricsCollectorTests {
+
+ companion object {
+ const val DEVICE_ID = 1
+ const val DEFAULT_VENDOR_ID = 123
+ const val DEFAULT_PRODUCT_ID = 456
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() {
+ assertThrows(IllegalStateException::class.java) {
+ KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ null,
+ null
+ )
+ ).addLayoutSelection(createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
+ null, 123).build()
+ }
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() {
+ val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ "de-CH",
+ "qwertz"
+ )
+ )
+ val event = builder.addLayoutSelection(
+ createImeSubtype(1, ULocale.forLanguageTag("en-US"), "qwerty"),
+ "English(US)(Qwerty)",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD
+ ).addLayoutSelection(
+ createImeSubtype(2, ULocale.forLanguageTag("en-US"), "azerty"),
+ null, // Default layout type
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER
+ ).addLayoutSelection(
+ createImeSubtype(3, ULocale.forLanguageTag("en-US"), "qwerty"),
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ ).setIsFirstTimeConfiguration(true).build()
+
+ assertEquals(
+ "KeyboardConfigurationEvent should pick vendor ID from provided InputDevice",
+ DEFAULT_VENDOR_ID,
+ event.vendorId
+ )
+ assertEquals(
+ "KeyboardConfigurationEvent should pick product ID from provided InputDevice",
+ DEFAULT_PRODUCT_ID,
+ event.productId
+ )
+ assertTrue(event.isFirstConfiguration)
+
+ assertEquals(
+ "KeyboardConfigurationEvent should contain 3 configurations provided",
+ 3,
+ event.layoutConfigurations.size
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[0],
+ "de-CH",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
+ "English(US)(Qwerty)",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[1],
+ "de-CH",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
+ KeyboardMetricsCollector.DEFAULT_LAYOUT_NAME,
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER,
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
+ )
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[2],
+ "de-CH",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"),
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ "en-US",
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
+ )
+ }
+
+ @Test
+ fun testCreateKeyboardConfigurationEvent_withDefaultLanguageTag() {
+ val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder(
+ createKeyboard(
+ DEVICE_ID,
+ DEFAULT_VENDOR_ID,
+ DEFAULT_PRODUCT_ID,
+ "und", // Undefined language tag
+ "azerty"
+ )
+ )
+ val event = builder.addLayoutSelection(
+ createImeSubtype(4, null, "qwerty"), // Default language tag
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE
+ ).build()
+
+ assertExpectedLayoutConfiguration(
+ event.layoutConfigurations[0],
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"),
+ "German",
+ KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE,
+ KeyboardMetricsCollector.DEFAULT_LANGUAGE_TAG,
+ KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"),
+ )
+ }
+
+ private fun assertExpectedLayoutConfiguration(
+ configuration: KeyboardMetricsCollector.LayoutConfiguration,
+ expectedKeyboardLanguageTag: String,
+ expectedKeyboardLayoutType: Int,
+ expectedSelectedLayout: String,
+ expectedLayoutSelectionCriteria: Int,
+ expectedImeLanguageTag: String,
+ expectedImeLayoutType: Int
+ ) {
+ assertEquals(expectedKeyboardLanguageTag, configuration.keyboardLanguageTag)
+ assertEquals(expectedKeyboardLayoutType, configuration.keyboardLayoutType)
+ assertEquals(expectedSelectedLayout, configuration.keyboardLayoutName)
+ assertEquals(expectedLayoutSelectionCriteria, configuration.layoutSelectionCriteria)
+ assertEquals(expectedImeLanguageTag, configuration.imeLanguageTag)
+ assertEquals(expectedImeLayoutType, configuration.imeLayoutType)
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
new file mode 100644
index 000000000000..ae7fb3b29f6c
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/FocusEventDebugViewTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2023 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.debug;
+
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyLong;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.input.InputManagerService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest FocusEventDebugViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class FocusEventDebugViewTest {
+
+ private FocusEventDebugView mFocusEventDebugView;
+ private RotaryInputValueView mRotaryInputValueView;
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ InputManagerService mockService = mock(InputManagerService.class);
+ when(mockService.monitorInput(anyString(), anyInt()))
+ .thenReturn(InputChannel.openInputChannelPair("FocusEventDebugViewTest")[1]);
+
+ mRotaryInputValueView = spy(new RotaryInputValueView(context));
+ mRotaryInputGraphView = spy(new RotaryInputGraphView(context));
+ mFocusEventDebugView = new FocusEventDebugView(context, mockService,
+ () -> mRotaryInputValueView, () -> mRotaryInputGraphView);
+ }
+
+ @Test
+ public void handleRotaryInput_sendsMotionEventWhenEnabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(true);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView).addValue(0.5f, 10L);
+ verify(mRotaryInputValueView).updateValue(0.5f);
+ }
+
+ @Test
+ public void handleRotaryInput_doesNotSendMotionEventWhenDisabled() {
+ mFocusEventDebugView.handleUpdateShowRotaryInput(false);
+
+ mFocusEventDebugView.handleRotaryInput(createRotaryMotionEvent(0.5f, 10L));
+
+ verify(mRotaryInputGraphView, never()).addValue(anyFloat(), anyLong());
+ verify(mRotaryInputValueView, never()).updateValue(anyFloat());
+ }
+
+ private MotionEvent createRotaryMotionEvent(float scrollAxisValue, long eventTime) {
+ PointerCoords pointerCoords = new PointerCoords();
+ pointerCoords.setAxisValue(MotionEvent.AXIS_SCROLL, scrollAxisValue);
+ PointerProperties pointerProperties = new PointerProperties();
+
+ return MotionEvent.obtain(
+ /* downTime */ 0,
+ /* eventTime */ eventTime,
+ /* action */ MotionEvent.ACTION_SCROLL,
+ /* pointerCount */ 1,
+ /* pointerProperties */ new PointerProperties[] {pointerProperties},
+ /* pointerCoords */ new PointerCoords[] {pointerCoords},
+ /* metaState */ 0,
+ /* buttonState */ 0,
+ /* xPrecision */ 0,
+ /* yPrecision */ 0,
+ /* deviceId */ 0,
+ /* edgeFlags */ 0,
+ /* source */ InputDevice.SOURCE_ROTARY_ENCODER,
+ /* flags */ 0
+ );
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
new file mode 100644
index 000000000000..af6ece414fd1
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputGraphViewTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 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.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputGraphViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputGraphViewTest {
+
+ private RotaryInputGraphView mRotaryInputGraphView;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+
+ mRotaryInputGraphView = new RotaryInputGraphView(context);
+ }
+
+ @Test
+ public void startsWithDefaultFrameCenter() {
+ assertEquals(0, mRotaryInputGraphView.getFrameCenterPosition(), 0.01);
+ }
+
+ @Test
+ public void addValue_translatesRotaryInputGraphViewWithHighScrollValue() {
+ final float scrollAxisValue = 1000f;
+ final long eventTime = 0;
+
+ mRotaryInputGraphView.addValue(scrollAxisValue, eventTime);
+
+ assertTrue(mRotaryInputGraphView.getFrameCenterPosition() > 0);
+ }
+}
diff --git a/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
new file mode 100644
index 000000000000..e5e3852dc318
--- /dev/null
+++ b/tests/Input/src/com/android/server/input/debug/RotaryInputValueViewTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2023 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.debug;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+/**
+ * Build/Install/Run:
+ * atest RotaryInputValueViewTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RotaryInputValueViewTest {
+
+ private final Locale mDefaultLocale = Locale.getDefault();
+
+ private RotaryInputValueView mRotaryInputValueView;
+ private float mScaledVerticalScrollFactor;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ mScaledVerticalScrollFactor =
+ ViewConfiguration.get(context).getScaledVerticalScrollFactor();
+
+ mRotaryInputValueView = new RotaryInputValueView(context);
+ }
+
+ @Test
+ public void startsWithDefaultValue() {
+ assertEquals("+0.0", mRotaryInputValueView.getText().toString());
+ }
+
+ @Test
+ public void updateValue_updatesTextWithScrollValue() {
+ final float scrollAxisValue = 1000f;
+ final String expectedText = String.format(mDefaultLocale, "+%.1f",
+ scrollAxisValue * mScaledVerticalScrollFactor);
+
+ mRotaryInputValueView.updateValue(scrollAxisValue);
+
+ assertEquals(expectedText, mRotaryInputValueView.getText().toString());
+ }
+
+ @Test
+ public void updateActivityStatus_setsAndRemovesColorFilter() {
+ // It should not be active initially.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(true);
+ // It should be active after rotary input.
+ assertNotNull(mRotaryInputValueView.getBackground().getColorFilter());
+
+ mRotaryInputValueView.updateActivityStatus(false);
+ // It should not be active after waiting for mUpdateActivityStatusCallback.
+ assertNull(mRotaryInputValueView.getBackground().getColorFilter());
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 7bc5df52e316..4893d14ad79b 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -100,6 +100,7 @@ class AnrTest {
private fun clickCloseAppOnAnrDialog() {
// Find anr dialog and kill app
+ val timestamp = System.currentTimeMillis()
val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val closeAppButton: UiObject2? =
uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
@@ -107,7 +108,6 @@ class AnrTest {
fail("Could not find anr dialog")
return
}
- val initialReasons = getExitReasons()
closeAppButton.click()
/**
* We must wait for the app to be fully closed before exiting this test. This is because
@@ -116,7 +116,7 @@ class AnrTest {
* the killing logic will apply to the newly launched 'am start' instance, and the second
* test will fail because the unresponsive activity will never be launched.
*/
- waitForNewExitReason(initialReasons[0].timestamp)
+ waitForNewExitReasonAfter(timestamp)
}
private fun clickWaitOnAnrDialog() {
@@ -140,20 +140,20 @@ class AnrTest {
return infos
}
- private fun waitForNewExitReason(previousExitTimestamp: Long) {
+ private fun waitForNewExitReasonAfter(timestamp: Long) {
PollingCheck.waitFor {
- getExitReasons()[0].timestamp > previousExitTimestamp
+ val reasons = getExitReasons()
+ !reasons.isEmpty() && reasons[0].timestamp >= timestamp
}
val reasons = getExitReasons()
- assertTrue(reasons[0].timestamp > previousExitTimestamp)
+ assertTrue(reasons[0].timestamp > timestamp)
assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
}
private fun triggerAnr() {
startUnresponsiveActivity()
val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
- val obj: UiObject2? = uiDevice.wait(Until.findObject(
- By.text("Unresponsive gesture monitor")), 10000)
+ val obj: UiObject2? = uiDevice.wait(Until.findObject(By.pkg(PACKAGE_NAME)), 10000)
if (obj == null) {
fail("Could not find unresponsive activity")
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index 37b67f4c183c..075cf0cc5a45 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -113,13 +113,11 @@ class InputEventSenderAndReceiverTest {
val sent = SpyInputEventSender.Timeline(
inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
- val received = mSender.getTimeline()
- assertEquals(null, received)
+ mSender.assertNoEvents()
// Sender will no longer receive callbacks for this fd, even if receiver sends a valid
// timeline later
mReceiver.reportTimeline(2 /*inputEventId*/, 3 /*gpuCompletedTime*/, 4 /*presentTime*/)
- val receivedSecondTimeline = mSender.getTimeline()
- assertEquals(null, receivedSecondTimeline)
+ mSender.assertNoEvents()
}
/**
diff --git a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
index 1099878a1954..f311bc222d22 100644
--- a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
+++ b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
@@ -25,7 +25,6 @@ import android.view.WindowManagerPolicyConstants.PointerEventListener
import com.android.server.UiThread
import com.android.server.wm.PointerEventDispatcher
import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNull
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -86,8 +85,7 @@ class PointerEventDispatcherTest {
// Since the listener raises an exception during the event handling, the event should be
// marked as 'not handled'.
assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = false), finishedSignal)
- // Ensure that there aren't double finish calls. This would crash if there's a call
- // to finish twice.
- assertNull(mSender.getFinishedSignal())
+ // Ensure that there aren't double finish calls.
+ mSender.assertNoEvents()
}
}
diff --git a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
index 2d9af9a65d33..5cbfce534b15 100644
--- a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
+++ b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
@@ -27,10 +27,17 @@ import android.view.MotionEvent
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
+import org.junit.Assert.assertNull
+
private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T? {
return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
}
+private fun <T> assertNoEvents(queue: LinkedBlockingQueue<T>) {
+ // Poll the queue with a shorter timeout, to make the check faster.
+ assertNull(queue.poll(100L, TimeUnit.MILLISECONDS))
+}
+
class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
InputEventReceiver(channel, looper) {
private val mInputEvents = LinkedBlockingQueue<InputEvent>()
@@ -72,4 +79,9 @@ class SpyInputEventSender(channel: InputChannel, looper: Looper) :
fun getTimeline(): Timeline? {
return getEvent(mTimelines)
}
+
+ fun assertNoEvents() {
+ assertNoEvents(mFinishedSignals)
+ assertNoEvents(mTimelines)
+ }
}
diff --git a/tests/InputMethodStressTest/TEST_MAPPING b/tests/InputMethodStressTest/TEST_MAPPING
index 06e2ce84eb0f..d982dd817eaa 100644
--- a/tests/InputMethodStressTest/TEST_MAPPING
+++ b/tests/InputMethodStressTest/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit-large": [
+ "postsubmit": [
{
"name": "InputMethodStressTest"
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index f4a04a163ebb..b6b99242c414 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -97,6 +97,8 @@ public final class NotificationTest {
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
// Do not run on TV. Direct Reply isn't supported on TV.
assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ // Do not run on Wear. Direct Reply isn't supported on Wear.
+ assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_WATCH));
}
@After
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
new file mode 100644
index 000000000000..83ced2c2258f
--- /dev/null
+++ b/tests/InputScreenshotTest/Android.bp
@@ -0,0 +1,76 @@
+package {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "InputScreenshotTestRNGFiles",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ exclude_srcs: [
+ "src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt",
+ "src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt",
+ ],
+}
+
+android_test {
+ name: "InputScreenshotTests",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ exclude_srcs: [
+ "src/android/input/screenshot/package-info.java",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ static_libs: [
+ "androidx.arch.core_core-testing",
+ "androidx.compose.ui_ui-test-junit4",
+ "androidx.compose.ui_ui-test-manifest",
+ "androidx.lifecycle_lifecycle-runtime-testing",
+ "androidx.compose.animation_animation",
+ "androidx.compose.material3_material3",
+ "androidx.compose.material_material-icons-extended",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.runtime_runtime-livedata",
+ "androidx.compose.ui_ui-tooling-preview",
+ "androidx.lifecycle_lifecycle-livedata-ktx",
+ "androidx.lifecycle_lifecycle-runtime-compose",
+ "androidx.navigation_navigation-compose",
+ "truth",
+ "androidx.compose.runtime_runtime",
+ "androidx.test.core",
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "androidx.test.uiautomator_uiautomator",
+ "servicestests-utils",
+ "frameworks-base-testutils",
+ "platform-screenshot-diff-core",
+ "hamcrest-library",
+ "kotlin-test",
+ "flag-junit",
+ "platform-parametric-runner-lib",
+ "platform-test-annotations",
+ "services.core.unboosted",
+ "testables",
+ "testng",
+ "truth",
+ ],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ ],
+ test_suites: ["device-tests"],
+ compile_multilib: "both",
+ use_embedded_native_libs: false,
+ asset_dirs: ["assets"],
+}
diff --git a/tests/notification/AndroidManifest.xml b/tests/InputScreenshotTest/AndroidManifest.xml
index 7cee00a72781..9ffbb3a58ebb 100644
--- a/tests/notification/AndroidManifest.xml
+++ b/tests/InputScreenshotTest/AndroidManifest.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright 2023 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.
@@ -15,14 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.frameworks.tests.notification"
- >
+ package="com.android.input.screenshot">
+
+ <uses-sdk android:minSdkVersion="21"/>
+
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.frameworks.tests.notification"
- android:label="Frameworks Notification Tests" />
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Screenshot tests for Input"
+ android:targetPackage="com.android.input.screenshot">
+ </instrumentation>
</manifest>
diff --git a/tests/InputScreenshotTest/AndroidTest.xml b/tests/InputScreenshotTest/AndroidTest.xml
new file mode 100644
index 000000000000..cc25fa454f06
--- /dev/null
+++ b/tests/InputScreenshotTest/AndroidTest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 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.
+ -->
+<configuration description="Runs Input screendiff tests.">
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="test-suite-tag" value="apct" />
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="optimized-property-setting" value="true" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="InputScreenshotTests.apk" />
+ </target_preparer>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys"
+ value="/data/user/0/com.android.input.screenshot/files/input_screenshots" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.input.screenshot" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
diff --git a/tests/InputScreenshotTest/OWNERS b/tests/InputScreenshotTest/OWNERS
new file mode 100644
index 000000000000..3cffce960b1c
--- /dev/null
+++ b/tests/InputScreenshotTest/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 136048
+include /core/java/android/hardware/input/OWNERS
diff --git a/tests/InputScreenshotTest/TEST_MAPPING b/tests/InputScreenshotTest/TEST_MAPPING
new file mode 100644
index 000000000000..727e609d91ac
--- /dev/null
+++ b/tests/InputScreenshotTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "InputScreenshotTests"
+ }
+ ]
+}
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 000000000000..70e4a7101c7f
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
new file mode 100644
index 000000000000..502c1b4499d4
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
new file mode 100644
index 000000000000..591b2fa9608e
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 000000000000..0137a853e538
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 000000000000..37a91e1fce53
--- /dev/null
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
new file mode 100644
index 000000000000..912f4b8069b4
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -0,0 +1,71 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+ name: "InputRoboRNGTestsAssetsLib",
+ asset_dirs: ["assets"],
+ sdk_version: "current",
+ platform_apis: true,
+ manifest: "AndroidManifest.xml",
+ optimize: {
+ enabled: false,
+ },
+ use_resource_processor: true,
+}
+
+android_app {
+ name: "InputRoboApp",
+ srcs: [],
+ static_libs: [
+ "androidx.test.espresso.core",
+ "androidx.appcompat_appcompat",
+ "flag-junit",
+ "guava",
+ "InputRoboRNGTestsAssetsLib",
+ "platform-screenshot-diff-core",
+ "PlatformComposeSceneTransitionLayoutTestsUtils",
+ ],
+ manifest: "robo-manifest.xml",
+ aaptflags: [
+ "--extra-packages",
+ "com.android.input.screenshot",
+ ],
+ dont_merge_manifests: true,
+ platform_apis: true,
+ system_ext_specific: true,
+ certificate: "platform",
+ privileged: true,
+ resource_dirs: [],
+ kotlincflags: ["-Xjvm-default=all"],
+
+ plugins: ["dagger2-compiler"],
+ use_resource_processor: true,
+}
+
+android_robolectric_test {
+ name: "InputRoboRNGTests",
+ srcs: [
+ ":InputScreenshotTestRNGFiles",
+ ":flag-junit",
+ ":platform-test-screenshot-rules",
+ ],
+ // Do not add any new libraries here, they should be added to SystemUIGoogleRobo above.
+ static_libs: [
+ "androidx.compose.runtime_runtime",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.ext.junit",
+ "inline-mockito-robolectric-prebuilt",
+ "platform-parametric-runner-lib",
+ "uiautomator-helpers",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ "truth",
+ ],
+ upstream: true,
+ java_resource_dirs: ["config"],
+ instrumentation_for: "InputRoboApp",
+}
diff --git a/tests/InputScreenshotTest/robotests/AndroidManifest.xml b/tests/InputScreenshotTest/robotests/AndroidManifest.xml
new file mode 100644
index 000000000000..56893113288d
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.input.screenshot">
+ <uses-sdk android:minSdkVersion="21"/>
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+</manifest>
diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png
new file mode 100644
index 000000000000..baf204a6cfb3
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png
new file mode 100644
index 000000000000..deb3ceeca7fb
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png
new file mode 100644
index 000000000000..34e25f73d953
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/config/robolectric.properties b/tests/InputScreenshotTest/robotests/config/robolectric.properties
new file mode 100644
index 000000000000..83d7549551ce
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/config/robolectric.properties
@@ -0,0 +1,15 @@
+# Copyright (C) 2022 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.
+#
+sdk=NEWEST_SDK \ No newline at end of file
diff --git a/tests/InputScreenshotTest/robotests/robo-manifest.xml b/tests/InputScreenshotTest/robotests/robo-manifest.xml
new file mode 100644
index 000000000000..e86f58ef0e55
--- /dev/null
+++ b/tests/InputScreenshotTest/robotests/robo-manifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--- Include all the namespaces we will ever need anywhere, because this is the source the manifest merger uses for namespaces -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.input.screenshot"
+ coreApp="true">
+ <application>
+ <activity
+ android:name="androidx.activity.ComponentActivity"
+ android:exported="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
new file mode 100644
index 000000000000..84c971c750fb
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/Bitmap.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.os.Build
+import android.view.View
+import platform.test.screenshot.matchers.MSSIMMatcher
+import platform.test.screenshot.matchers.PixelPerfectMatcher
+
+/** Draw this [View] into a [Bitmap]. */
+// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their
+// tests.
+fun View.drawIntoBitmap(): Bitmap {
+ val bitmap =
+ Bitmap.createBitmap(
+ measuredWidth,
+ measuredHeight,
+ Bitmap.Config.ARGB_8888,
+ )
+ val canvas = Canvas(bitmap)
+ draw(canvas)
+ return bitmap
+}
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ */
+val UnitTestBitmapMatcher =
+ if (Build.CPU_ABI == "x86_64") {
+ // Different CPU architectures can sometimes end up rendering differently, so we can't do
+ // pixel-perfect matching on different architectures using the same golden. Given that our
+ // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the
+ // x86_64 architecture and use the Structural Similarity Index on others.
+ // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can
+ // do pixel perfect matching both at presubmit time and at development time with actual
+ // devices.
+ PixelPerfectMatcher()
+ } else {
+ MSSIMMatcher()
+ }
+
+/**
+ * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for
+ * screenshot *unit* tests.
+ *
+ * We use the Structural Similarity Index for integration tests because they usually contain
+ * additional information and noise that shouldn't break the test.
+ */
+val IntegrationTestBitmapMatcher = MSSIMMatcher() \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
new file mode 100644
index 000000000000..edddc6b41cf7
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/DefaultDeviceEmulationSpec.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.DisplaySpec
+
+/**
+ * The emulations specs for all 8 permutations of:
+ * - phone or tablet.
+ * - dark of light mode.
+ * - portrait or landscape.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletFull
+ get() = PhoneAndTabletFullSpec
+
+private val PhoneAndTabletFullSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, Displays.Tablet)
+
+/**
+ * The emulations specs of:
+ * - phone + light mode + portrait.
+ * - phone + light mode + landscape.
+ * - tablet + dark mode + portrait.
+ *
+ * This allows to test the most important permutations of a screen/layout with only 3
+ * configurations.
+ */
+val DeviceEmulationSpec.Companion.PhoneAndTabletMinimal
+ get() = PhoneAndTabletMinimalSpec
+
+private val PhoneAndTabletMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false) +
+ DeviceEmulationSpec.forDisplays(Displays.Tablet, isDarkTheme = true, isLandscape = false)
+
+/**
+ * This allows to test only single most important configuration.
+ */
+val DeviceEmulationSpec.Companion.PhoneMinimal
+ get() = PhoneMinimalSpec
+
+private val PhoneMinimalSpec =
+ DeviceEmulationSpec.forDisplays(Displays.Phone, isDarkTheme = false, isLandscape = false)
+
+object Displays {
+ val Phone =
+ DisplaySpec(
+ "phone",
+ width = 1440,
+ height = 3120,
+ densityDpi = 560,
+ )
+
+ val Tablet =
+ DisplaySpec(
+ "tablet",
+ width = 2560,
+ height = 1600,
+ densityDpi = 320,
+ )
+} \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
new file mode 100644
index 000000000000..8faf22440828
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputGoldenImagePathManager.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import androidx.test.platform.app.InstrumentationRegistry
+import platform.test.screenshot.GoldenImagePathManager
+import platform.test.screenshot.PathConfig
+
+/** A [GoldenImagePathManager] that should be used for all Input screenshot tests. */
+class InputGoldenImagePathManager(
+ pathConfig: PathConfig,
+ assetsPathRelativeToBuildRoot: String
+) :
+ GoldenImagePathManager(
+ appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToBuildRoot = assetsPathRelativeToBuildRoot,
+ deviceLocalPath =
+ InstrumentationRegistry.getInstrumentation()
+ .targetContext
+ .filesDir
+ .absolutePath
+ .toString() + "/input_screenshots",
+ pathConfig = pathConfig,
+ ) {
+ override fun toString(): String {
+ // This string is appended to all actual/expected screenshots on the device, so make sure
+ // it is a static value.
+ return "InputGoldenImagePathManager"
+ }
+} \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
new file mode 100644
index 000000000000..75dab41d3609
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.os.Build
+import androidx.activity.ComponentActivity
+import androidx.compose.foundation.Image
+import androidx.compose.ui.platform.ViewRootForTest
+import androidx.compose.ui.test.junit4.createAndroidComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.graphics.asImageBitmap
+import org.junit.rules.RuleChain
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import platform.test.screenshot.DeviceEmulationRule
+import platform.test.screenshot.DeviceEmulationSpec
+import platform.test.screenshot.MaterialYouColorsRule
+import platform.test.screenshot.ScreenshotTestRule
+import platform.test.screenshot.getEmulatedDevicePathConfig
+
+/** A rule for Input screenshot diff tests. */
+class InputScreenshotTestRule(
+ emulationSpec: DeviceEmulationSpec,
+ assetsPathRelativeToBuildRoot: String
+) : TestRule {
+ private val colorsRule = MaterialYouColorsRule()
+ private val deviceEmulationRule = DeviceEmulationRule(emulationSpec)
+ private val screenshotRule =
+ ScreenshotTestRule(
+ InputGoldenImagePathManager(
+ getEmulatedDevicePathConfig(emulationSpec),
+ assetsPathRelativeToBuildRoot
+ )
+ )
+ private val composeRule = createAndroidComposeRule<ComponentActivity>()
+ private val roboRule =
+ RuleChain.outerRule(deviceEmulationRule)
+ .around(screenshotRule)
+ .around(composeRule)
+ private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule)
+ private val matcher = UnitTestBitmapMatcher
+ private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false
+
+ override fun apply(base: Statement, description: Description): Statement {
+ val ruleToApply = if (isRobolectric) roboRule else delegateRule
+ return ruleToApply.apply(base, description)
+ }
+
+ /**
+ * Compare [content] with the golden image identified by [goldenIdentifier].
+ */
+ fun screenshotTest(
+ goldenIdentifier: String,
+ content: (Context) -> Bitmap,
+ ) {
+ // Make sure that the activity draws full screen and fits the whole display.
+ val activity = composeRule.activity
+ activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) }
+
+ // Set the content using the AndroidComposeRule to make sure that the Activity is set up
+ // correctly.
+ composeRule.setContent {
+ Image(
+ bitmap = content(activity).asImageBitmap(),
+ contentDescription = null,
+ )
+ }
+ composeRule.waitForIdle()
+
+ val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view
+ screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher)
+ }
+}
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
new file mode 100644
index 000000000000..e85578663764
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Ansi physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewAnsiScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-ansi") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.US),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+} \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
new file mode 100644
index 000000000000..ab7bb4eda899
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for Iso physical layout. */
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview") {
+ context: Context -> LayoutPreview.createLayoutPreview(context, null)
+ }
+ }
+
+}
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
new file mode 100644
index 000000000000..5231c14bfc9a
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2023 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.input.screenshot
+
+import android.content.Context
+import android.hardware.input.KeyboardLayout
+import android.os.LocaleList
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.hardware.input.Flags
+import java.util.Locale
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import platform.test.screenshot.DeviceEmulationSpec
+
+/** A screenshot test for Keyboard layout preview for JIS physical layout. */
+@RunWith(Parameterized::class)
+class KeyboardLayoutPreviewJisScreenshotTest(emulationSpec: DeviceEmulationSpec) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getTestSpecs() = DeviceEmulationSpec.PhoneMinimal
+ }
+
+ val setFlagsRule = SetFlagsRule()
+ val screenshotRule = InputScreenshotTestRule(
+ emulationSpec,
+ "frameworks/base/tests/InputScreenshotTest/assets"
+ )
+
+ @get:Rule
+ val ruleChain = RuleChain.outerRule(screenshotRule).around(setFlagsRule)
+
+ @Test
+ fun test() {
+ setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
+ screenshotRule.screenshotTest("layout-preview-jis") {
+ context: Context -> LayoutPreview.createLayoutPreview(
+ context,
+ KeyboardLayout(
+ "descriptor",
+ "layout",
+ /* collection= */null,
+ /* priority= */0,
+ LocaleList(Locale.JAPAN),
+ /* layoutType= */0,
+ /* vid= */0,
+ /* pid= */0
+ )
+ )
+ }
+ }
+
+} \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
new file mode 100644
index 000000000000..76ee3791011b
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/LayoutPreview.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 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.
+ */
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.hardware.input.InputManager
+import android.hardware.input.KeyboardLayout
+import android.util.TypedValue
+import kotlin.math.roundToInt
+
+object LayoutPreview {
+ fun createLayoutPreview(context: Context, layout: KeyboardLayout?): Bitmap {
+ val im = context.getSystemService(InputManager::class.java)!!
+ val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 600.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 200.0F, context.getResources().getDisplayMetrics()).roundToInt()
+ val drawable = im.getKeyboardLayoutPreview(layout, width, height)!!
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight())
+ drawable.draw(canvas)
+ return bitmap
+ }
+} \ No newline at end of file
diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java
new file mode 100644
index 000000000000..4b5a56d3bd1d
--- /dev/null
+++ b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java
@@ -0,0 +1,4 @@
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+package com.android.input.screenshot;
+
+import org.robolectric.annotation.GraphicsMode;
diff --git a/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java
index 7419ee1230d3..00085f845cd0 100644
--- a/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java
+++ b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java
@@ -104,11 +104,11 @@ public class TimeoutRecordTest {
@Test
public void forServiceExec_returnsCorrectTimeoutRecord() {
- TimeoutRecord record = TimeoutRecord.forServiceExec("Test ANR reason");
+ TimeoutRecord record = TimeoutRecord.forServiceExec("com.app.MyService", 1000L);
assertNotNull(record);
assertEquals(record.mKind, TimeoutRecord.TimeoutKind.SERVICE_EXEC);
- assertEquals(record.mReason, "Test ANR reason");
+ assertEquals(record.mReason, "executing service com.app.MyService, waited 1000ms");
assertTrue(record.mEndTakenBeforeLocks);
}
diff --git a/tests/MotionPrediction/Android.bp b/tests/MotionPrediction/Android.bp
index 6cda8f050987..b4a435909953 100644
--- a/tests/MotionPrediction/Android.bp
+++ b/tests/MotionPrediction/Android.bp
@@ -26,5 +26,8 @@ package {
android_app {
name: "MotionPrediction",
srcs: ["**/*.kt"],
+ kotlincflags: [
+ "-Werror",
+ ],
sdk_version: "current",
}
diff --git a/tests/MultiDeviceInput/Android.bp b/tests/MultiDeviceInput/Android.bp
new file mode 100644
index 000000000000..3c80873168b4
--- /dev/null
+++ b/tests/MultiDeviceInput/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright 2023 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 {
+ // 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"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "MultiDeviceInput",
+ srcs: ["**/*.kt"],
+ kotlincflags: [
+ "-Werror",
+ ],
+ sdk_version: "current",
+}
diff --git a/tests/MultiDeviceInput/AndroidManifest.xml b/tests/MultiDeviceInput/AndroidManifest.xml
new file mode 100644
index 000000000000..ed8cadb9519b
--- /dev/null
+++ b/tests/MultiDeviceInput/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.multideviceinput">
+
+ <application android:allowBackup="false"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name=".MainActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tests/MultiDeviceInput/OWNERS b/tests/MultiDeviceInput/OWNERS
new file mode 100644
index 000000000000..c88bfe97cab9
--- /dev/null
+++ b/tests/MultiDeviceInput/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/tests/MultiDeviceInput/README.md b/tests/MultiDeviceInput/README.md
new file mode 100644
index 000000000000..5fcdeda6e5d7
--- /dev/null
+++ b/tests/MultiDeviceInput/README.md
@@ -0,0 +1,19 @@
+# MultiDeviceInput test app #
+
+This demo app is for manual testing of the multi-device input feature.
+It creates two windows - one on the left and one on the right. You can use different input devices
+in these windows.
+
+## Installation ##
+Install this using:
+```
+APP=MultiDeviceInput; m $APP && adb install $ANDROID_PRODUCT_OUT/system/app/$APP/$APP.apk
+```
+
+## Features ##
+
+* Touch in one window, use stylus in another window, at the same time
+* Visualize hovering stylus
+* Pinch zoom in one window to affect the line thickness in another window
+* Check whether stylus rejects touch in the same window
+* (in the future) Check stylus and touch operation in the same window
diff --git a/tests/MultiDeviceInput/res/layout/activity_main.xml b/tests/MultiDeviceInput/res/layout/activity_main.xml
new file mode 100644
index 000000000000..a6a6f891a034
--- /dev/null
+++ b/tests/MultiDeviceInput/res/layout/activity_main.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context="test.multideviceinput.MainActivity">
+
+</LinearLayout>
diff --git a/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000000..cde69bcccec6
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..c133a0cbd379
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..bfa42f0e7b91
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..324e72cdd748
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png b/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..aee44e138434
--- /dev/null
+++ b/tests/MultiDeviceInput/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MultiDeviceInput/res/values-w820dp/dimens.xml b/tests/MultiDeviceInput/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000000..b14a560efd72
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/colors.xml b/tests/MultiDeviceInput/res/values/colors.xml
new file mode 100644
index 000000000000..c37df9f7b428
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/dimens.xml b/tests/MultiDeviceInput/res/values/dimens.xml
new file mode 100644
index 000000000000..bdb8ede1c913
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/strings.xml b/tests/MultiDeviceInput/res/values/strings.xml
new file mode 100644
index 000000000000..3827c344f87f
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/strings.xml
@@ -0,0 +1,17 @@
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string name="app_name">Simultaneous touch and stylus</string>
+</resources>
diff --git a/tests/MultiDeviceInput/res/values/styles.xml b/tests/MultiDeviceInput/res/values/styles.xml
new file mode 100644
index 000000000000..a563e7e09706
--- /dev/null
+++ b/tests/MultiDeviceInput/res/values/styles.xml
@@ -0,0 +1,23 @@
+<!-- Copyright 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="android:colorPrimary">@color/colorPrimary</item>
+ <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="android:colorAccent">@color/colorAccent</item>
+ </style>
+</resources>
diff --git a/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt
new file mode 100644
index 000000000000..b5bd9ca746aa
--- /dev/null
+++ b/tests/MultiDeviceInput/src/test/multideviceinput/DrawingView.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2023 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 test.multideviceinput
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.InputDevice.SOURCE_STYLUS
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_HOVER_EXIT
+import android.view.MotionEvent.ACTION_UP
+import android.view.ScaleGestureDetector
+import android.view.View
+
+import java.util.Vector
+
+private fun drawLine(canvas: Canvas, from: MotionEvent, to: MotionEvent, paint: Paint) {
+ // Correct implementation here would require us to build a set of pointers and then iterate
+ // through them. Instead, we are taking a few shortcuts and ignore some of the events, which
+ // causes occasional gaps in the drawings.
+ if (from.pointerCount != to.pointerCount) {
+ return
+ }
+ // Now, 'from' is guaranteed to have as many pointers as the 'to' event. It doesn't
+ // necessarily mean they are the same pointers, though.
+ for (p in 0..<from.pointerCount) {
+ val x0 = from.getX(p)
+ val y0 = from.getY(p)
+ if (to.getPointerId(p) == from.getPointerId(p)) {
+ // This only works when the i-th pointer in "to" is the same pointer
+ // as the i-th pointer in "from"`. It's not guaranteed by the input APIs,
+ // but it works in practice.
+ val x1 = to.getX(p)
+ val y1 = to.getY(p)
+ // Ignoring historical data here for simplicity
+ canvas.drawLine(x0, y0, x1, y1, paint)
+ }
+ }
+}
+
+private fun drawCircle(canvas: Canvas, event: MotionEvent, paint: Paint, radius: Float) {
+ val x = event.getX()
+ val y = event.getY()
+ canvas.drawCircle(x, y, radius, paint)
+}
+
+/**
+ * Draw the current stroke
+ */
+class DrawingView : View {
+ private val TAG = "DrawingView"
+
+ private var myState: SharedScaledPointerSize? = null
+ private var otherState: SharedScaledPointerSize? = null
+
+ constructor(
+ context: Context,
+ myState: SharedScaledPointerSize,
+ otherState: SharedScaledPointerSize
+ ) : super(context) {
+ this.myState = myState
+ this.otherState = otherState
+ init()
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init()
+ }
+
+ val touchEvents = mutableMapOf<Int, Vector<Pair<MotionEvent, Paint>>>()
+ val hoverEvents = mutableMapOf<Int, MotionEvent>()
+
+ val scaleGestureListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
+
+ override fun onScaleBegin(scaleGestureDetector: ScaleGestureDetector): Boolean {
+ return true
+ }
+
+ override fun onScale(scaleGestureDetector: ScaleGestureDetector): Boolean {
+ val scaleFactor = scaleGestureDetector.scaleFactor
+ when (otherState?.state) {
+ PointerState.DOWN -> {
+ otherState?.lineSize = (otherState?.lineSize ?: 5f) * scaleFactor
+ }
+ PointerState.HOVER -> {
+ otherState?.circleSize = (otherState?.circleSize ?: 20f) * scaleFactor
+ }
+ else -> {}
+ }
+ return true
+ }
+ }
+ private val scaleGestureDetector = ScaleGestureDetector(context, scaleGestureListener, null)
+
+ private var touchPaint = Paint()
+ private var stylusPaint = Paint()
+
+ private fun init() {
+ touchPaint.color = Color.RED
+ touchPaint.setStrokeWidth(5f)
+ stylusPaint.color = Color.YELLOW
+ stylusPaint.setStrokeWidth(5f)
+
+ setOnHoverListener { _, event -> processHoverEvent(event); true }
+ }
+
+ private fun processTouchEvent(event: MotionEvent) {
+ scaleGestureDetector.onTouchEvent(event)
+ if (event.actionMasked == ACTION_DOWN) {
+ touchEvents.remove(event.deviceId)
+ myState?.state = PointerState.DOWN
+ } else if (event.actionMasked == ACTION_UP) {
+ myState?.state = PointerState.NONE
+ }
+ var vec = touchEvents.getOrPut(event.deviceId) { Vector<Pair<MotionEvent, Paint>>() }
+
+ val paint = if (event.isFromSource(SOURCE_STYLUS)) {
+ val size = myState?.lineSize ?: 5f
+ stylusPaint.setStrokeWidth(size)
+ Paint(stylusPaint)
+ } else {
+ val size = myState?.lineSize ?: 5f
+ touchPaint.setStrokeWidth(size)
+ Paint(touchPaint)
+ }
+ vec.add(Pair(MotionEvent.obtain(event), paint))
+ invalidate()
+ }
+
+ private fun processHoverEvent(event: MotionEvent) {
+ hoverEvents.remove(event.deviceId)
+ if (event.getActionMasked() != ACTION_HOVER_EXIT) {
+ hoverEvents.put(event.deviceId, MotionEvent.obtain(event))
+ myState?.state = PointerState.HOVER
+ } else {
+ myState?.state = PointerState.NONE
+ }
+ invalidate()
+ }
+
+ public override fun onTouchEvent(event: MotionEvent): Boolean {
+ processTouchEvent(event)
+ return true
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ // Draw touch and stylus MotionEvents
+ for ((_, vec) in touchEvents ) {
+ for (i in 1 until vec.size) {
+ drawLine(canvas, vec[i - 1].first, vec[i].first, vec[i].second)
+ }
+ }
+ // Draw hovers
+ for ((_, event) in hoverEvents ) {
+ if (event.isFromSource(SOURCE_STYLUS)) {
+ val size = myState?.circleSize ?: 20f
+ drawCircle(canvas, event, stylusPaint, size)
+ } else {
+ val size = myState?.circleSize ?: 20f
+ drawCircle(canvas, event, touchPaint, size)
+ }
+ }
+ }
+}
diff --git a/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt b/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt
new file mode 100644
index 000000000000..911208579d4f
--- /dev/null
+++ b/tests/MultiDeviceInput/src/test/multideviceinput/MainActivity.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 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 test.multideviceinput
+
+import android.app.Activity
+import android.graphics.Color
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets.Type
+import android.view.WindowManager
+
+
+enum class PointerState {
+ DOWN, // One or more pointer(s) down, lines are being drawn
+ HOVER, // Pointer is hovering
+ NONE, // Nothing is touching or hovering
+}
+
+data class SharedScaledPointerSize(
+ var lineSize: Float,
+ var circleSize: Float,
+ var state: PointerState
+)
+
+class MainActivity : Activity() {
+ val TAG = "MultiDeviceInput"
+ private val leftState = SharedScaledPointerSize(5f, 20f, PointerState.NONE)
+ private val rightState = SharedScaledPointerSize(5f, 20f, PointerState.NONE)
+ private lateinit var left: View
+ private lateinit var right: View
+
+ override fun onResume() {
+ super.onResume()
+
+ val wm = getSystemService(WindowManager::class.java)
+ val wmlp = WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_APPLICATION)
+ wmlp.flags = (wmlp.flags or
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+
+ val windowMetrics = windowManager.currentWindowMetrics
+ val insets = windowMetrics.windowInsets.getInsetsIgnoringVisibility(Type.systemBars())
+ val width = windowMetrics.bounds.width() - insets.left - insets.right
+ val height = windowMetrics.bounds.height() - insets.top - insets.bottom
+
+ wmlp.width = width * 24 / 50
+ wmlp.height = height * 35 / 50
+
+ val vglp = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ )
+
+ wmlp.setTitle("Left -- " + getPackageName())
+ wmlp.gravity = Gravity.CENTER_VERTICAL or Gravity.START
+ left = DrawingView(this, leftState, rightState)
+ left.setBackgroundColor(Color.LTGRAY)
+ left.setLayoutParams(vglp)
+ wm.addView(left, wmlp)
+
+ wmlp.setTitle("Right -- " + getPackageName())
+ wmlp.gravity = Gravity.CENTER_VERTICAL or Gravity.END
+ right = DrawingView(this, rightState, leftState)
+ right.setBackgroundColor(Color.LTGRAY)
+ right.setLayoutParams(vglp)
+ wm.addView(right, wmlp)
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 56ab755af47b..7e43566d56f8 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -37,6 +37,8 @@ class ColorModeControls : LinearLayout, WindowObserver {
private var window: Window? = null
private var currentModeDisplay: TextView? = null
+ private var desiredRatio = 0.0f
+
override fun onFinishInflate() {
super.onFinishInflate()
val window = window ?: throw IllegalStateException("Failed to attach window")
@@ -67,6 +69,7 @@ class ColorModeControls : LinearLayout, WindowObserver {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
+ desiredRatio = window?.desiredHdrHeadroom ?: 0.0f
val hdrVis = if (display.isHdrSdrRatioAvailable) {
display.registerHdrSdrRatioChangedListener({ it.run() }, hdrSdrListener)
View.VISIBLE
@@ -83,6 +86,11 @@ class ColorModeControls : LinearLayout, WindowObserver {
}
private fun setColorMode(newMode: Int) {
+ if (newMode == ActivityInfo.COLOR_MODE_HDR &&
+ window!!.colorMode == ActivityInfo.COLOR_MODE_HDR) {
+ desiredRatio = (desiredRatio + 1) % 5.0f
+ window!!.desiredHdrHeadroom = desiredRatio
+ }
window!!.colorMode = newMode
updateModeInfoDisplay()
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
index 7cf69b7780d9..c92d768439fd 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
@@ -42,7 +42,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
private var selectedImage = -1
private var outputMode = R.id.output_hdr
private var bitmap: Bitmap? = null
- private var gainmap: Gainmap? = null
+ private var originalGainmap: Gainmap? = null
private var gainmapVisualizer: Bitmap? = null
private lateinit var imageView: SubsamplingScaleImageView
private lateinit var gainmapMetadataEditor: GainmapMetadataEditor
@@ -65,15 +65,8 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
findViewById<RadioGroup>(R.id.output_mode)!!.also {
it.check(outputMode)
it.setOnCheckedChangeListener { _, checkedId ->
- val previousMode = outputMode
outputMode = checkedId
- if (previousMode == R.id.output_sdr && checkedId == R.id.output_hdr) {
- animateToHdr()
- } else if (previousMode == R.id.output_hdr && checkedId == R.id.output_sdr) {
- animateToSdr()
- } else {
- updateDisplay()
- }
+ updateDisplay()
}
}
@@ -120,7 +113,7 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
}
private fun doDecode(source: ImageDecoder.Source) {
- gainmap = null
+ originalGainmap = null
bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, source ->
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
@@ -138,9 +131,10 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
findViewById<TextView>(R.id.error_msg)!!.visibility = View.GONE
findViewById<RadioGroup>(R.id.output_mode)!!.visibility = View.VISIBLE
- gainmap = bitmap!!.gainmap
- gainmapMetadataEditor.setGainmap(gainmap)
- val map = gainmap!!.gainmapContents
+ val gainmap = bitmap!!.gainmap!!
+ originalGainmap = gainmap
+ gainmapMetadataEditor.setGainmap(Gainmap(gainmap, gainmap.gainmapContents))
+ val map = gainmap.gainmapContents
if (map.config != Bitmap.Config.ALPHA_8) {
gainmapVisualizer = map
} else {
@@ -164,33 +158,17 @@ class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context
updateDisplay()
}
- private fun animateToHdr() {
- if (bitmap == null || gainmap == null) return
-
- // TODO: Trigger an animation
- updateDisplay()
- }
-
- private fun animateToSdr() {
- if (bitmap == null) return
-
- // TODO: Trigger an animation
- updateDisplay()
- }
-
private fun updateDisplay() {
if (bitmap == null) return
imageView.setImage(ImageSource.cachedBitmap(when (outputMode) {
R.id.output_hdr -> {
- gainmapMetadataEditor.useOriginalMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = originalGainmap
bitmap!!
}
R.id.output_hdr_test -> {
- gainmapMetadataEditor.useEditMetadata()
- bitmap!!.gainmap = gainmap
+ bitmap!!.gainmap = gainmapMetadataEditor.editedGainmap()
bitmap!!
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
index 1a79a11a3718..43debb11013a 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -28,23 +28,27 @@ import android.widget.TextView
import com.android.test.silkfx.R
data class GainmapMetadata(
- var ratioMin: Float,
- var ratioMax: Float,
- var capacityMin: Float,
- var capacityMax: Float,
- var gamma: Float,
- var offsetSdr: Float,
- var offsetHdr: Float
+ var ratioMin: Float,
+ var ratioMax: Float,
+ var capacityMin: Float,
+ var capacityMax: Float,
+ var gamma: Float,
+ var offsetSdr: Float,
+ var offsetHdr: Float
)
+/**
+ * Note: This can only handle single-channel gainmaps nicely. It will force all 3-channel
+ * metadata to have the same value single value and is not intended to be a robust demonstration
+ * of gainmap metadata editing
+ */
class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
- private var gainmap: Gainmap? = null
- private var showingEdits = false
+ private lateinit var gainmap: Gainmap
private var metadataPopup: PopupWindow? = null
private var originalMetadata: GainmapMetadata = GainmapMetadata(
- 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
+ 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f)
private var currentMetadata: GainmapMetadata = originalMetadata.copy()
private val maxProgress = 100.0f
@@ -61,23 +65,18 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
private val maxGamma = 3.0f
// Min and max offsets are 0.0 and 1.0 respectively
- fun setGainmap(newGainmap: Gainmap?) {
+ fun setGainmap(newGainmap: Gainmap) {
gainmap = newGainmap
- originalMetadata = GainmapMetadata(gainmap!!.getRatioMin()[0],
- gainmap!!.getRatioMax()[0], gainmap!!.getMinDisplayRatioForHdrTransition(),
- gainmap!!.getDisplayRatioForFullHdr(), gainmap!!.getGamma()[0],
- gainmap!!.getEpsilonSdr()[0], gainmap!!.getEpsilonHdr()[0])
+ originalMetadata = GainmapMetadata(gainmap.getRatioMin()[0],
+ gainmap.getRatioMax()[0], gainmap.getMinDisplayRatioForHdrTransition(),
+ gainmap.getDisplayRatioForFullHdr(), gainmap.getGamma()[0],
+ gainmap.getEpsilonSdr()[0], gainmap.getEpsilonHdr()[0])
currentMetadata = originalMetadata.copy()
}
- fun useOriginalMetadata() {
- showingEdits = false
- applyMetadata(originalMetadata)
- }
-
- fun useEditMetadata() {
- showingEdits = true
+ fun editedGainmap(): Gainmap {
applyMetadata(currentMetadata)
+ return gainmap
}
fun closeEditor() {
@@ -93,7 +92,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null)
metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT)
+ ViewGroup.LayoutParams.WRAP_CONTENT)
metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0)
(view.getParent() as ViewGroup).removeView(view)
@@ -117,7 +116,7 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
val offsetSdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
val offsetHdrSeek = view.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
- offsetSdrSeek, offsetHdrSeek).forEach {
+ offsetSdrSeek, offsetHdrSeek).forEach {
it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (!fromUser) return
@@ -149,37 +148,37 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
val offsetHdrSeek = parent.requireViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
gainmapMinSeek.setProgress(
- ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
+ ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
gainmapMaxSeek.setProgress(
- ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
+ ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
capacityMinSeek.setProgress(
- ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
+ ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
capacityMaxSeek.setProgress(
- ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
+ ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
gammaSeek.setProgress(
- ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
+ ((currentMetadata.gamma - minGamma) / maxGamma * maxProgress).toInt())
// Log base 3 via: log_b(x) = log_y(x) / log_y(b)
offsetSdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetSdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
offsetHdrSeek.setProgress(
- ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
- .toFloat() * maxProgress).toInt())
+ ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
- "%.3f".format(currentMetadata.ratioMin))
+ "%.3f".format(currentMetadata.ratioMin))
parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
- "%.3f".format(currentMetadata.ratioMax))
+ "%.3f".format(currentMetadata.ratioMax))
parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
- "%.3f".format(currentMetadata.capacityMin))
+ "%.3f".format(currentMetadata.capacityMin))
parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
- "%.3f".format(currentMetadata.capacityMax))
+ "%.3f".format(currentMetadata.capacityMax))
parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
- "%.3f".format(currentMetadata.gamma))
+ "%.3f".format(currentMetadata.gamma))
parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
- "%.5f".format(currentMetadata.offsetSdr))
+ "%.5f".format(currentMetadata.offsetSdr))
parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
- "%.5f".format(currentMetadata.offsetHdr))
+ "%.5f".format(currentMetadata.offsetHdr))
}
private fun resetGainmapMetadata() {
@@ -189,69 +188,59 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
}
private fun applyMetadata(newMetadata: GainmapMetadata) {
- gainmap!!.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
- gainmap!!.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
- gainmap!!.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
- gainmap!!.setDisplayRatioForFullHdr(newMetadata.capacityMax)
- gainmap!!.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
- gainmap!!.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
- gainmap!!.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
+ gainmap.setRatioMin(newMetadata.ratioMin, newMetadata.ratioMin, newMetadata.ratioMin)
+ gainmap.setRatioMax(newMetadata.ratioMax, newMetadata.ratioMax, newMetadata.ratioMax)
+ gainmap.setMinDisplayRatioForHdrTransition(newMetadata.capacityMin)
+ gainmap.setDisplayRatioForFullHdr(newMetadata.capacityMax)
+ gainmap.setGamma(newMetadata.gamma, newMetadata.gamma, newMetadata.gamma)
+ gainmap.setEpsilonSdr(newMetadata.offsetSdr, newMetadata.offsetSdr, newMetadata.offsetSdr)
+ gainmap.setEpsilonHdr(newMetadata.offsetHdr, newMetadata.offsetHdr, newMetadata.offsetHdr)
renderView.invalidate()
}
private fun updateGainmapMin(normalized: Float) {
val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val).setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMin = newValue
- if (showingEdits) {
- gainmap!!.setRatioMin(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMin(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateGainmapMax(normalized: Float) {
val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
parent.requireViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val).setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.ratioMax = newValue
- if (showingEdits) {
- gainmap!!.setRatioMax(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setRatioMax(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateCapacityMin(normalized: Float) {
val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymin_val).setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMin = newValue
- if (showingEdits) {
- gainmap!!.setMinDisplayRatioForHdrTransition(newValue)
- renderView.invalidate()
- }
+ gainmap.setMinDisplayRatioForHdrTransition(newValue)
+ renderView.invalidate()
}
private fun updateCapacityMax(normalized: Float) {
val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
parent.requireViewById<TextView>(R.id.gainmap_metadata_capacitymax_val).setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.capacityMax = newValue
- if (showingEdits) {
- gainmap!!.setDisplayRatioForFullHdr(newValue)
- renderView.invalidate()
- }
+ gainmap.setDisplayRatioForFullHdr(newValue)
+ renderView.invalidate()
}
private fun updateGamma(normalized: Float) {
val newValue = minGamma + normalized * (maxGamma - minGamma)
parent.requireViewById<TextView>(R.id.gainmap_metadata_gamma_val).setText(
- "%.3f".format(newValue))
+ "%.3f".format(newValue))
currentMetadata.gamma = newValue
- if (showingEdits) {
- gainmap!!.setGamma(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setGamma(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetSdr(normalized: Float) {
@@ -260,12 +249,10 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.requireViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val).setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetSdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonSdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonSdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
private fun updateOffsetHdr(normalized: Float) {
@@ -274,11 +261,9 @@ class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
}
parent.requireViewById<TextView>(R.id.gainmap_metadata_offsethdr_val).setText(
- "%.5f".format(newValue))
+ "%.5f".format(newValue))
currentMetadata.offsetHdr = newValue
- if (showingEdits) {
- gainmap!!.setEpsilonHdr(newValue, newValue, newValue)
- renderView.invalidate()
- }
+ gainmap.setEpsilonHdr(newValue, newValue, newValue)
+ renderView.invalidate()
}
}
diff --git a/tests/SurfaceViewBufferTests/Android.bp b/tests/SurfaceViewBufferTests/Android.bp
index d34f780fd40c..055d6258d1ac 100644
--- a/tests/SurfaceViewBufferTests/Android.bp
+++ b/tests/SurfaceViewBufferTests/Android.bp
@@ -44,6 +44,7 @@ android_test {
"kotlin-stdlib",
"kotlinx-coroutines-android",
"flickerlib",
+ "flickerlib-trace_processor_shell",
"truth",
"cts-wm-util",
"CtsSurfaceValidatorLib",
diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml
index c910ecdac1b3..798c67a320ce 100644
--- a/tests/SurfaceViewBufferTests/AndroidManifest.xml
+++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml
@@ -29,9 +29,12 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- Save failed test bitmap images !-->
<uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/>
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
<application android:allowBackup="false"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:networkSecurityConfig="@xml/network_security_config">
<activity android:name=".MainActivity"
android:taskAffinity="com.android.test.MainActivity"
android:theme="@style/AppTheme"
@@ -43,7 +46,7 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
+ <service android:name="com.android.test.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
</service>
diff --git a/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml b/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..4bd9ca049f55
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/res/xml/network_security_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config>
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
index e722ba537a8e..1de965ebcb36 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -76,4 +76,4 @@ class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
}
LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
}
-}
+} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java b/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java
new file mode 100644
index 000000000000..7339a6b8c9a4
--- /dev/null
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.test;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+
+public class LocalMediaProjectionService extends Service {
+
+ private Bitmap mTestBitmap;
+
+ private static final String NOTIFICATION_CHANNEL_ID = "Surfacevalidator";
+ private static final String CHANNEL_NAME = "ProjectionService";
+
+ static final int MSG_START_FOREGROUND_DONE = 1;
+ static final String EXTRA_MESSENGER = "messenger";
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ startForeground(intent);
+ return super.onStartCommand(intent, flags, startId);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mTestBitmap != null) {
+ mTestBitmap.recycle();
+ mTestBitmap = null;
+ }
+ super.onDestroy();
+ }
+
+ private Icon createNotificationIcon() {
+ mTestBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mTestBitmap);
+ canvas.drawColor(Color.BLUE);
+ return Icon.createWithBitmap(mTestBitmap);
+ }
+
+ private void startForeground(Intent intent) {
+ final NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
+
+ final NotificationManager notificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.createNotificationChannel(channel);
+
+ final Notification.Builder notificationBuilder =
+ new Notification.Builder(this, NOTIFICATION_CHANNEL_ID);
+
+ final Notification notification = notificationBuilder.setOngoing(true)
+ .setContentTitle("App is running")
+ .setSmallIcon(createNotificationIcon())
+ .setCategory(Notification.CATEGORY_SERVICE)
+ .setContentText("Context")
+ .build();
+
+ startForeground(2, notification);
+
+ final Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
+ final Message msg = Message.obtain();
+ msg.what = MSG_START_FOREGROUND_DONE;
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ }
+ }
+
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
index df3d30e13908..e80dd8ed0690 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt
@@ -19,7 +19,6 @@ import android.annotation.ColorInt
import android.content.Context
import android.content.Intent
import android.graphics.Rect
-import android.server.wm.WindowManagerState.getLogicalDisplaySize
import android.view.cts.surfacevalidator.CapturedActivity
import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase
import android.view.cts.surfacevalidator.PixelChecker
@@ -44,8 +43,6 @@ open class ScreenRecordTestBase(useBlastAdapter: Boolean) :
mActivity = mActivityRule.launchActivity(Intent())
lateinit var surfaceReadyLatch: CountDownLatch
runOnUiThread {
- it.dismissPermissionDialog()
- it.setLogicalDisplaySize(getLogicalDisplaySize())
surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
}
surfaceReadyLatch.await()
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
index be3ed715d4e2..4c5224a8b151 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -87,4 +87,4 @@ class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(u
checkPixels(svBounds, Color.BLUE)
}
}
-}
+} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
index cf4cb8c97ea1..b03b7335b08b 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -21,11 +21,9 @@ import android.graphics.Color
import android.graphics.Rect
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import android.tools.common.flicker.subject.layers.LayerSubject
import android.tools.common.traces.surfaceflinger.LayersTrace
-import android.tools.device.traces.io.ResultWriter
-import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
import android.tools.device.traces.monitors.withSFTracing
+import android.tools.device.traces.monitors.PerfettoTraceMonitor
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -33,6 +31,7 @@ import org.junit.Rule
import java.io.FileOutputStream
import java.io.IOException
import java.util.concurrent.CountDownLatch
+import perfetto.protos.PerfettoConfig.SurfaceFlingerLayersConfig
open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
SurfaceViewBufferTestBase(useBlastAdapter) {
@@ -43,7 +42,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
@Before
override fun setup() {
super.setup()
- stopLayerTrace()
+ PerfettoTraceMonitor.stopAllSessions()
addSurfaceView()
}
@@ -83,10 +82,6 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
instrumentation.waitForIdleSync()
}
- private fun stopLayerTrace() {
- LayersTraceMonitor().stop(ResultWriter())
- }
-
fun checkPixels(bounds: Rect, @ColorInt color: Int) {
val screenshot = instrumentation.getUiAutomation().takeScreenshot()
val pixels = IntArray(screenshot.width * screenshot.height)
@@ -106,14 +101,19 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e)
}
}
- Assert.assertEquals("Checking $bounds found mismatch $i,$j",
- Color.valueOf(color), Color.valueOf(actualColor))
+ Assert.assertEquals(
+ "Checking $bounds found mismatch $i,$j",
+ Color.valueOf(color),
+ Color.valueOf(actualColor)
+ )
}
}
}
private companion object {
- private const val TRACE_FLAGS =
- (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
+ private val TRACE_FLAGS = listOf(
+ SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_BUFFERS,
+ SurfaceFlingerLayersConfig.TraceFlag.TRACE_FLAG_VIRTUAL_DISPLAYS,
+ )
}
-}
+} \ No newline at end of file
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
index bba967815ba5..1770e32a5145 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -100,4 +100,4 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
INVERSE_DISPLAY(0x08)
}
}
-}
+} \ No newline at end of file
diff --git a/tests/TaskOrganizerTest/Android.bp b/tests/TaskOrganizerTest/Android.bp
index 2a92c42b8ae0..d2ade34148e2 100644
--- a/tests/TaskOrganizerTest/Android.bp
+++ b/tests/TaskOrganizerTest/Android.bp
@@ -42,6 +42,7 @@ android_test {
"kotlin-stdlib",
"kotlinx-coroutines-android",
"flickerlib",
+ "flickerlib-trace_processor_shell",
"truth",
],
}
diff --git a/tests/TaskOrganizerTest/AndroidManifest.xml b/tests/TaskOrganizerTest/AndroidManifest.xml
index 1f1bd3ef7d81..cbeb246eb86c 100644
--- a/tests/TaskOrganizerTest/AndroidManifest.xml
+++ b/tests/TaskOrganizerTest/AndroidManifest.xml
@@ -16,9 +16,11 @@
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+ <!-- Allow the test to connect to perfetto trace processor -->
+ <uses-permission android:name="android.permission.INTERNET"/>
<!-- Enable / Disable tracing !-->
<uses-permission android:name="android.permission.DUMP" />
- <application>
+ <application android:networkSecurityConfig="@xml/network_security_config">
<activity android:name="TaskOrganizerMultiWindowTest"
android:label="TaskOrganizer MW Test"
android:exported="true"
diff --git a/tests/TaskOrganizerTest/res/xml/network_security_config.xml b/tests/TaskOrganizerTest/res/xml/network_security_config.xml
new file mode 100644
index 000000000000..e450a993da28
--- /dev/null
+++ b/tests/TaskOrganizerTest/res/xml/network_security_config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+~ Copyright (C) 2023 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.
+-->
+<network-security-config>
+ <domain-config cleartextTrafficPermitted="true">
+ <domain includeSubdomains="true">localhost</domain>
+ </domain-config>
+</network-security-config> \ No newline at end of file
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index 6f4f7b13af66..2c7905d552f1 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -22,8 +22,6 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import android.tools.common.datatypes.Size
import android.tools.common.flicker.subject.layers.LayersTraceSubject
-import android.tools.device.traces.io.ResultWriter
-import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor
import android.tools.device.traces.monitors.withSFTracing
import org.junit.After
import org.junit.Before
@@ -41,16 +39,13 @@ class ResizeTasksSyncTest {
@get:Rule
var scenarioRule: ActivityScenarioRule<TaskOrganizerMultiWindowTest> =
ActivityScenarioRule<TaskOrganizerMultiWindowTest>(
- TaskOrganizerMultiWindowTest::class.java)
+ TaskOrganizerMultiWindowTest::class.java
+ )
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@Before
fun setup() {
- val monitor = LayersTraceMonitor()
- if (monitor.isEnabled) {
- monitor.stop(ResultWriter())
- }
val firstTaskBounds = Rect(0, 0, 1080, 1000)
val secondTaskBounds = Rect(0, 1000, 1080, 2000)
@@ -71,7 +66,7 @@ class ResizeTasksSyncTest {
val firstBounds = Rect(0, 0, 1080, 800)
val secondBounds = Rect(0, 1000, 1080, 1800)
- val trace = withSFTracing(TRACE_FLAGS) {
+ val trace = withSFTracing() {
lateinit var resizeReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds)
@@ -106,7 +101,6 @@ class ResizeTasksSyncTest {
}
companion object {
- private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
private const val FIRST_ACTIVITY = "Activity1"
private const val SECOND_ACTIVITY = "Activity2"
}
diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp
index 0d2f2ef46cab..af5676df49bc 100644
--- a/tests/UiBench/Android.bp
+++ b/tests/UiBench/Android.bp
@@ -10,7 +10,10 @@ package {
android_test {
name: "UiBench",
sdk_version: "current",
- min_sdk_version: "21",
+ // As Perfetto trace recording is supported on non-rooted devices
+ // since Android 12. Set min/target sdk version to 31.
+ target_sdk_version: "31",
+ min_sdk_version: "31",
// omit gradle 'build' dir
srcs: ["src/**/*.java"],
// use appcompat/support lib from the tree, so improvements/
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index fa5b7c15a6fe..ba9e4a831789 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -24,7 +24,6 @@ import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import static org.junit.Assume.assumeTrue;
import static java.util.concurrent.TimeUnit.SECONDS;
@@ -36,7 +35,6 @@ import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontStyle;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.RootPermissionTest;
-import android.security.FileIntegrityManager;
import android.text.FontConfig;
import android.util.Log;
import android.util.Pair;
@@ -139,10 +137,6 @@ public class UpdatableSystemFontTest {
@Before
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- // Run tests only if updatable system font is enabled.
- FileIntegrityManager fim = context.getSystemService(FileIntegrityManager.class);
- assumeTrue(fim != null);
- assumeTrue(fim.isApkVeritySupported());
mKeyId = insertCert(CERT_PATH);
mFontManager = context.getSystemService(FontManager.class);
expectCommandToSucceed("cmd font clear");
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index f695cbd5daf9..5160df8e9b6d 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -122,7 +122,7 @@ public class UsageStatsDatabasePerfTest {
while (benchmarkState.keepRunning(elapsedTimeNs)) {
final long startTime = SystemClock.elapsedRealtimeNanos();
List<UsageEvents.Event> temp = sUsageStatsDatabase.queryUsageStats(
- UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner);
+ UsageStatsManager.INTERVAL_DAILY, 0, 2, sUsageStatsCombiner, false);
final long endTime = SystemClock.elapsedRealtimeNanos();
elapsedTimeNs = endTime - startTime;
assertEquals(packageCount * eventsPerPackage, temp.size());
diff --git a/tests/UsbTests/Android.bp b/tests/UsbTests/Android.bp
index 1c2f746da5bd..92c271165ad7 100644
--- a/tests/UsbTests/Android.bp
+++ b/tests/UsbTests/Android.bp
@@ -29,7 +29,7 @@ android_test {
static_libs: [
"frameworks-base-testutils",
"androidx.test.rules",
- "mockito-target-inline-minus-junit4",
+ "mockito-target-extended-minus-junit4",
"platform-test-annotations",
"services.core",
"services.net",
@@ -37,7 +37,12 @@ android_test {
"truth",
"UsbManagerTestLib",
],
- jni_libs: ["libdexmakerjvmtiagent"],
+ jni_libs: [
+ // Required for ExtendedMockito
+ "libdexmakerjvmtiagent",
+ "libmultiplejvmtiagentsinterferenceagent",
+ "libstaticjvmtiagent",
+ ],
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index 210e3ea2a9b2..c2d0f7c05b91 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -27,23 +28,29 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.HashMap;
import java.util.Locale;
@@ -68,6 +75,8 @@ public class UsbHandlerTest {
private SharedPreferences mSharedPreferences;
@Mock
private SharedPreferences.Editor mEditor;
+ @Mock
+ private AdbManagerInternal mAdbManagerInternal;
private MockUsbHandler mUsbHandler;
@@ -83,6 +92,7 @@ public class UsbHandlerTest {
private Map<String, String> mMockProperties;
private Map<String, Integer> mMockGlobalSettings;
+ private MockitoSession mStaticMockSession;
private class MockUsbHandler extends UsbDeviceManager.UsbHandler {
boolean mIsUsbTransferAllowed;
@@ -157,6 +167,10 @@ public class UsbHandlerTest {
@Before
public void before() {
MockitoAnnotations.initMocks(this);
+ mStaticMockSession = ExtendedMockito.mockitoSession()
+ .mockStatic(LocalServices.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
mMockProperties = new HashMap<>();
mMockGlobalSettings = new HashMap<>();
when(mSharedPreferences.edit()).thenReturn(mEditor);
@@ -164,6 +178,16 @@ public class UsbHandlerTest {
mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
mUsbSettingsManager, mUsbPermissionManager);
+
+ when(LocalServices.getService(eq(AdbManagerInternal.class)))
+ .thenReturn(mAdbManagerInternal);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mStaticMockSession != null) {
+ mStaticMockSession.finishMocking();
+ }
}
@SmallTest
@@ -234,8 +258,8 @@ public class UsbHandlerTest {
assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
assertEquals(mMockProperties.get(UsbDeviceManager.UsbHandler
.USB_PERSISTENT_CONFIG_PROPERTY), UsbManager.USB_FUNCTION_ADB);
- assertTrue(mUsbHandler.isAdbEnabled());
+ when(mAdbManagerInternal.isAdbEnabled(eq(AdbTransportType.USB))).thenReturn(true);
mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_UPDATE_STATE, 1, 1));
assertTrue(mUsbHandler.mBroadcastedIntent.getBooleanExtra(UsbManager.USB_CONNECTED, false));
@@ -271,20 +295,6 @@ public class UsbHandlerTest {
@SmallTest
@Test
- public void bootCompletedAdbEnabled() {
- mMockProperties.put(UsbDeviceManager.UsbHandler.USB_PERSISTENT_CONFIG_PROPERTY, "adb");
- mUsbHandler = new MockUsbHandler(FgThread.get().getLooper(),
- InstrumentationRegistry.getContext(), mUsbDeviceManager, mUsbAlsaManager,
- mUsbSettingsManager, mUsbPermissionManager);
-
- sendBootCompleteMessages(mUsbHandler);
- assertEquals(mUsbHandler.getEnabledFunctions(), UsbManager.FUNCTION_NONE);
- assertEquals(mMockGlobalSettings.get(Settings.Global.ADB_ENABLED).intValue(), 1);
- assertTrue(mUsbHandler.isAdbEnabled());
- }
-
- @SmallTest
- @Test
public void userSwitchedDisablesMtp() {
mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
UsbManager.FUNCTION_MTP));
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index ba12acb2c877..2b605c594ce8 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -18,6 +18,7 @@ package com.google.android.test.windowinsetstests;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -41,11 +42,11 @@ import android.view.WindowInsetsAnimationController;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
+import androidx.appcompat.app.AppCompatActivity;
+
import java.util.ArrayList;
import java.util.List;
-import androidx.appcompat.app.AppCompatActivity;
-
public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -148,7 +149,7 @@ public class ChatActivity extends AppCompatActivity {
inset = min(inset, shown);
mAnimationController.setInsetsAndAlpha(
Insets.of(0, 0, 0, inset),
- 1f, (inset - start) / (float)(end - start));
+ 1f, start == end ? 1f : (inset - start) / (float) (end - start));
}
});
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
index bbd46c5fddfe..39037f22fdcb 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -26,7 +26,6 @@ java_defaults {
"compatibility-device-util-axt",
"mockito-target-extended-minus-junit4",
"truth",
- "ub-uiautomator",
],
libs: ["android.test.base"],
srcs: [
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
new file mode 100644
index 000000000000..afaa3f0f293f
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "ConcurrentMultiSessionImeTest",
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "platform-test-annotations",
+ "platform-test-rules",
+ "truth",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ sdk_version: "current",
+
+ // Store test artifacts in separated directories for easier debugging.
+ per_testcase_directory: true,
+}
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
new file mode 100644
index 000000000000..0defe5b3f2ff
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.inputmethod.multisessiontest">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.inputmethod.multisessiontest"></instrumentation>
+</manifest>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
new file mode 100644
index 000000000000..fd598c568974
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<configuration description="Config for Concurrent Multi-Session IME tests">
+ <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController"
+ type="module_controller">
+ <option name="required-feature" value="android.software.input_methods" />
+
+ <!-- Currently enabled to automotive only -->
+ <option name="required-feature" value="android.hardware.type.automotive" />
+ </object>
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" />
+ <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" />
+ <option name="teardown-command"
+ value="settings delete secure show_ime_with_hard_keyboard" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL" />
+ <option name="test-file-name" value="ConcurrentMultiSessionImeTest.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.inputmethod.multisessiontest" />
+ </test>
+</configuration>
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
new file mode 100644
index 000000000000..b66ceba458ac
--- /dev/null
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/src/com/android/server/inputmethod/multisessiontest/ConcurrentMultiUserTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod.multisessiontest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class ConcurrentMultiUserTest {
+
+ @Before
+ public void doBeforeEachTest() {
+ // No op
+ }
+
+ @Test
+ public void behaviorBeingTested_expectedResult() {
+ // Sample test
+ Context context =
+ InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertThat(context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE)).isTrue();
+ assertThat(context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INPUT_METHODS)).isTrue();
+ }
+}
diff --git a/tests/inputmethod/OWNERS b/tests/inputmethod/OWNERS
new file mode 100644
index 000000000000..6bb4b17ed4eb
--- /dev/null
+++ b/tests/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/notification/Android.bp b/tests/notification/Android.bp
deleted file mode 100644
index 1c1b5a231e86..000000000000
--- a/tests/notification/Android.bp
+++ /dev/null
@@ -1,16 +0,0 @@
-package {
- // 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"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "NotificationTests",
- // Include all test java files.
- srcs: ["src/**/*.java"],
- libs: ["android.test.runner.stubs"],
- sdk_version: "21",
-}
diff --git a/tests/notification/OWNERS b/tests/notification/OWNERS
deleted file mode 100644
index 396fd1213aca..000000000000
--- a/tests/notification/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/notification/OWNERS
diff --git a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg b/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
deleted file mode 100644
index c6d8ae96485b..000000000000
--- a/tests/notification/res/drawable-nodpi/arubin_hed.jpeg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/bucket.png b/tests/notification/res/drawable-nodpi/bucket.png
deleted file mode 100644
index c8656493dec9..000000000000
--- a/tests/notification/res/drawable-nodpi/bucket.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/matias_hed.jpg b/tests/notification/res/drawable-nodpi/matias_hed.jpg
deleted file mode 100644
index 8cc3081359f7..000000000000
--- a/tests/notification/res/drawable-nodpi/matias_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/page_hed.jpg b/tests/notification/res/drawable-nodpi/page_hed.jpg
deleted file mode 100644
index ea950c8bdb25..000000000000
--- a/tests/notification/res/drawable-nodpi/page_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg b/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
deleted file mode 100644
index 5b7643eb9063..000000000000
--- a/tests/notification/res/drawable-nodpi/romainguy_hed.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg b/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
deleted file mode 100644
index 68473ba6c962..000000000000
--- a/tests/notification/res/drawable-nodpi/romainguy_rockaway.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/add.png b/tests/notification/res/drawable-xhdpi/add.png
deleted file mode 100644
index 7226b3d8b40c..000000000000
--- a/tests/notification/res/drawable-xhdpi/add.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png b/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
deleted file mode 100644
index ca20a917be3d..000000000000
--- a/tests/notification/res/drawable-xhdpi/ic_dial_action_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_end_call.png b/tests/notification/res/drawable-xhdpi/ic_end_call.png
deleted file mode 100644
index c464a6dc5f6f..000000000000
--- a/tests/notification/res/drawable-xhdpi/ic_end_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_media_next.png b/tests/notification/res/drawable-xhdpi/ic_media_next.png
deleted file mode 100644
index 4def965cec24..000000000000
--- a/tests/notification/res/drawable-xhdpi/ic_media_next.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png b/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
deleted file mode 100644
index f1438ed66858..000000000000
--- a/tests/notification/res/drawable-xhdpi/ic_menu_upload.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/icon.png b/tests/notification/res/drawable-xhdpi/icon.png
deleted file mode 100644
index 189e85b5d4f6..000000000000
--- a/tests/notification/res/drawable-xhdpi/icon.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png b/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
deleted file mode 100644
index 658d04feabf9..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_alarm.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png b/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
deleted file mode 100644
index 5ae7782d5d5e..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_calendar.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_email.png b/tests/notification/res/drawable-xhdpi/stat_notify_email.png
deleted file mode 100644
index 23c4672e8d18..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_email.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png b/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
deleted file mode 100644
index 8719eff5ae1a..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_missed_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png b/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
deleted file mode 100644
index 323cb3df46f9..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_sms.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
deleted file mode 100644
index 26dcda358bf8..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png b/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
deleted file mode 100644
index b8b2f8ad1044..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_snooze_longer.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png b/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
deleted file mode 100644
index 12cae9f110b9..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_notify_talk_text.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png b/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
deleted file mode 100644
index db42b7cd969f..000000000000
--- a/tests/notification/res/drawable-xhdpi/stat_sys_phone_call.png
+++ /dev/null
Binary files differ
diff --git a/tests/notification/res/layout/full_screen.xml b/tests/notification/res/layout/full_screen.xml
deleted file mode 100644
index 6ff75525d7a1..000000000000
--- a/tests/notification/res/layout/full_screen.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <ImageView
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:src="@drawable/page_hed"
- android:onClick="dismiss"
- />
-</FrameLayout> \ No newline at end of file
diff --git a/tests/notification/res/layout/main.xml b/tests/notification/res/layout/main.xml
deleted file mode 100644
index f5a740f50bd1..000000000000
--- a/tests/notification/res/layout/main.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <LinearLayout android:id="@+id/linearLayout1" android:orientation="vertical" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_width="match_parent" android:layout_margin="35dp">
- <Button android:id="@+id/button1" android:text="@string/post_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doPost"></Button>
- <Button android:id="@+id/button2" android:text="@string/remove_button_label" android:layout_height="wrap_content" android:layout_width="match_parent" android:onClick="doRemove"></Button>
- </LinearLayout>
-</FrameLayout>
diff --git a/tests/notification/res/values/dimens.xml b/tests/notification/res/values/dimens.xml
deleted file mode 100644
index 21e7bc3f1ef2..000000000000
--- a/tests/notification/res/values/dimens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources>
- <!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_width">64dp</dimen>
- <!-- The width of the big icons in notifications. -->
- <dimen name="notification_large_icon_height">64dp</dimen>
-</resources>
diff --git a/tests/notification/res/values/strings.xml b/tests/notification/res/values/strings.xml
deleted file mode 100644
index 80bf10326f7a..000000000000
--- a/tests/notification/res/values/strings.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="hello">Hello World, NotificationShowcaseActivity!</string>
- <string name="app_name">NotificationShowcase</string>
- <string name="post_button_label">Post Notifications</string>
- <string name="remove_button_label">Remove Notifications</string>
- <string name="answered">call answered</string>
- <string name="ignored">call ignored</string>
- <string name="full_screen_name">Full Screen Activity</string>
-</resources>
diff --git a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java b/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
deleted file mode 100644
index 5d639f6f6266..000000000000
--- a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2015 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.app;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcel;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.style.StyleSpan;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import java.lang.reflect.Method;
-import java.lang.InterruptedException;
-import java.lang.NoSuchMethodError;
-import java.lang.NoSuchMethodException;
-import java.util.ArrayList;
-
-import com.android.frameworks.tests.notification.R;
-
-public class NotificationTests extends AndroidTestCase {
- private static final String TAG = "NOTEST";
- public static void L(String msg, Object... args) {
- Log.v(TAG, (args == null || args.length == 0) ? msg : String.format(msg, args));
- }
-
- public static final String ACTION_CREATE = "create";
- public static final int NOTIFICATION_ID = 31338;
-
- public static final boolean SHOW_PHONE_CALL = false;
- public static final boolean SHOW_INBOX = true;
- public static final boolean SHOW_BIG_TEXT = true;
- public static final boolean SHOW_BIG_PICTURE = true;
- public static final boolean SHOW_MEDIA = true;
- public static final boolean SHOW_STOPWATCH = false;
- public static final boolean SHOW_SOCIAL = false;
- public static final boolean SHOW_CALENDAR = false;
- public static final boolean SHOW_PROGRESS = false;
-
- private static Bitmap getBitmap(Context context, int resId) {
- int largeIconWidth = (int) context.getResources()
- .getDimension(R.dimen.notification_large_icon_width);
- int largeIconHeight = (int) context.getResources()
- .getDimension(R.dimen.notification_large_icon_height);
- Drawable d = context.getResources().getDrawable(resId);
- Bitmap b = Bitmap.createBitmap(largeIconWidth, largeIconHeight, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- d.setBounds(0, 0, largeIconWidth, largeIconHeight);
- d.draw(c);
- return b;
- }
-
- private static PendingIntent makeEmailIntent(Context context, String who) {
- final Intent intent = new Intent(android.content.Intent.ACTION_SENDTO,
- Uri.parse("mailto:" + who));
- return PendingIntent.getActivity(
- context, 0, intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
- }
-
- static final String[] LINES = new String[] {
- "Uh oh",
- "Getting kicked out of this room",
- "I'll be back in 5-10 minutes.",
- "And now \u2026 I have to find my shoes. \uD83D\uDC63",
- "\uD83D\uDC5F \uD83D\uDC5F",
- "\uD83D\uDC60 \uD83D\uDC60",
- };
- static final int MAX_LINES = 5;
- public static Notification makeBigTextNotification(Context context, int update, int id,
- long when) {
- String personUri = null;
- /*
- Cursor c = null;
- try {
- String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY };
- String selections = ContactsContract.Contacts.DISPLAY_NAME + " = 'Mike Cleron'";
- final ContentResolver contentResolver = context.getContentResolver();
- c = contentResolver.query(ContactsContract.Contacts.CONTENT_URI,
- projection, selections, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- int lookupIdx = c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY);
- int idIdx = c.getColumnIndex(ContactsContract.Contacts._ID);
- String lookupKey = c.getString(lookupIdx);
- long contactId = c.getLong(idIdx);
- Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
- personUri = lookupUri.toString();
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (TextUtils.isEmpty(personUri)) {
- Log.w(TAG, "failed to find contact for Mike Cleron");
- } else {
- Log.w(TAG, "Mike Cleron is " + personUri);
- }
- */
-
- StringBuilder longSmsText = new StringBuilder();
- int end = 2 + update;
- if (end > LINES.length) {
- end = LINES.length;
- }
- final int start = Math.max(0, end - MAX_LINES);
- for (int i=start; i<end; i++) {
- if (i >= LINES.length) break;
- if (i > start) longSmsText.append("\n");
- longSmsText.append(LINES[i]);
- }
- if (update > 2) {
- when = System.currentTimeMillis();
- }
- Notification.BigTextStyle bigTextStyle = new Notification.BigTextStyle()
- .bigText(longSmsText);
- Notification bigText = new Notification.Builder(context)
- .setContentTitle("Mike Cleron")
- .setContentIntent(ToastService.getPendingIntent(context, "Clicked on bigText"))
- .setContentText(longSmsText)
- //.setTicker("Mike Cleron: " + longSmsText)
- .setWhen(when)
- .setLargeIcon(getBitmap(context, R.drawable.bucket))
- .setPriority(Notification.PRIORITY_HIGH)
- .setNumber(update)
- .setSmallIcon(R.drawable.stat_notify_talk_text)
- .setStyle(bigTextStyle)
- .setDefaults(Notification.DEFAULT_SOUND)
- .addPerson(personUri)
- .build();
- return bigText;
- }
-
- public static Notification makeUploadNotification(Context context, int progress, long when) {
- Notification.Builder uploadNotification = new Notification.Builder(context)
- .setContentTitle("File Upload")
- .setContentText("foo.txt")
- .setPriority(Notification.PRIORITY_MIN)
- .setContentIntent(ToastService.getPendingIntent(context, "Clicked on Upload"))
- .setWhen(when)
- .setSmallIcon(R.drawable.ic_menu_upload)
- .setProgress(100, Math.min(progress, 100), false);
- return uploadNotification.build();
- }
-
- static SpannableStringBuilder BOLD(CharSequence str) {
- final SpannableStringBuilder ssb = new SpannableStringBuilder(str);
- ssb.setSpan(new StyleSpan(Typeface.BOLD), 0, ssb.length(), 0);
- return ssb;
- }
-
- public static class ToastService extends IntentService {
-
- private static final String TAG = "ToastService";
-
- private static final String ACTION_TOAST = "toast";
-
- private Handler handler;
-
- public ToastService() {
- super(TAG);
- }
- public ToastService(String name) {
- super(name);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- handler = new Handler();
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- Log.v(TAG, "clicked a thing! intent=" + intent.toString());
- if (intent.hasExtra("text")) {
- final String text = intent.getStringExtra("text");
- handler.post(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(ToastService.this, text, Toast.LENGTH_LONG).show();
- Log.v(TAG, "toast " + text);
- }
- });
- }
- }
-
- public static PendingIntent getPendingIntent(Context context, String text) {
- Intent toastIntent = new Intent(context, ToastService.class);
- toastIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- toastIntent.setAction(ACTION_TOAST + ":" + text); // one per toast message
- toastIntent.putExtra("text", text);
- PendingIntent pi = PendingIntent.getService(
- context, 58, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
- }
- }
-
- public static void sleepIfYouCan(int ms) {
- try {
- Thread.sleep(ms);
- } catch (InterruptedException e) {}
- }
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- }
-
- public static String summarize(Notification n) {
- return String.format("<notif title=\"%s\" icon=0x%08x view=%s>",
- n.extras.get(Notification.EXTRA_TITLE),
- n.icon,
- String.valueOf(n.contentView));
- }
-
- public void testCreate() throws Exception {
- ArrayList<Notification> mNotifications = new ArrayList<Notification>();
- NotificationManager noMa =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-
- L("Constructing notifications...");
- if (SHOW_BIG_TEXT) {
- int bigtextId = mNotifications.size();
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = makeBigTextNotification(mContext, 0, bigtextId, System.currentTimeMillis());
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- int uploadId = mNotifications.size();
- long uploadWhen = System.currentTimeMillis();
-
- if (SHOW_PROGRESS) {
- mNotifications.add(makeUploadNotification(mContext, 0, uploadWhen));
- }
-
- if (SHOW_PHONE_CALL) {
- int phoneId = mNotifications.size();
- final PendingIntent fullscreenIntent
- = FullScreenActivity.getPendingIntent(mContext, phoneId);
- final long time = SystemClock.currentThreadTimeMillis();
- Notification phoneCall = new Notification.Builder(mContext)
- .setContentTitle("Incoming call")
- .setContentText("Matias Duarte")
- .setLargeIcon(getBitmap(mContext, R.drawable.matias_hed))
- .setSmallIcon(R.drawable.stat_sys_phone_call)
- .setDefaults(Notification.DEFAULT_SOUND)
- .setPriority(Notification.PRIORITY_MAX)
- .setContentIntent(fullscreenIntent)
- .setFullScreenIntent(fullscreenIntent, true)
- .addAction(R.drawable.ic_dial_action_call, "Answer",
- ToastService.getPendingIntent(mContext, "Clicked on Answer"))
- .addAction(R.drawable.ic_end_call, "Ignore",
- ToastService.getPendingIntent(mContext, "Clicked on Ignore"))
- .setOngoing(true)
- .addPerson(Uri.fromParts("tel", "1 (617) 555-1212", null).toString())
- .build();
- L(" %s: create=%dms", phoneCall.toString(), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(phoneCall);
- }
-
- if (SHOW_STOPWATCH) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Stopwatch PRO")
- .setContentText("Counting up")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Stopwatch"))
- .setSmallIcon(R.drawable.stat_notify_alarm)
- .setUsesChronometer(true)
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_CALENDAR) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("J Planning")
- .setContentText("The Botcave")
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.stat_notify_calendar)
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on calendar event"))
- .setContentInfo("7PM")
- .addAction(R.drawable.stat_notify_snooze, "+10 min",
- ToastService.getPendingIntent(mContext, "snoozed 10 min"))
- .addAction(R.drawable.stat_notify_snooze_longer, "+1 hour",
- ToastService.getPendingIntent(mContext, "snoozed 1 hr"))
- .addAction(R.drawable.stat_notify_email, "Email",
- ToastService.getPendingIntent(mContext,
- "Congratulations, you just destroyed someone's inbox zero"))
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_BIG_PICTURE) {
- BitmapDrawable d =
- (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.romainguy_rockaway);
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Romain Guy")
- .setContentText("I was lucky to find a Canon 5D Mk III at a local Bay Area "
- + "store last week but I had not been able to try it in the field "
- + "until tonight. After a few days of rain the sky finally cleared "
- + "up. Rockaway Beach did not disappoint and I was finally able to "
- + "see what my new camera feels like when shooting landscapes.")
- .setSmallIcon(android.R.drawable.stat_notify_chat)
- .setContentIntent(
- ToastService.getPendingIntent(mContext, "Clicked picture"))
- .setLargeIcon(getBitmap(mContext, R.drawable.romainguy_hed))
- .addAction(R.drawable.add, "Add to Gallery",
- ToastService.getPendingIntent(mContext, "Added"))
- .setStyle(new Notification.BigPictureStyle()
- .bigPicture(d.getBitmap()))
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_INBOX) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("New mail")
- .setContentText("3 new messages")
- .setSubText("example@gmail.com")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Mail"))
- .setSmallIcon(R.drawable.stat_notify_email)
- .setStyle(new Notification.InboxStyle()
- .setSummaryText("example@gmail.com")
- .addLine(BOLD("Alice:").append(" hey there!"))
- .addLine(BOLD("Bob:").append(" hi there!"))
- .addLine(BOLD("Charlie:").append(" Iz IN UR EMAILZ!!"))
- ).build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- if (SHOW_SOCIAL) {
- final long time = SystemClock.currentThreadTimeMillis();
- final Notification n = new Notification.Builder(mContext)
- .setContentTitle("Social Network")
- .setContentText("You were mentioned in a post")
- .setContentInfo("example@gmail.com")
- .setContentIntent(ToastService.getPendingIntent(mContext, "Clicked on Social"))
- .setSmallIcon(android.R.drawable.stat_notify_chat)
- .setPriority(Notification.PRIORITY_LOW)
- .build();
- L(" %s: create=%dms", summarize(n), SystemClock.currentThreadTimeMillis() - time);
- mNotifications.add(n);
- }
-
- L("Posting notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- final int count = 4;
- for (int j=0; j<count; j++) {
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n = mNotifications.get(i);
- noMa.notify(NOTIFICATION_ID + i, n);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: notify=%dms (%d/%d)", summarize(n), time,
- j + 1, count);
- sleepIfYouCan(150);
- }
- }
-
- sleepIfYouCan(1000);
-
- L("Canceling notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- final Notification n = mNotifications.get(i);
- long time = SystemClock.currentThreadTimeMillis();
- noMa.cancel(NOTIFICATION_ID + i);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: cancel=%dms", summarize(n), time);
- }
-
- sleepIfYouCan(500);
-
- L("Parceling notifications...");
- // we want to be able to use this test on older OSes that do not have getOpenAshmemSize
- Method getOpenAshmemSize = null;
- try {
- getOpenAshmemSize = Parcel.class.getMethod("getOpenAshmemSize");
- } catch (NoSuchMethodException ex) {
- }
- for (int i=0; i<mNotifications.size(); i++) {
- Parcel p = Parcel.obtain();
- {
- final Notification n = mNotifications.get(i);
- long time = SystemClock.currentThreadTimeMillis();
- n.writeToParcel(p, 0);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: write parcel=%dms size=%d ashmem=%s",
- summarize(n), time, p.dataPosition(),
- (getOpenAshmemSize != null)
- ? getOpenAshmemSize.invoke(p)
- : "???");
- p.setDataPosition(0);
- }
-
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n2 = Notification.CREATOR.createFromParcel(p);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: parcel read=%dms", summarize(n2), time);
-
- time = SystemClock.currentThreadTimeMillis();
- noMa.notify(NOTIFICATION_ID + i, n2);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: notify=%dms", summarize(n2), time);
- }
-
- sleepIfYouCan(500);
-
- L("Canceling notifications...");
- for (int i=0; i<mNotifications.size(); i++) {
- long time = SystemClock.currentThreadTimeMillis();
- final Notification n = mNotifications.get(i);
- noMa.cancel(NOTIFICATION_ID + i);
- time = SystemClock.currentThreadTimeMillis() - time;
- L(" %s: cancel=%dms", summarize(n), time);
- }
-
-
-// if (SHOW_PROGRESS) {
-// ProgressService.startProgressUpdater(this, uploadId, uploadWhen, 0);
-// }
- }
-
- public static class FullScreenActivity extends Activity {
- public static final String EXTRA_ID = "id";
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.full_screen);
- final Intent intent = getIntent();
- if (intent != null && intent.hasExtra(EXTRA_ID)) {
- final int id = intent.getIntExtra(EXTRA_ID, -1);
- if (id >= 0) {
- NotificationManager noMa =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- noMa.cancel(NOTIFICATION_ID + id);
- }
- }
- }
-
- public void dismiss(View v) {
- finish();
- }
-
- public static PendingIntent getPendingIntent(Context context, int id) {
- Intent fullScreenIntent = new Intent(context, FullScreenActivity.class);
- fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- fullScreenIntent.putExtra(EXTRA_ID, id);
- PendingIntent pi = PendingIntent.getActivity(
- context, 22, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- return pi;
- }
- }
-}
-
diff --git a/tests/permission/OWNERS b/tests/permission/OWNERS
index 999ea0e62a0a..2fe78c5a7092 100644
--- a/tests/permission/OWNERS
+++ b/tests/permission/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
include /core/java/android/permission/OWNERS
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
index 421ceb797c15..07b733830bd3 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,7 +50,7 @@ import org.junit.runners.JUnit4;
public class VibratorManagerServicePermissionTest {
private static final String PACKAGE_NAME = "com.android.framework.permission.tests";
- private static final int DISPLAY_ID = 1;
+ private static final int DEVICE_ID = 1;
private static final CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -107,7 +107,7 @@ public class VibratorManagerServicePermissionTest {
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -117,7 +117,7 @@ public class VibratorManagerServicePermissionTest {
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.myUid(), DEVICE_ID, PACKAGE_NAME, EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -127,7 +127,7 @@ public class VibratorManagerServicePermissionTest {
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
@@ -137,7 +137,7 @@ public class VibratorManagerServicePermissionTest {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ mVibratorService.vibrate(Process.SYSTEM_UID, DEVICE_ID, "android", EFFECT, ATTRS,
"testVibrate",
new Binder());
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 06cbeb5368a5..330bc84d7a76 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -124,7 +124,7 @@ public class WindowManagerPermissionTests extends TestCase {
@SmallTest
public void testSET_ORIENTATION() {
try {
- mWm.freezeRotation(-1);
+ mWm.freezeRotation(/* rotation= */ -1, /* caller= */ "WindowManagerPermissionTests");
fail("IWindowManager.freezeRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -134,7 +134,7 @@ public class WindowManagerPermissionTests extends TestCase {
}
try {
- mWm.thawRotation();
+ mWm.thawRotation(/* called= */ "WindowManagerPermissionTests");
fail("IWindowManager.thawRotation did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
index 6a6ab00e33ff..a43e1b091d41 100644
--- a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
+++ b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
@@ -21,15 +21,39 @@ import java.util.Map;
public class TestableFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver {
private Map<String, Boolean> mOverrides = new HashMap<>();
+ private Map<String, Integer> mOverridesInt = new HashMap<>();
+ private Map<String, String> mOverridesString = new HashMap<>();
@Override
public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) {
return mOverrides.getOrDefault(flag.mSysPropKey, flag.mDefaultValue);
}
+ @Override
+ public int getIntValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesInt.getOrDefault(flag.mSysPropKey, flag.mDefaultIntValue);
+ }
+
+ @Override
+ public String getStringValue(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverridesString.getOrDefault(flag.mSysPropKey, flag.mDefaultStringValue);
+ }
+
public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
boolean isEnabled) {
mOverrides.put(flag.mSysPropKey, isEnabled);
return this;
}
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ int value) {
+ mOverridesInt.put(flag.mSysPropKey, value);
+ return this;
+ }
+
+ public TestableFlagResolver setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag,
+ String value) {
+ mOverridesString.put(flag.mSysPropKey, value);
+ return this;
+ }
}
diff --git a/tests/utils/testutils/TEST_MAPPING b/tests/utils/testutils/TEST_MAPPING
new file mode 100644
index 000000000000..52fd5a8779ad
--- /dev/null
+++ b/tests/utils/testutils/TEST_MAPPING
@@ -0,0 +1,29 @@
+{
+ "presubmit": [
+ {
+ "name": "frameworks-base-testutils-tests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "frameworks-base-testutils-tests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
new file mode 100644
index 000000000000..7012daf586d8
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/LocalServiceKeeperRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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.util.test;
+
+import com.android.server.LocalServices;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JUnit Rule helps override /restore {@link LocalServices} state.
+ */
+public class LocalServiceKeeperRule implements TestRule {
+
+ private final Map<Class<?>, Object> mOverriddenServices = new HashMap<>();
+ private final List<Class<?>> mAddedServices = new ArrayList<>();
+
+ private volatile boolean mRuleApplied = false;
+
+ /**
+ * Overrides service in LocalServices. Service will be restored to original after test run.
+ */
+ public <T> void overrideLocalService(Class<T> type, T service) {
+ if (!mRuleApplied) {
+ throw new IllegalStateException("Can't override service without applying rule");
+ }
+ if (mOverriddenServices.containsKey(type) || mAddedServices.contains(type)) {
+ throw new IllegalArgumentException("Type already overridden: " + type);
+ }
+
+ T currentService = LocalServices.getService(type);
+ if (currentService != null) {
+ mOverriddenServices.put(type, currentService);
+ LocalServices.removeServiceForTest(type);
+ } else {
+ mAddedServices.add(type);
+ }
+ LocalServices.addService(type, service);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ public void evaluate() throws Throwable {
+ try {
+ mRuleApplied = true;
+ base.evaluate();
+ } finally {
+ mRuleApplied = false;
+ mAddedServices.forEach(LocalServices::removeServiceForTest);
+ mOverriddenServices.forEach((clazz, service) -> {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService((Class) clazz, service);
+ });
+ mAddedServices.clear();
+ mOverriddenServices.clear();
+ }
+ }
+ };
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/server/accessibility/TEST_MAPPING b/tests/utils/testutils/java/com/android/server/accessibility/TEST_MAPPING
new file mode 100644
index 000000000000..1c673996b550
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/server/accessibility/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/base/services/accessibility/TEST_MAPPING"
+ }
+ ]
+}
diff --git a/tests/utils/testutils/tests/Android.bp b/tests/utils/testutils/tests/Android.bp
new file mode 100644
index 000000000000..b901b1802383
--- /dev/null
+++ b/tests/utils/testutils/tests/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2023 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "frameworks-base-testutils-tests",
+
+ srcs: [
+ "src/**/*.java",
+ ],
+
+ static_libs: [
+ "junit",
+ "hamcrest-library",
+ "androidx.test.runner",
+ "mockito-target-minus-junit4",
+ "frameworks-base-testutils",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+
+ certificate: "platform",
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/utils/testutils/tests/AndroidManifest.xml
index 3b5bfa986119..9e7e2752f1d5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
+++ b/tests/utils/testutils/tests/AndroidManifest.xml
@@ -1,4 +1,5 @@
-/*
+<?xml version="1.0" encoding="utf-8"?>
+<!--
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,18 +13,17 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- */
+ -->
-package com.android.server.wm.flicker.ime
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.frameworks.tests.utils.testutils.tests">
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.LegacyFlickerTest
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ </application>
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : CloseImeOnGoHomeTest(flicker)
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.frameworks.tests.utils.testutils.tests"
+ android:label="Test Utils Tests"/>
+</manifest>
diff --git a/tests/utils/testutils/java/android/os/test/TestLooperTest.java b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
index c72e20ccc5ba..c72e20ccc5ba 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooperTest.java
+++ b/tests/utils/testutils/tests/src/android/os/test/TestLooperTest.java
diff --git a/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
new file mode 100644
index 000000000000..12f2fae0a581
--- /dev/null
+++ b/tests/utils/testutils/tests/src/com/android/internal/util/test/LocalServiceKeeperRuleTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 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.util.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+@SmallTest
+public class LocalServiceKeeperRuleTest {
+
+ private final Description mDescription = Description.createSuiteDescription("Description");
+
+ LocalServiceKeeperRule mRule = new LocalServiceKeeperRule();
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(TestService.class);
+ }
+
+ @Test
+ public void testFailedIfCalledOutsideOfTheRule() {
+ TestService service = new TestService() {};
+
+ assertThrows(IllegalStateException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ }
+
+ @Test
+ public void testSetsLocalServiceIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertEquals(service, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testOverridesLocalServiceIfPresent() throws Throwable {
+ TestService service = new TestService() {};
+ LocalServices.addService(TestService.class, service);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, overriddenService);
+ assertEquals(overriddenService, LocalServices.getService(TestService.class));
+ });
+ }
+
+ @Test
+ public void testDoesNotAllowToOverrideSameServiceTwice() throws Throwable {
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> {
+ mRule.overrideLocalService(TestService.class, service);
+ assertThrows(IllegalArgumentException.class,
+ () -> mRule.overrideLocalService(TestService.class, service));
+ });
+ }
+
+ @Test
+ public void testRestroresLocalServiceAfterTestIfPresent() throws Throwable {
+ TestService expectedService = new TestService() {};
+ LocalServices.addService(TestService.class, expectedService);
+ TestService overriddenService = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, overriddenService));
+
+ assertEquals(expectedService, LocalServices.getService(TestService.class));
+ }
+
+ @Test
+ public void testRemovesLocalServiceAfterTestIfNotPresent() throws Throwable {
+ LocalServices.removeServiceForTest(TestService.class);
+ TestService service = new TestService() {};
+
+ runInRuleApply(() -> mRule.overrideLocalService(TestService.class, service));
+
+ assertNull(LocalServices.getService(TestService.class));
+ }
+
+ private void runInRuleApply(Runnable runnable) throws Throwable {
+ Statement testStatement = new Statement() {
+ @Override
+ public void evaluate() {
+ runnable.run();
+ }
+ };
+ mRule.apply(testStatement, mDescription).evaluate();
+ }
+
+ interface TestService {
+ }
+}
diff --git a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java b/tests/utils/testutils/tests/src/com/android/test/filters/SelectTestTests.java
index df18985f77bf..df18985f77bf 100644
--- a/tests/utils/testutils/java/com/android/test/filters/SelectTestTests.java
+++ b/tests/utils/testutils/tests/src/com/android/test/filters/SelectTestTests.java