summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml6
-rw-r--r--tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java7
-rw-r--r--tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java12
-rw-r--r--tests/ActivityManagerPerfTests/utils/Android.bp2
-rw-r--r--tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java2
-rw-r--r--tests/ApkVerityTest/AndroidTest.xml7
-rw-r--r--tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java44
-rw-r--r--tests/AttestationVerificationTest/assets/test_owned_by_system_certs.pem81
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt29
-rw-r--r--tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java6
-rw-r--r--tests/BinaryTransparencyHostTest/Android.bp43
-rw-r--r--tests/BinaryTransparencyHostTest/AndroidTest.xml31
-rw-r--r--tests/BinaryTransparencyHostTest/OWNERS1
-rw-r--r--tests/BinaryTransparencyHostTest/src/android/transparency/test/BaseInstallMultiple.java140
-rw-r--r--tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java213
-rw-r--r--tests/BinaryTransparencyHostTest/test-app/Android.bp40
-rw-r--r--[-rwxr-xr-x]tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml (renamed from tests/componentalias/AndroidManifest.xml)10
-rw-r--r--tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java146
-rw-r--r--tests/Camera2Tests/CameraToo/Android.bp28
-rw-r--r--tests/Camera2Tests/CameraToo/Android.mk26
-rw-r--r--tests/CanvasCompare/Android.bp63
-rw-r--r--tests/CanvasCompare/AndroidManifest.xml48
-rw-r--r--tests/CanvasCompare/OWNERS1
-rw-r--r--tests/CanvasCompare/res/drawable/sunset1.jpgbin28050 -> 0 bytes
-rw-r--r--tests/CanvasCompare/res/layout/automatic_layout.xml38
-rw-r--r--tests/CanvasCompare/res/layout/manual_layout.xml119
-rw-r--r--tests/CanvasCompare/res/values/strings.xml31
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java310
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java122
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java532
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java187
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java56
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java212
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java136
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java46
-rw-r--r--tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript61
-rw-r--r--tests/ChoreographerTests/Android.bp49
-rw-r--r--tests/ChoreographerTests/AndroidManifest.xml33
-rw-r--r--tests/ChoreographerTests/AndroidTest.xml30
-rw-r--r--tests/ChoreographerTests/OWNERS2
-rw-r--r--tests/ChoreographerTests/TEST_MAPPING7
-rw-r--r--tests/ChoreographerTests/jni/Android.bp44
-rw-r--r--tests/ChoreographerTests/jni/ChoreographerTestsJniOnLoad.cpp33
-rw-r--r--tests/ChoreographerTests/jni/android_view_tests_ChoreographerNativeTest.cpp167
-rw-r--r--tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerNativeTest.java179
-rw-r--r--tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java481
-rw-r--r--tests/ChoreographerTests/src/main/java/android/view/choreographertests/GraphicsActivity.java28
-rw-r--r--tests/ChoreographerTests/src/main/res/layout/activity_attached_choreographer.xml31
-rw-r--r--tests/ChoreographerTests/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--tests/ChoreographerTests/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--tests/ChoreographerTests/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--tests/ChoreographerTests/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--tests/ChoreographerTests/src/main/res/values/strings.xml (renamed from tests/CanvasCompare/res/values/values.xml)11
-rw-r--r--tests/Codegen/Android.bp8
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java14
-rw-r--r--tests/EnforcePermission/Android.bp22
-rw-r--r--tests/EnforcePermission/OWNERS3
-rw-r--r--tests/EnforcePermission/TEST_MAPPING7
-rw-r--r--tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl (renamed from tests/componentalias/src/android/content/componentalias/tests/b/Target01.java)12
-rw-r--r--tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl34
-rw-r--r--tests/EnforcePermission/service-app/Android.bp32
-rw-r--r--tests/EnforcePermission/service-app/AndroidManifest.xml27
-rw-r--r--tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java48
-rw-r--r--tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.java119
-rw-r--r--tests/EnforcePermission/test-app/Android.bp38
-rw-r--r--tests/EnforcePermission/test-app/AndroidManifest.xml (renamed from tests/SoundTriggerTests/AndroidManifest.xml)21
-rw-r--r--tests/EnforcePermission/test-app/AndroidTest.xml28
-rw-r--r--tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java129
-rw-r--r--tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java2
-rw-r--r--tests/FlickerTests/Android.bp27
-rw-r--r--tests/FlickerTests/AndroidManifest.xml5
-rw-r--r--tests/FlickerTests/AndroidTest.xml18
-rw-r--r--tests/FlickerTests/libs/window-extensions-release.aarbin0 -> 21364 bytes
-rw-r--r--tests/FlickerTests/res/anim/show_hide_show_3000ms.xml31
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt209
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt294
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt32
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt129
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt122
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt73
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt46
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt85
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt167
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt120
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt27
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt90
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt15
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt115
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java279
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt116
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt83
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt40
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt147
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt (renamed from tests/componentalias/src/android/content/componentalias/tests/ComponentAliasTestCommon.java)21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt59
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt68
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt70
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt38
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt43
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt426
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt22
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt182
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt190
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt136
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt110
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt)37
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt124
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt29
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt104
-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/CloseImeShownOnAppStartToAppOnPressBackTest.kt98
-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/CloseImeToAppOnPressBackTest.kt122
-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/CloseImeToHomeOnFinishActivityTest.kt99
-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/CloseImeWindowToAppTest.kt166
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt176
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt132
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt156
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt131
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt122
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt164
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt117
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt238
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt252
-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/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt143
-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/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt149
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt43
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt134
-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/ShowImeOnAppStartWhenLaunchingAppTest.kt137
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt88
-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/ShowImeWhileDismissingThemedPopupDialogTest.kt98
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt48
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt217
-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/ime/SwitchImeWindowsFromGestureNavTest.kt205
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt110
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt73
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt219
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt92
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt52
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt63
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt97
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt142
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt118
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt131
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt81
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt (renamed from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt)42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt178
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt45
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt136
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt47
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt188
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt239
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt96
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt46
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt167
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt262
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt278
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt268
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt42
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt276
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt45
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt114
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt44
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt140
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt229
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt50
-rw-r--r--tests/FlickerTests/test-apps/Android.bp0
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/Android.bp18
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml283
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml25
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.pngbin0 -> 1966 bytes
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml31
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml48
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml24
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml40
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml31
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml (renamed from tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml)0
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml34
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml48
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml141
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml33
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml32
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml21
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml25
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml37
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml36
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml24
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/strings.xml19
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml20
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml17
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/xml/shortcuts.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java34
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java102
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java30
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java30
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java30
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java241
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java (renamed from tests/componentalias/src/android/content/componentalias/tests/b/Target00.java)9
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java53
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java37
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java77
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java173
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java35
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java77
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java16
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java3
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java82
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java (renamed from tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java)6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java31
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java134
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java36
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java49
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java (renamed from tests/componentalias/src/android/content/componentalias/tests/b/Target02.java)14
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java8
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java307
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java29
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java28
-rw-r--r--tests/FrameworkPerf/AndroidManifest.xml10
-rw-r--r--tests/HandwritingIme/OWNERS3
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java116
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java328
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java33
-rw-r--r--tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java2
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml43
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java315
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java86
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java125
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java167
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java168
-rw-r--r--tests/Input/Android.bp6
-rw-r--r--tests/Input/src/com/android/test/input/AnrTest.kt11
-rw-r--r--tests/Input/src/com/android/test/input/InputDeviceTest.java47
-rw-r--r--tests/Input/src/com/android/test/input/MotionPredictorTest.kt135
-rw-r--r--tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt3
-rw-r--r--tests/InputMethodStressTest/Android.bp6
-rw-r--r--tests/InputMethodStressTest/AndroidManifest.xml7
-rw-r--r--tests/InputMethodStressTest/AndroidTest.xml2
-rw-r--r--tests/InputMethodStressTest/TEST_MAPPING2
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java479
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java92
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java601
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java153
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java441
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java25
-rw-r--r--tests/Internal/src/android/app/WallpaperColorsTest.java66
-rw-r--r--tests/Internal/src/android/service/wallpaper/OWNERS4
-rw-r--r--tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java13
-rw-r--r--tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java267
-rw-r--r--tests/Internal/src/com/android/internal/app/LocaleStoreTest.java263
-rw-r--r--tests/Internal/src/com/android/internal/app/OWNERS2
-rw-r--r--tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java145
-rw-r--r--tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java10
-rw-r--r--tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java5
-rw-r--r--tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java2
-rw-r--r--tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java1
-rw-r--r--tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java70
-rw-r--r--tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java14
-rw-r--r--tests/MidiTests/Android.bp40
-rw-r--r--tests/MidiTests/AndroidManifest.xml30
-rw-r--r--tests/MidiTests/AndroidTest.xml30
-rw-r--r--tests/MidiTests/OWNERS1
-rw-r--r--tests/MidiTests/TEST_MAPPING7
-rw-r--r--tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java342
-rw-r--r--tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java4
-rw-r--r--tests/MotionPrediction/Android.bp30
-rw-r--r--tests/MotionPrediction/AndroidManifest.xml34
-rw-r--r--tests/MotionPrediction/OWNERS1
-rw-r--r--tests/MotionPrediction/res/layout/activity_main.xml32
-rw-r--r--tests/MotionPrediction/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3418 bytes
-rw-r--r--tests/MotionPrediction/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2206 bytes
-rw-r--r--tests/MotionPrediction/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4842 bytes
-rw-r--r--tests/MotionPrediction/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 7718 bytes
-rw-r--r--tests/MotionPrediction/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 10486 bytes
-rw-r--r--tests/MotionPrediction/res/values-w820dp/dimens.xml20
-rw-r--r--tests/MotionPrediction/res/values/colors.xml20
-rw-r--r--tests/MotionPrediction/res/values/dimens.xml19
-rw-r--r--tests/MotionPrediction/res/values/strings.xml17
-rw-r--r--tests/MotionPrediction/res/values/styles.xml23
-rw-r--r--tests/MotionPrediction/src/test/motionprediction/DrawingView.kt106
-rw-r--r--tests/MotionPrediction/src/test/motionprediction/MainActivity.kt29
-rw-r--r--tests/OdmApps/Android.bp4
-rw-r--r--tests/OneMedia/AndroidManifest.xml4
-rw-r--r--tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java50
-rw-r--r--tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java16
-rw-r--r--tests/RollbackTest/Android.bp3
-rw-r--r--tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java18
-rw-r--r--tests/RollbackTest/SampleRollbackApp/Android.bp33
-rw-r--r--tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml31
-rw-r--r--tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml37
-rw-r--r--tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml41
-rw-r--r--tests/RollbackTest/SampleRollbackApp/res/values/strings.xml20
-rw-r--r--tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java161
-rw-r--r--tests/SharedLibraryLoadingTest/AndroidTest.xml1
-rw-r--r--tests/SilkFX/Android.bp8
-rw-r--r--tests/SilkFX/AndroidManifest.xml15
-rw-r--r--tests/SilkFX/assets/gainmaps/city_night.jpgbin0 -> 2995396 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/desert_palms.jpgbin0 -> 3859886 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/desert_sunset.jpgbin0 -> 2577663 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/desert_wanda.jpgbin0 -> 1925203 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/fountain_night.jpgbin0 -> 3579758 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/grand_canyon.jpgbin0 -> 4714624 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/lamps.jpgbin0 -> 1645109 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/mountain_lake.jpgbin0 -> 3242535 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/mountains.jpgbin0 -> 4936427 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/sunflower.jpgbin0 -> 2525581 bytes
-rw-r--r--tests/SilkFX/assets/gainmaps/train_station_night.jpgbin0 -> 3281254 bytes
-rw-r--r--tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml23
-rw-r--r--tests/SilkFX/res/drawable-nodpi/dark_gradient.xml23
-rw-r--r--tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml23
-rw-r--r--tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml23
-rw-r--r--tests/SilkFX/res/drawable-nodpi/light_gradient.xml23
-rw-r--r--tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml23
-rw-r--r--tests/SilkFX/res/layout/color_grid.xml20
-rw-r--r--tests/SilkFX/res/layout/common_base.xml12
-rw-r--r--tests/SilkFX/res/layout/gainmap_decode_test.xml115
-rw-r--r--tests/SilkFX/res/layout/gainmap_image.xml91
-rw-r--r--tests/SilkFX/res/layout/gainmap_metadata.xml264
-rw-r--r--tests/SilkFX/res/layout/gainmap_transform_test.xml122
-rw-r--r--tests/SilkFX/res/layout/gradient_sweep.xml53
-rw-r--r--tests/SilkFX/res/layout/hdr_image_viewer.xml26
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/Main.kt12
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt10
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt51
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt3
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt123
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt77
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt130
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt205
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt284
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt116
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt4
-rw-r--r--tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java1
-rw-r--r--tests/SoundTriggerTestApp/OWNERS2
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java2
-rw-r--r--tests/SoundTriggerTests/Android.mk39
-rw-r--r--tests/SoundTriggerTests/OWNERS1
-rw-r--r--tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java371
-rw-r--r--tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java293
-rw-r--r--tests/StagedInstallTest/Android.bp3
-rw-r--r--tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java92
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java108
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java4
-rw-r--r--tests/SurfaceControlViewHostTest/Android.bp5
-rw-r--r--tests/SurfaceControlViewHostTest/AndroidManifest.xml10
-rw-r--r--tests/SurfaceControlViewHostTest/OWNERS1
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java109
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl26
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindowCallback.aidl (renamed from tests/componentalias/src/android/content/componentalias/tests/b/Target03.java)12
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java215
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt12
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt40
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt29
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt13
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt3
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt8
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt19
-rw-r--r--tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt8
-rw-r--r--tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java26
-rw-r--r--tests/SystemMemoryTest/host/Android.bp3
-rw-r--r--tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt28
-rw-r--r--tests/TelephonyCommonTests/Android.bp2
-rw-r--r--tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java77
-rw-r--r--tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java77
-rw-r--r--tests/TouchLatency/Android.bp1
-rw-r--r--tests/TouchLatency/OWNERS2
-rw-r--r--tests/TouchLatency/app/build.gradle12
-rw-r--r--tests/TouchLatency/app/src/main/AndroidManifest.xml4
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java322
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java219
-rw-r--r--tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml16
-rw-r--r--tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml13
-rw-r--r--tests/TouchLatency/app/src/main/res/values/strings.xml2
-rw-r--r--tests/TouchLatency/app/src/main/res/values/styles.xml2
-rw-r--r--tests/TouchLatency/build.gradle2
-rw-r--r--tests/TouchLatency/gradle.properties3
-rw-r--r--tests/TouchLatency/gradle/wrapper/gradle-wrapper.jarbin54417 -> 59536 bytes
-rw-r--r--tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xtests/TouchLatency/gradlew282
-rw-r--r--tests/TouchLatency/gradlew.bat43
-rw-r--r--tests/TrustTests/Android.bp2
-rw-r--r--tests/TrustTests/AndroidManifest.xml10
-rw-r--r--tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt131
-rw-r--r--tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt10
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt10
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt45
-rw-r--r--tests/UiBench/Android.bp1
-rw-r--r--tests/UiBench/AndroidManifest.xml1
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp1
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java29
-rw-r--r--tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java16
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java28
-rw-r--r--tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java407
-rw-r--r--tests/VectorDrawableTest/OWNERS3
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java4
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java5
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java5
-rw-r--r--tests/WindowAnimationJank/Android.bp2
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java11
-rw-r--r--tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java3
-rw-r--r--tests/WindowInsetsTests/res/layout/controller_activity.xml2
-rw-r--r--tests/WindowInsetsTests/res/values/strings.xml2
-rw-r--r--tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java7
-rw-r--r--tests/backup/Android.bp54
-rw-r--r--tests/backup/Android.mk53
-rw-r--r--tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java902
-rw-r--r--tests/componentalias/Android.bp54
-rwxr-xr-xtests/componentalias/AndroidManifest_main.xml28
-rw-r--r--tests/componentalias/AndroidManifest_service_aliases.xml81
-rw-r--r--tests/componentalias/AndroidManifest_service_targets.xml57
-rwxr-xr-xtests/componentalias/AndroidManifest_sub1.xml28
-rwxr-xr-xtests/componentalias/AndroidManifest_sub2.xml28
-rw-r--r--tests/componentalias/AndroidTest-template.xml38
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java103
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java110
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java63
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java215
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java60
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java315
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java44
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/Target04.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/BaseService.java70
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/Target00.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/Target01.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/Target02.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/Target03.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/s/Target04.java19
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java13
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java10
-rw-r--r--tests/testables/src/android/testing/TestWithLooperRule.java161
-rw-r--r--tests/testables/src/android/testing/TestableContext.java82
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java44
-rw-r--r--tests/testables/src/android/testing/TestableResources.java38
-rw-r--r--tests/testables/src/android/testing/TestableSettingsProvider.java17
-rw-r--r--tests/testables/src/com/android/internal/config/sysui/OWNERS1
-rw-r--r--tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java33
-rw-r--r--tests/testables/tests/Android.bp5
-rw-r--r--tests/testables/tests/com/android/internal/config/sysui/OWNERS1
-rw-r--r--tests/testables/tests/src/android/testing/TestableLooperTest.java65
-rw-r--r--tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java77
-rw-r--r--tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java64
-rw-r--r--tests/utils/testutils/java/android/os/test/OWNERS1
-rw-r--r--tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java29
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java1
-rw-r--r--tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java77
-rw-r--r--tests/vcn/java/android/net/vcn/VcnConfigTest.java91
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java103
-rw-r--r--tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java34
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java45
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java371
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java82
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java172
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java74
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java38
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java123
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java380
482 files changed, 23802 insertions, 10969 deletions
diff --git a/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml
index 6bdeea06053f..02d03bbfdb1e 100644
--- a/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/stub-app/AndroidManifest.xml
@@ -19,6 +19,12 @@
package="com.android.stubs.am">
<uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
+ <queries>
+ <intent>
+ <action android:name="com.android.stubs.am.ACTION_BROADCAST_TEST"/>
+ </intent>
+ </queries>
+
<application android:label="Android TestCase">
<provider android:authorities="@string/authority"
android:name=".TestContentProvider"
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
index daff76f4f522..5d4a4efecf83 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BasePerfTest.java
@@ -61,10 +61,11 @@ public class BasePerfTest {
return intent;
}
- protected Intent createBroadcastIntent(String action) {
+ protected Intent createFgBroadcastIntent(String action) {
final Intent intent = new Intent(action);
- intent.addFlags(
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
putTimeReceiverBinderExtra(intent);
return intent;
}
diff --git a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
index bc528d4d4fb7..6a535759c699 100644
--- a/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
+++ b/tests/ActivityManagerPerfTests/tests/src/com/android/frameworks/perftests/am/tests/BroadcastPerfTest.java
@@ -30,11 +30,11 @@ import org.junit.runner.RunWith;
@LargeTest
public class BroadcastPerfTest extends BasePerfTest {
@Test
- public void manifestBroadcastRunning() {
+ public void manifestFgBroadcastRunning() {
runPerfFunction(() -> {
startTargetPackage();
- final Intent intent = createBroadcastIntent(
+ final Intent intent = createFgBroadcastIntent(
Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
final long startTime = System.nanoTime();
@@ -48,9 +48,9 @@ public class BroadcastPerfTest extends BasePerfTest {
}
@Test
- public void manifestBroadcastNotRunning() {
+ public void manifestFgBroadcastNotRunning() {
runPerfFunction(() -> {
- final Intent intent = createBroadcastIntent(
+ final Intent intent = createFgBroadcastIntent(
Constants.ACTION_BROADCAST_MANIFEST_RECEIVE);
final long startTime = System.nanoTime();
@@ -64,11 +64,11 @@ public class BroadcastPerfTest extends BasePerfTest {
}
@Test
- public void registeredBroadcast() {
+ public void registeredFgBroadcast() {
runPerfFunction(() -> {
startTargetPackage();
- final Intent intent = createBroadcastIntent(
+ final Intent intent = createFgBroadcastIntent(
Constants.ACTION_BROADCAST_REGISTERED_RECEIVE);
final long startTime = System.nanoTime();
diff --git a/tests/ActivityManagerPerfTests/utils/Android.bp b/tests/ActivityManagerPerfTests/utils/Android.bp
index 99c43c8d8fca..5902c1cbda15 100644
--- a/tests/ActivityManagerPerfTests/utils/Android.bp
+++ b/tests/ActivityManagerPerfTests/utils/Android.bp
@@ -32,6 +32,6 @@ java_test {
static_libs: [
"androidx.test.rules",
"junit",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
}
diff --git a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
index fc787bafa93a..9bd94f2a9a1e 100644
--- a/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
+++ b/tests/ActivityManagerPerfTests/utils/src/com/android/frameworks/perftests/am/util/Utils.java
@@ -19,10 +19,10 @@ package com.android.frameworks.perftests.am.util;
import android.content.Intent;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.support.test.uiautomator.UiDevice;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import java.io.IOException;
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 39b75cc27acb..4487cefb4f9c 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -16,6 +16,11 @@
<configuration description="APK fs-verity integration/regression test">
<option name="test-suite-tag" value="apct" />
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController">
+ <!-- fs-verity is required since R/30 -->
+ <option name="vsr-min-api-level" value="30" />
+ </object>
+
<!-- This test requires root to write against block device. -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
@@ -43,6 +48,8 @@
<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" />
</test>
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index d96005b8a71a..591ffeb39721 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -25,7 +25,6 @@ import static org.junit.Assert.fail;
import android.platform.test.annotations.RootPermissionTest;
import com.android.blockdevicewriter.BlockDeviceWriter;
-import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
@@ -36,7 +35,6 @@ import com.android.tradefed.util.CommandStatus;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,6 +42,7 @@ 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.
@@ -90,10 +89,6 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
/** Only 4K page is supported by fs-verity currently. */
private static final int FSVERITY_PAGE_SIZE = 4096;
- @Rule
- public final AddFsVerityCertRule mAddFsVerityCertRule =
- new AddFsVerityCertRule(this, CERT_PATH);
-
private ITestDevice mDevice;
private boolean mDmRequireFsVerity;
@@ -103,11 +98,13 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
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);
}
@@ -469,10 +466,10 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
break;
}
try {
- CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath));
+ String openFiles = expectRemoteCommandToSucceed("lsof " + apkPath);
+ CLog.d("lsof: " + openFiles);
Thread.sleep(1000);
- String pid = expectRemoteCommandToSucceed("pidof system_server");
- mDevice.executeShellV2Command("kill -10 " + pid); // force GC
+ forceGCOnOpenFilesProcess(getOpenFilesPIDs(openFiles));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
@@ -482,6 +479,35 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
}
}
+ /**
+ * 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("/"));
diff --git a/tests/AttestationVerificationTest/assets/test_owned_by_system_certs.pem b/tests/AttestationVerificationTest/assets/test_owned_by_system_certs.pem
new file mode 100644
index 000000000000..34d55fffc6bf
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_owned_by_system_certs.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIIClzCCAjygAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQMDANURUUxKTAn
+BgNVBAUTIDRiY2Q3MzM5MjZmZDkwYjhlMDE1ZDczYmIxYmE0MjZhMCAXDTIzMDIw
+OTAwMDQwMloYDzIyOTYxMTI0MDAwNDAyWjAfMR0wGwYDVQQDExRBbmRyb2lkIEtl
+eXN0b3JlIEtleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAxz70D0rX31tqv+
+mMjyet+KlfVF4h5zJeHVP6BPtqP9AM/l0KQEuttYEKbtmw4k/phS9hdjHoiitUTO
+7gD5gRqjggFLMIIBRzAOBgNVHQ8BAf8EBAMCB4AwggEzBgorBgEEAdZ5AgERBIIB
+IzCCAR8CAgDICgEBAgIAyAoBAQQRYWN0aXZlVW5sb2NrVmFsaWQEADBQv4MQCAIG
+AYYzfH33v4MRCAIGCWHbnf33v4MSCAIGCWHbnf33v4U9CAIGAYYzfICnv4VFHAQa
+MBgxFDASBA1BbmRyb2lkU3lzdGVtAgEBMQAwgaehCDEGAgECAgEDogMCAQOjBAIC
+AQClCDEGAgEEAgEGqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAoBAgQguYuTO2l0Dwq5FwpSl+Selr+Y
+ky0NvCPbBXFMqRVWStW/hUEFAgMB+9C/hUIFAgMDFj+/hU4GAgQBNLChv4VPBgIE
+ATSwoTAKBggqhkjOPQQDAgNJADBGAiEA9+6Y5LEvdxER46O3V+2H4MYn1ILLJk56
+Uo5uGZqbIfECIQDtITu0l4fKeTVE3sQo50oFd4iCVKVp62PlpTEJ+D1hOQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8zCCAXmgAwIBAgIQD9aWSuFM+VgqbBcSDskVWjAKBggqhkjOPQQDAjA5MQww
+CgYDVQQMDANURUUxKTAnBgNVBAUTIDRiODI4ZTI2MjM2YjRmMGJiNjIwZWRiZjI4
+MTRiMmQyMB4XDTIxMDExMzIwNTkxOVoXDTMxMDExMTIwNTkxOVowOTEMMAoGA1UE
+DAwDVEVFMSkwJwYDVQQFEyA0YmNkNzMzOTI2ZmQ5MGI4ZTAxNWQ3M2JiMWJhNDI2
+YTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKhknO2s1yuEK9NXTneVzwHXVL9N
++AyjLIakshRvMupH341gZNI8H9nuGomXhfPLH4igCB50IIdpjZyUe87DWrmjYzBh
+MB0GA1UdDgQWBBSVB/mrPxEP0okhRNeyAxgtM1KDlzAfBgNVHSMEGDAWgBRGO8we
+F/IGW5HwMQUf5yM8ZmQ/JjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIC
+BDAKBggqhkjOPQQDAgNoADBlAjEA0+6jQMnBNqmzHGRTjrq6bC5PHlF/nN4FOLt3
+a8HhiiXAKddnq38PBI5JBM/+kT7jAjBMpt56pMdwDWag+c+FCB1wCtRvIVic6ATU
+EvY+ZsuRQ0d1ZGvfO3s79j9T/xv7B7E=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAXugAwIBAgIQBMTs9wahoh5aI97uDtkVIjANBgkqhkiG9w0BAQsFADAb
+MRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MB4XDTIxMDExMzIwNTgyM1oXDTMx
+MDExMTIwNTgyM1owOTEMMAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA0YjgyOGUyNjIz
+NmI0ZjBiYjYyMGVkYmYyODE0YjJkMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJTy
+ijqyb9Ay9rys3DDQgn2Lr8n/NDzKmbmITHWWrnbc2POKyCBzcBXo597ewSyLgQcp
+CKSW7R2vRzTWvxFHVNRdEM1H3k4OKhQS8VpTVeHIlGsN37G6jXJJpFhHOW40uqNj
+MGEwHQYDVR0OBBYEFEY7zB4X8gZbkfAxBR/nIzxmZD8mMB8GA1UdIwQYMBaAFDZh
+4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBFoMIAhtEFxu5YUKD7WJj4X8nTRAK7W1Li
+X3AssR769CyrNO5OttkU+5LQb8EGyGs90OhTy/eA7m1sPAh4mMHV5yL+td/Sdg2j
+fZ2ZBayeZuteihaLwB9SpHKUPR3+VDMPBNevUWLpjiwRfbNzb1A40LlRIsfFFs2s
+I33WVcpEH5KAj5ci7UtRIF8ryw3FyFNsbHqvdVf1Wet4JosIhnbZuOruB4qUq8oW
+ZOi6nXBKnY+ebdZusPRUE/6h8pDS5xZrN/is2HmFDXuEjuMibw1bZirQ5cygn+fQ
+DUjjK+4/k/IlfoLL8A07XdrtlYynMZm88FzB5tkwdwXkJTW8l+vZv6fhXv34go6N
+MoZNpLZZaoJDckvpPP4oXdpOLtUJyaNoMWPtn97q8eQklkHK1e8SSfYN5GShCxOF
+wC+797rRfm1qG2CLZ3lMsI70AGOFAyb76VS0s8vpTjDbU5RBHQqfZ1HbYzRSt8GS
+4QlOeJx71IZjI8F3BhkjrQncjKLrcF+lDUA/AVuezn2kxAAeNiNHYWrDq+C6Odaj
+PIr4DDrtfgsnemBy7TG2eubWmc761DVqtgP/To7QySg7vbwqNsX3BcJsE0AeD1Jy
+0BhfNgvhhq9N8zlzLhv4dAHK/HiRQdtb7Z6qEPawihXxY5so3R+K4shfHet98Ri9
+naCVlMdaOg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
+NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
+XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
+h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
+L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
+QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
+D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
+mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
+Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
+oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
+jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
+ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
+ex0SdDrx+tWUDqG8At2JHA==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
index 45f2e5c6fdf7..dfbbda6c6f5e 100644
--- a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -89,6 +89,34 @@ class AttestationVerificationPeerDeviceVerifierTest {
}
@Test
+ fun verifyAttestation_returnsSuccessOwnedBySystem() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "activeUnlockValid".encodeToByteArray())
+ challengeRequirements.putBoolean("android.key_owned_by_system", true)
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_OWNED_BY_SYSTEM_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureOwnedBySystem() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+ challengeRequirements.putBoolean("android.key_owned_by_system", true)
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
val verifier = AttestationVerificationPeerDeviceVerifier(
context, trustAnchors, false, LocalDate.of(2023, 3, 1),
@@ -171,5 +199,6 @@ class AttestationVerificationPeerDeviceVerifierTest {
private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
"test_attestation_with_root_certs.pem"
private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ private const val TEST_OWNED_BY_SYSTEM_FILENAME = "test_owned_by_system_certs.pem"
}
}
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
index 90ddb6ffb34a..81efda17abe3 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import static org.junit.Assume.assumeFalse;
+
import android.app.AlarmManager;
import android.content.Context;
import android.os.Environment;
@@ -31,6 +33,7 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -111,6 +114,7 @@ public final class BackgroundDexOptServiceIntegrationTests {
@Before
public void setUp() throws IOException {
+ assumeFalse(SystemProperties.getBoolean("dalvik.vm.useartservice", false));
File dataDir = getContext().getDataDir();
mBigFile = new File(dataDir, BIG_FILE);
}
@@ -299,6 +303,8 @@ public final class BackgroundDexOptServiceIntegrationTests {
// Test that background dexopt under low storage conditions downgrades unused packages.
@Test
+ @Ignore("b/251438180: This test has been failing for a long time; temporarily disable it while"
+ + " we investigate this issue.")
public void testBackgroundDexOptDowngradeSuccessful() throws IOException {
// Should be more than DOWNGRADE_AFTER_DAYS.
long deltaDays = DOWNGRADE_AFTER_DAYS + 1;
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
new file mode 100644
index 000000000000..dc6bdff6716c
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -0,0 +1,43 @@
+// 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 {
+ // 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_test_host {
+ name: "BinaryTransparencyHostTest",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ "compatibility-host-util",
+ ],
+ static_libs: [
+ "truth-prebuilt",
+ ],
+ data: [
+ ":BinaryTransparencyTestApp",
+ ":EasterEgg",
+ ":com.android.apex.cts.shim.v2_rebootless_prebuilt",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/tests/BinaryTransparencyHostTest/AndroidTest.xml b/tests/BinaryTransparencyHostTest/AndroidTest.xml
new file mode 100644
index 000000000000..e0d11c0c1097
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?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="Binary Transparency integration test">
+ <option name="test-suite-tag" value="apct" />
+
+ <!-- Service is not exposed to apps. Disable SELinux for testing purpose. -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="BinaryTransparencyTestApp.apk" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="BinaryTransparencyHostTest.jar" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/BinaryTransparencyHostTest/OWNERS b/tests/BinaryTransparencyHostTest/OWNERS
new file mode 100644
index 000000000000..ca84550c76a4
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/transparency/OWNERS
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BaseInstallMultiple.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BaseInstallMultiple.java
new file mode 100644
index 000000000000..3e94f25b5fc7
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BaseInstallMultiple.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.transparency.test;
+
+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/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
new file mode 100644
index 000000000000..346622f0f467
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.transparency.test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.LargeTest;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class BinaryTransparencyHostTest extends BaseHostJUnit4Test {
+ private static final String PACKAGE_NAME = "android.transparency.test.app";
+
+ private static final String JOB_ID = "1740526926";
+
+ /** Waiting time for the job to be scheduled */
+ private static final int JOB_CREATION_MAX_SECONDS = 30;
+
+ @Before
+ public void setUp() throws Exception {
+ cancelPendingJob();
+ }
+
+ @Test
+ public void testCollectAllApexInfo() throws Exception {
+ var options = new DeviceTestRunOptions(PACKAGE_NAME);
+ options.setTestClassName(PACKAGE_NAME + ".BinaryTransparencyTest");
+ options.setTestMethodName("testCollectAllApexInfo");
+
+ // Collect APEX package names from /apex, then pass them as expectation to be verified.
+ // The package names are collected from the find name with deduplication (NB: we used to
+ // deduplicate by dropping directory names with '@', but there's a DCLA case where it only
+ // has one directory with '@'. So we have to keep it and deduplicate the current way).
+ CommandResult result = getDevice().executeShellV2Command(
+ "ls -d /apex/*/ |grep -v /apex/sharedlibs |cut -d/ -f3 |cut -d@ -f1 |sort |uniq");
+ assertTrue(result.getStatus() == CommandStatus.SUCCESS);
+ String[] packageNames = result.getStdout().split("\n");
+ for (var i = 0; i < packageNames.length; i++) {
+ options.addInstrumentationArg("apex-" + String.valueOf(i), packageNames[i]);
+ }
+ options.addInstrumentationArg("apex-number", Integer.toString(packageNames.length));
+ runDeviceTests(options);
+ }
+
+ @Test
+ public void testCollectAllUpdatedPreloadInfo() throws Exception {
+ try {
+ updatePreloadApp();
+ runDeviceTest("testCollectAllUpdatedPreloadInfo");
+ } finally {
+ // No need to wait until job complete, since we can't verifying very meaningfully.
+ cancelPendingJob();
+ uninstallPackage("com.android.egg");
+ }
+ }
+
+ @Test
+ public void testCollectAllSilentInstalledMbaInfo() throws Exception {
+ try {
+ new InstallMultiple()
+ .addFile("ApkVerityTestApp.apk")
+ .addFile("ApkVerityTestAppSplit.apk")
+ .run();
+ updatePreloadApp();
+ assertNotNull(getDevice().getAppPackageInfo("com.android.apkverity"));
+ assertNotNull(getDevice().getAppPackageInfo("com.android.egg"));
+
+ assertTrue(getDevice().setProperty("debug.transparency.bg-install-apps",
+ "com.android.apkverity,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.egg");
+ }
+ }
+
+ @LargeTest
+ @Test
+ public void testRebootlessApexUpdateTriggersJobScheduling() throws Exception {
+ try {
+ installRebootlessApex();
+
+ // Verify
+ expectJobToBeScheduled();
+ } finally {
+ // No need to wait until job complete, since we can't verifying very meaningfully.
+ uninstallRebootlessApexThenReboot();
+ }
+ }
+
+ @Test
+ public void testPreloadUpdateTriggersJobScheduling() throws Exception {
+ try {
+ updatePreloadApp();
+
+ // Verify
+ expectJobToBeScheduled();
+ } finally {
+ // No need to wait until job complete, since we can't verifying very meaningfully.
+ cancelPendingJob();
+ uninstallPackage("com.android.egg");
+ }
+ }
+
+ private void runDeviceTest(String method) throws DeviceNotAvailableException {
+ var options = new DeviceTestRunOptions(PACKAGE_NAME);
+ options.setTestClassName(PACKAGE_NAME + ".BinaryTransparencyTest");
+ options.setTestMethodName(method);
+ runDeviceTests(options);
+ }
+
+ private void cancelPendingJob() throws DeviceNotAvailableException {
+ CommandResult result = getDevice().executeShellV2Command(
+ "cmd jobscheduler cancel android " + JOB_ID);
+ if (result.getStatus() == CommandStatus.SUCCESS) {
+ CLog.d("Canceling, output: " + result.getStdout());
+ } else {
+ CLog.d("Something went wrong, error: " + result.getStderr());
+ }
+ }
+
+ private void expectJobToBeScheduled() throws Exception {
+ for (int i = 0; i < JOB_CREATION_MAX_SECONDS; i++) {
+ CommandResult result = getDevice().executeShellV2Command(
+ "cmd jobscheduler get-job-state android " + JOB_ID);
+ String state = result.getStdout().toString();
+ CLog.i("Job status: " + state);
+ if (state.startsWith("unknown")) {
+ // The job hasn't been scheduled yet. So try again.
+ TimeUnit.SECONDS.sleep(1);
+ } else if (result.getExitCode() != 0) {
+ fail("Failing due to unexpected job state: " + result);
+ } else {
+ // The job exists, which is all we care about here
+ return;
+ }
+ }
+ fail("Timed out waiting for the job to be scheduled");
+ }
+
+ private void installRebootlessApex() throws Exception {
+ installPackage("com.android.apex.cts.shim.v2_rebootless.apex", "--force-non-staged");
+ }
+
+ private void uninstallRebootlessApexThenReboot() throws DeviceNotAvailableException {
+ // Reboot only if the APEX is not the pre-install one.
+ CommandResult result = getDevice().executeShellV2Command(
+ "pm list packages -f --apex-only |grep com.android.apex.cts.shim");
+ assertTrue(result.getStatus() == CommandStatus.SUCCESS);
+ if (result.getStdout().contains("/data/apex/active/")) {
+ uninstallPackage("com.android.apex.cts.shim");
+ getDevice().reboot();
+
+ // Reboot enforces SELinux. Make it permissive again.
+ CommandResult runResult = getDevice().executeShellV2Command("setenforce 0");
+ assertTrue(runResult.getStatus() == CommandStatus.SUCCESS);
+ }
+ }
+
+ private void updatePreloadApp() throws DeviceNotAvailableException {
+ CommandResult result = getDevice().executeShellV2Command("pm path com.android.egg");
+ assertTrue(result.getStatus() == CommandStatus.SUCCESS);
+ assertThat(result.getStdout()).startsWith("package:/system/app/");
+ String path = result.getStdout().replaceFirst("^package:", "");
+
+ result = getDevice().executeShellV2Command("pm install " + path);
+ assertTrue(result.getStatus() == CommandStatus.SUCCESS);
+ }
+
+ private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> {
+ InstallMultiple() {
+ super(getDevice(), getBuild());
+ // Needed since in getMockBackgroundInstalledPackages, getPackageInfo runs as the caller
+ // uid. This also makes it consistent with installPackage's behavior.
+ addArg("--force-queryable");
+ }
+ }
+}
diff --git a/tests/BinaryTransparencyHostTest/test-app/Android.bp b/tests/BinaryTransparencyHostTest/test-app/Android.bp
new file mode 100644
index 000000000000..b5193ddf7c2c
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/test-app/Android.bp
@@ -0,0 +1,40 @@
+// 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 {
+ // 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_helper_app {
+ name: "BinaryTransparencyTestApp",
+ manifest: "AndroidManifest.xml",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "androidx.test.core",
+ "compatibility-device-util-axt",
+ "junit",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ platform_apis: true,
+ dex_preopt: {
+ enabled: false,
+ },
+}
diff --git a/tests/componentalias/AndroidManifest.xml b/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml
index 7bb83a336833..42e616e24eb9 100755..100644
--- a/tests/componentalias/AndroidManifest.xml
+++ b/tests/BinaryTransparencyHostTest/test-app/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- * 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,10 +16,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
-
+ package="android.transparency.test.app">
<application>
<uses-library android:name="android.test.runner" />
- <property android:name="com.android.EXPERIMENTAL_COMPONENT_ALIAS_OPT_IN" android:value="true" />
</application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="APCT tests for binary transparency"
+ android:targetPackage="android.transparency.test.app" />
</manifest>
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
new file mode 100644
index 000000000000..c087a85da2a8
--- /dev/null
+++ b/tests/BinaryTransparencyHostTest/test-app/src/android/transparency/test/app/BinaryTransparencyTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.transparency.test.app;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.transparency.BinaryTransparencyManager;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.IBinaryTransparencyService.AppInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.HexFormat;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class BinaryTransparencyTest {
+ private static final String TAG = "BinaryTransparencyTest";
+
+ private BinaryTransparencyManager mBt;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ mBt = context.getSystemService(BinaryTransparencyManager.class);
+ }
+
+ @Test
+ public void testCollectAllApexInfo() {
+ // Prepare the expectation received from host's shell command
+ Bundle args = InstrumentationRegistry.getArguments();
+ assertThat(args).isNotNull();
+ int number = Integer.valueOf(args.getString("apex-number"));
+ assertThat(number).isGreaterThan(0);
+ var expectedApexNames = new ArrayList<String>();
+ for (var i = 0; i < number; i++) {
+ String moduleName = args.getString("apex-" + Integer.toString(i));
+ expectedApexNames.add(moduleName);
+ }
+ assertThat(expectedApexNames).containsNoDuplicates();
+
+ // Action
+ var apexInfoList = mBt.collectAllApexInfo(/* includeTestOnly */ true);
+
+ // Verify actual apex names
+ var actualApexesNames = apexInfoList.stream().map((apex) -> apex.moduleName)
+ .collect(Collectors.toList());
+ assertThat(actualApexesNames).containsExactlyElementsIn(expectedApexNames);
+
+ // Perform more valitidy checks
+ var digestsSeen = new HashSet<String>();
+ var hexFormatter = HexFormat.of();
+ for (var apex : apexInfoList) {
+ Log.d(TAG, "Verifying " + apex.packageName + " / " + apex.moduleName);
+
+ assertThat(apex.longVersion).isGreaterThan(0);
+ assertThat(apex.digestAlgorithm).isGreaterThan(0);
+ assertThat(apex.signerDigests).asList().containsNoneOf(null, "");
+
+ assertThat(apex.digest).isNotNull();
+ String digestHex = hexFormatter.formatHex(apex.digest);
+ boolean isNew = digestsSeen.add(digestHex);
+ assertWithMessage(
+ "Digest should be unique, but received a dup: " + digestHex)
+ .that(isNew).isTrue();
+ }
+ }
+
+ @Test
+ public void testCollectAllUpdatedPreloadInfo() {
+ var preloadInfoList = mBt.collectAllUpdatedPreloadInfo(new Bundle());
+ assertThat(preloadInfoList).isNotEmpty(); // because we just installed from the host side
+ AppInfo updatedPreload = null;
+ for (var preload : preloadInfoList) {
+ Log.d(TAG, "Received " + preload.packageName);
+ if (preload.packageName.equals("com.android.egg")) {
+ assertWithMessage("Received the same package").that(updatedPreload).isNull();
+ updatedPreload = preload;
+ }
+ }
+
+ // Verify
+ assertThat(updatedPreload.longVersion).isGreaterThan(0);
+ assertThat(updatedPreload.digestAlgorithm).isGreaterThan(0);
+ assertThat(updatedPreload.digest).isNotEmpty();
+ assertThat(updatedPreload.mbaStatus).isEqualTo(/* MBA_STATUS_UPDATED_PRELOAD */ 2);
+ assertThat(updatedPreload.signerDigests).asList().containsNoneOf(null, "");
+ }
+
+ @Test
+ public void testCollectAllSilentInstalledMbaInfo() {
+ // Action
+ var appInfoList = mBt.collectAllSilentInstalledMbaInfo(new Bundle());
+
+ // Verify
+ assertThat(appInfoList).isNotEmpty(); // because we just installed from the host side
+
+ var expectedAppNames = Set.of("com.android.apkverity", "com.android.egg");
+ var actualAppNames = appInfoList.stream().map((appInfo) -> appInfo.packageName)
+ .collect(Collectors.toList());
+ assertThat(actualAppNames).containsAtLeastElementsIn(expectedAppNames);
+
+ var actualSplitNames = new ArrayList<String>();
+ for (var appInfo : appInfoList) {
+ Log.d(TAG, "Received " + appInfo.packageName + " as a silent install");
+ if (expectedAppNames.contains(appInfo.packageName)) {
+ assertThat(appInfo.longVersion).isGreaterThan(0);
+ assertThat(appInfo.digestAlgorithm).isGreaterThan(0);
+ assertThat(appInfo.digest).isNotEmpty();
+ assertThat(appInfo.mbaStatus).isEqualTo(/* MBA_STATUS_NEW_INSTALL */ 3);
+ assertThat(appInfo.signerDigests).asList().containsNoneOf(null, "");
+
+ if (appInfo.splitName != null) {
+ actualSplitNames.add(appInfo.splitName);
+ }
+ }
+ }
+ assertThat(actualSplitNames).containsExactly("feature_x"); // Name of ApkVerityTestAppSplit
+ }
+}
diff --git a/tests/Camera2Tests/CameraToo/Android.bp b/tests/Camera2Tests/CameraToo/Android.bp
new file mode 100644
index 000000000000..ebc6fed9fefd
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2014 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
+ default_applicable_licenses: [
+ "frameworks_base_license",
+ ],
+}
+
+android_test {
+ name: "CameraToo",
+
+ sdk_version: "current",
+ srcs: ["src/**/*.java"],
+
+}
diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk
deleted file mode 100644
index 33473143c8cb..000000000000
--- a/tests/Camera2Tests/CameraToo/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := CameraToo
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-
-include $(BUILD_PACKAGE)
diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp
deleted file mode 100644
index 98831154ddc2..000000000000
--- a/tests/CanvasCompare/Android.bp
+++ /dev/null
@@ -1,63 +0,0 @@
-//
-// Copyright (C) 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.
-//
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: [
- "frameworks_base_license",
- ],
-}
-
-android_test {
- name: "CanvasCompare",
- srcs: [
- "src/**/*.java",
- ":CanvasCompare-rscript{CanvasCompare.srcjar}",
- ],
- resource_zips: [
- ":CanvasCompare-rscript{CanvasCompare.res.zip}",
- ],
- platform_apis: true,
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
- static_libs: ["junit"],
-}
-
-genrule {
- name: "CanvasCompare-rscript",
- srcs: [
- "src/**/*.rscript",
- ":rs_script_api",
- ":rs_clang_headers",
- ],
- tools: [
- "llvm-rs-cc",
- "soong_zip",
- ],
- out: [
- "CanvasCompare.srcjar",
- "CanvasCompare.res.zip",
- ],
- cmd: "for f in $(locations src/**/*.rscript); do " +
- " $(location llvm-rs-cc) -o $(genDir)/res/raw -p $(genDir)/src " +
- " -I $$(dirname $$(echo $(locations :rs_script_api) | awk '{ print $$1 }')) " +
- " -I $$(dirname $$(echo $(locations :rs_clang_headers) | awk '{ print $$1 }')) $${f}; " +
- "done && " +
- "$(location soong_zip) -srcjar -o $(location CanvasCompare.srcjar) -C $(genDir)/src -D $(genDir)/src &&" +
- "$(location soong_zip) -o $(location CanvasCompare.res.zip) -C $(genDir)/res -D $(genDir)/res",
-}
diff --git a/tests/CanvasCompare/AndroidManifest.xml b/tests/CanvasCompare/AndroidManifest.xml
deleted file mode 100644
index 2734e7f07f27..000000000000
--- a/tests/CanvasCompare/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.hwuicompare">
-
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-
- <application android:label="@string/app_name"
- android:theme="@android:style/Theme.Holo.Light.NoActionBar">
- <activity android:name="AutomaticActivity"
- android:label="CanvasAutoCompare"
- 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="ManualActivity"
- android:label="CanvasManualCompare"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <uses-library android:name="android.test.runner"/>
- </application>
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.test.hwuicompare"
- android:label="HW/SW Canvas comparison tool."/>
-
-</manifest>
diff --git a/tests/CanvasCompare/OWNERS b/tests/CanvasCompare/OWNERS
deleted file mode 100644
index c88a9f82c347..000000000000
--- a/tests/CanvasCompare/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /libs/hwui/OWNERS
diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg
deleted file mode 100644
index 3b4e056b70d0..000000000000
--- a/tests/CanvasCompare/res/drawable/sunset1.jpg
+++ /dev/null
Binary files differ
diff --git a/tests/CanvasCompare/res/layout/automatic_layout.xml b/tests/CanvasCompare/res/layout/automatic_layout.xml
deleted file mode 100644
index e049ec0a5000..000000000000
--- a/tests/CanvasCompare/res/layout/automatic_layout.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/hardware_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
-
- <ImageView
- android:id="@+id/software_image_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true" />
-
- <ImageView
- android:id="@+id/hardware_image_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true" />
-
-</RelativeLayout>
diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml
deleted file mode 100644
index 1a9288ce1993..000000000000
--- a/tests/CanvasCompare/res/layout/manual_layout.xml
+++ /dev/null
@@ -1,119 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center_horizontal"
- android:orientation="vertical" >
-
- <HorizontalScrollView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <LinearLayout
- android:id="@+id/spinner_layout"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" />
- </HorizontalScrollView>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:baselineAligned="true"
- android:orientation="horizontal" >
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/hardware_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <com.android.test.hwuicompare.MainView
- android:id="@+id/software_view"
- android:layout_width="@dimen/layer_width"
- android:layout_height="@dimen/layer_width" />
- </LinearLayout>
- </LinearLayout>
-
- <ImageView
- android:id="@+id/compare_image_view"
- android:layout_width="@dimen/layer_width_double"
- android:layout_height="@dimen/layer_height_double"
- android:filter="false" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal" >
-
- <ImageButton
- android:id="@+id/previous"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/previous_combination"
- android:src="@android:drawable/ic_media_previous" />
-
- <ImageButton
- android:id="@+id/next"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:contentDescription="@string/next_combination"
- android:src="@android:drawable/ic_media_next" />
-
- <TextView
- android:id="@+id/current_error"
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceLarge" />
-
- <Button
- android:id="@+id/show_hardware_version"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_hardware_version" />
-
- <Button
- android:id="@+id/show_software_version"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_software_version" />
-
- <Button
- android:id="@+id/show_error_heatmap"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/show_error_heatmap" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/tests/CanvasCompare/res/values/strings.xml b/tests/CanvasCompare/res/values/strings.xml
deleted file mode 100644
index edd46103f4f5..000000000000
--- a/tests/CanvasCompare/res/values/strings.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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>
- <string name="app_name">Canvas Compare Test</string>
-
- <!-- show hardware rendered version of the layer -->
- <string name="show_hardware_version">Hardware</string>
- <!-- show software rendered version of the layer -->
- <string name="show_software_version">Software</string>
- <!-- show layer error -->
- <string name="show_error_values">Error</string>
- <!-- show layer error heatmap -->
- <string name="show_error_heatmap">Heatmap</string>
- <!-- select and display the next combination of painting options-->
- <string name="next_combination">Next Combination</string>
- <!-- select and display the previous combination of painting options-->
- <string name="previous_combination">Previous Combination</string>
-</resources>
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
deleted file mode 100644
index 8ccd4e2181ed..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/AutomaticActivity.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.TreeSet;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Trace;
-import android.util.Log;
-import android.widget.ImageView;
-import android.widget.Toast;
-
-public class AutomaticActivity extends CompareActivity {
- private static final String LOG_TAG = "AutomaticActivity";
- private static final float ERROR_DISPLAY_THRESHOLD = 0.01f;
- protected static final boolean DRAW_BITMAPS = false;
-
- /**
- * Threshold of error change required to consider a test regressed/improved
- */
- private static final float ERROR_CHANGE_THRESHOLD = 0.001f;
-
- private static final float[] ERROR_CUTOFFS = {
- 0, 0.005f, 0.01f, 0.02f, 0.05f, 0.1f, 0.25f, 0.5f, 1f, 2f
- };
-
- private final float[] mErrorRates = new float[ERROR_CUTOFFS.length];
- private float mTotalTests = 0;
- private float mTotalError = 0;
- private int mTestsRegressed = 0;
- private int mTestsImproved = 0;
-
- private ImageView mSoftwareImageView = null;
- private ImageView mHardwareImageView = null;
-
-
- public abstract static class FinalCallback {
- abstract void report(String name, float value);
- void complete() {};
- }
-
- private final ArrayList<FinalCallback> mFinalCallbacks = new ArrayList<FinalCallback>();
-
- Runnable mRunnable = new Runnable() {
- @Override
- public void run() {
- loadBitmaps();
- if (mSoftwareBitmap == null || mHardwareBitmap == null) {
- Log.e(LOG_TAG, "bitmap is null");
- return;
- }
-
- if (DRAW_BITMAPS) {
- mSoftwareImageView.setImageBitmap(mSoftwareBitmap);
- mHardwareImageView.setImageBitmap(mHardwareBitmap);
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "calculateError");
- float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
-
- final String[] modifierNames = DisplayModifier.getLastAppliedModifications();
- handleError(modifierNames, error);
-
- if (DisplayModifier.step()) {
- finishTest();
- } else {
- mHardwareView.invalidate();
- if (DRAW_BITMAPS) {
- mSoftwareImageView.invalidate();
- mHardwareImageView.invalidate();
- }
- }
- mHandler.removeCallbacks(mRunnable);
- }
- };
-
- @Override
- protected void onPause() {
- super.onPause();
- mHandler.removeCallbacks(mRunnable);
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.automatic_layout);
-
- mSoftwareImageView = findViewById(R.id.software_image_view);
- mHardwareImageView = findViewById(R.id.hardware_image_view);
-
- onCreateCommon(mRunnable);
- beginTest();
- }
-
- private static class TestResult {
- TestResult(String label, float error) {
- mLabel = label;
- mTotalError = error;
- mCount = 1;
- }
- public void addInto(float error) {
- mTotalError += error;
- mCount++;
- }
- public float getAverage() {
- return mTotalError / mCount;
- }
- final String mLabel;
- float mTotalError;
- int mCount;
- }
-
- JSONObject mOutputJson = null;
- JSONObject mInputJson = null;
- final HashMap<String, TestResult> mModifierResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mIndividualResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mModifierDiffResults = new HashMap<String, TestResult>();
- final HashMap<String, TestResult> mIndividualDiffResults = new HashMap<String, TestResult>();
- private void beginTest() {
- mFinalCallbacks.add(new FinalCallback() {
- @Override
- void report(String name, float value) {
- Log.d(LOG_TAG, name + " " + value);
- };
- });
-
- File inputFile = new File(Environment.getExternalStorageDirectory(),
- "CanvasCompareInput.json");
- if (inputFile.exists() && inputFile.canRead() && inputFile.length() > 0) {
- try {
- FileInputStream inputStream = new FileInputStream(inputFile);
- Log.d(LOG_TAG, "Parsing input file...");
- StringBuffer content = new StringBuffer((int)inputFile.length());
- byte[] buffer = new byte[1024];
- while (inputStream.read(buffer) != -1) {
- content.append(new String(buffer));
- }
- mInputJson = new JSONObject(content.toString());
- inputStream.close();
- Log.d(LOG_TAG, "Parsed input file with " + mInputJson.length() + " entries");
- } catch (JSONException e) {
- Log.e(LOG_TAG, "error parsing input json", e);
- } catch (IOException e) {
- Log.e(LOG_TAG, "error reading input json from sd", e);
- }
- }
-
- mOutputJson = new JSONObject();
- }
-
- private static void logTestResultHash(String label, HashMap<String, TestResult> map) {
- Log.d(LOG_TAG, "---------------");
- Log.d(LOG_TAG, label + ":");
- Log.d(LOG_TAG, "---------------");
- TreeSet<TestResult> set = new TreeSet<TestResult>(new Comparator<TestResult>() {
- @Override
- public int compare(TestResult lhs, TestResult rhs) {
- if (lhs == rhs) return 0; // don't need to worry about complex equality
-
- int cmp = Float.compare(lhs.getAverage(), rhs.getAverage());
- if (cmp != 0) {
- return cmp;
- }
- return lhs.mLabel.compareTo(rhs.mLabel);
- }
- });
-
- for (TestResult t : map.values()) {
- set.add(t);
- }
-
- for (TestResult t : set.descendingSet()) {
- if (Math.abs(t.getAverage()) > ERROR_DISPLAY_THRESHOLD) {
- Log.d(LOG_TAG, String.format("%2.4f : %s", t.getAverage(), t.mLabel));
- }
- }
- Log.d(LOG_TAG, "");
- }
-
- private void finishTest() {
- for (FinalCallback c : mFinalCallbacks) {
- c.report("averageError", (mTotalError / mTotalTests));
- for (int i = 1; i < ERROR_CUTOFFS.length; i++) {
- c.report(String.format("tests with error over %1.3f", ERROR_CUTOFFS[i]),
- mErrorRates[i]);
- }
- if (mInputJson != null) {
- c.report("tests regressed", mTestsRegressed);
- c.report("tests improved", mTestsImproved);
- }
- c.complete();
- }
-
- try {
- if (mOutputJson != null) {
- String outputString = mOutputJson.toString(4);
- File outputFile = new File(Environment.getExternalStorageDirectory(),
- "CanvasCompareOutput.json");
- FileOutputStream outputStream = new FileOutputStream(outputFile);
- outputStream.write(outputString.getBytes());
- outputStream.close();
- Log.d(LOG_TAG, "Saved output file with " + mOutputJson.length() + " entries");
- }
- } catch (JSONException e) {
- Log.e(LOG_TAG, "error during JSON stringify", e);
- } catch (IOException e) {
- Log.e(LOG_TAG, "error storing JSON output on sd", e);
- }
-
- logTestResultHash("Modifier change vs previous", mModifierDiffResults);
- logTestResultHash("Invidual test change vs previous", mIndividualDiffResults);
- logTestResultHash("Modifier average test results", mModifierResults);
- logTestResultHash("Individual test results", mIndividualResults);
-
- Toast.makeText(getApplicationContext(), "done!", Toast.LENGTH_SHORT).show();
- finish();
- }
-
- /**
- * Inserts the error value into all TestResult objects, associated with each of its modifiers
- */
- private static void addForAllModifiers(String fullName, float error, String[] modifierNames,
- HashMap<String, TestResult> modifierResults) {
- for (String modifierName : modifierNames) {
- TestResult r = modifierResults.get(fullName);
- if (r == null) {
- modifierResults.put(modifierName, new TestResult(modifierName, error));
- } else {
- r.addInto(error);
- }
- }
- }
-
- private void handleError(final String[] modifierNames, final float error) {
- String fullName = "";
- for (String s : modifierNames) {
- fullName = fullName.concat("." + s);
- }
- fullName = fullName.substring(1);
-
- float deltaError = 0;
- if (mInputJson != null) {
- try {
- deltaError = error - (float)mInputJson.getDouble(fullName);
- } catch (JSONException e) {
- Log.w(LOG_TAG, "Warning: unable to read from input json", e);
- }
- if (deltaError > ERROR_CHANGE_THRESHOLD) mTestsRegressed++;
- if (deltaError < -ERROR_CHANGE_THRESHOLD) mTestsImproved++;
- mIndividualDiffResults.put(fullName, new TestResult(fullName, deltaError));
- addForAllModifiers(fullName, deltaError, modifierNames, mModifierDiffResults);
- }
-
- mIndividualResults.put(fullName, new TestResult(fullName, error));
- addForAllModifiers(fullName, error, modifierNames, mModifierResults);
-
- try {
- if (mOutputJson != null) {
- mOutputJson.put(fullName, error);
- }
- } catch (JSONException e) {
- Log.e(LOG_TAG, "exception during JSON recording", e);
- mOutputJson = null;
- }
-
- for (int i = 0; i < ERROR_CUTOFFS.length; i++) {
- if (error <= ERROR_CUTOFFS[i]) break;
- mErrorRates[i]++;
- }
- mTotalError += error;
- mTotalTests++;
- }
-
- @Override
- protected boolean forceRecreateBitmaps() {
- // disable, unless needed for drawing into imageviews
- return DRAW_BITMAPS;
- }
-
- // FOR TESTING
- public void setFinalCallback(FinalCallback c) {
- mFinalCallbacks.add(c);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
deleted file mode 100644
index 0dec1de79a46..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/CompareActivity.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import com.android.test.hwuicompare.R;
-
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Handler;
-import android.os.Trace;
-import android.util.Log;
-import android.view.View;
-
-abstract public class CompareActivity extends Activity {
- private static final String LOG_TAG = "CompareActivity";
-
- protected MainView mHardwareView = null;
-
- protected Bitmap mSoftwareBitmap;
- protected Bitmap mHardwareBitmap;
-
- protected ErrorCalculator mErrorCalculator;
-
- protected Handler mHandler;
-
- Runnable mDrawCallback = null;
- protected boolean mRedrewFlag = true;
-
- protected void onCreateCommon(final Runnable postDrawCallback) {
- mDrawCallback = new Runnable() {
- @Override
- public void run() {
- mRedrewFlag = true;
- mHandler.post(postDrawCallback);
- };
- };
- getWindow().setBackgroundDrawable(new ColorDrawable(0xffefefef));
- ResourceModifiers.init(getResources());
-
- mHardwareView = findViewById(R.id.hardware_view);
- mHardwareView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mHardwareView.setBackgroundColor(Color.WHITE);
- mHardwareView.addDrawCallback(mDrawCallback);
-
- int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
- int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
- mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- mErrorCalculator = new ErrorCalculator(getApplicationContext(), getResources());
-
- mHandler = new Handler();
- }
-
- protected abstract boolean forceRecreateBitmaps();
-
- protected void loadBitmaps() {
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "loadBitmaps");
- if (forceRecreateBitmaps()) {
- int width = mSoftwareBitmap.getWidth();
- int height = mSoftwareBitmap.getHeight();
-
- mSoftwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mHardwareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "softwareDraw");
- mHardwareView.draw(new Canvas(mSoftwareBitmap));
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
-
- try {
- Method getHardwareLayer = View.class.getDeclaredMethod("getHardwareLayer");
- if (!getHardwareLayer.isAccessible())
- getHardwareLayer.setAccessible(true);
- Object hardwareLayer = getHardwareLayer.invoke(mHardwareView);
- if (hardwareLayer == null) {
- Log.d(LOG_TAG, "failure to access hardware layer");
- return;
- }
- Method copyInto = hardwareLayer.getClass()
- .getDeclaredMethod("copyInto", Bitmap.class);
- if (!copyInto.isAccessible())
- copyInto.setAccessible(true);
-
- Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, "copyInto");
- boolean success = (Boolean) copyInto.invoke(hardwareLayer, mHardwareBitmap);
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- if (!success) {
- Log.d(LOG_TAG, "failure to copy hardware layer into bitmap");
- }
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
- Trace.traceEnd(Trace.TRACE_TAG_ALWAYS);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
deleted file mode 100644
index 0f4e122d147a..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/DisplayModifier.java
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import java.util.LinkedHashMap;
-import java.util.Map.Entry;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.util.Log;
-
-public abstract class DisplayModifier {
-
- // automated tests ignore any combination of operations that don't together return TOTAL_MASK
- protected final static int TOTAL_MASK = 0x1F;
-
- // if we're filling, ensure we're not also sweeping over stroke parameters
- protected final static int SWEEP_STROKE_WIDTH_BIT = 0x1 << 0;
- protected final static int SWEEP_STROKE_CAP_BIT = 0x1 << 1;
- protected final static int SWEEP_STROKE_JOIN_BIT = 0x1 << 2;
-
- protected final static int SWEEP_SHADER_BIT = 0x1 << 3; // only allow non-simple shaders to use rectangle drawing
- protected final static int SWEEP_TRANSFORM_BIT = 0x1 << 4; // only sweep over specified transforms
-
- abstract public void modifyDrawing(Paint paint, Canvas canvas);
- protected int mask() { return 0x0; };
-
- private static final RectF gRect = new RectF(0, 0, 200, 175);
- private static final float[] gPts = new float[] {
- 0, 100, 100, 0, 100, 200, 200, 100
- };
-
- private static final int NUM_PARALLEL_LINES = 24;
- private static final float[] gTriPts = new float[] {
- 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
- };
- private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
- static {
- int index;
- for (index = 0; index < gTriPts.length; index++) {
- gLinePts[index] = gTriPts[index];
- }
- float val = 0;
- for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = 150;
- gLinePts[index + 1] = val;
- gLinePts[index + 2] = 300;
- gLinePts[index + 3] = val;
- index += 4;
- val += 8 + (2.0f/NUM_PARALLEL_LINES);
- }
- val = 0;
- for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = val;
- gLinePts[index + 1] = 150;
- gLinePts[index + 2] = val;
- gLinePts[index + 3] = 300;
- index += 4;
- val += 8 + (2.0f/NUM_PARALLEL_LINES);
- }
- };
-
- @SuppressWarnings("serial")
- private static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> gMaps = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>() {
- {
- put("aa", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("true", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setAntiAlias(true);
- }
- });
- put("false", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setAntiAlias(false);
- }
- });
- }
- });
- put("style", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("fill", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.FILL);
- }
- });
- put("stroke", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.STROKE);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- });
- put("fillAndStroke", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStyle(Paint.Style.FILL_AND_STROKE);
- }
-
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- });
- }
- });
- put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("hair", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(0);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_WIDTH_BIT; }
- });
- put("0.3", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(0.3f);
- }
- });
- put("1", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(1);
- }
- });
- put("5", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(5);
- }
- });
- put("30", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeWidth(30);
- }
- });
- }
- });
- put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("butt", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.BUTT);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; }
- });
- put("round", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.ROUND);
- }
- });
- put("square", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeCap(Paint.Cap.SQUARE);
- }
- });
- }
- });
- put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("bevel", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.BEVEL);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_JOIN_BIT; }
- });
- put("round", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.ROUND);
- }
- });
- put("miter", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setStrokeJoin(Paint.Join.MITER);
- }
- });
- // TODO: add miter0, miter1 etc to test miter distances
- }
- });
-
- put("transform", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("noTransform", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {}
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- });
- put("rotate5", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(5);
- }
- });
- put("rotate45", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(45);
- }
- });
- put("rotate90", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(90);
- canvas.translate(0, -200);
- }
- });
- put("scale2x2", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.scale(2, 2);
- }
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- });
- put("rot20scl1x4", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.rotate(20);
- canvas.scale(1, 4);
- }
- @Override
- protected int mask() { return SWEEP_TRANSFORM_BIT; };
- });
- }
- });
-
- put("shader", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("noShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {}
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- });
- put("repeatShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mRepeatShader);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- });
- put("translatedShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mTranslatedShader);
- }
- });
- put("scaledShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mScaledShader);
- }
- });
- put("horGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mHorGradient);
- }
- });
- put("diagGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mDiagGradient);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT; };
- });
- put("vertGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mVertGradient);
- }
- });
- put("radGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mRadGradient);
- }
- });
- put("sweepGradient", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mSweepGradient);
- }
- });
- put("composeShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mComposeShader);
- }
- });
- put("bad composeShader", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mBadComposeShader);
- }
- });
- put("bad composeShader 2", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setShader(ResourceModifiers.instance().mAnotherBadComposeShader);
- }
- });
- }
- });
-
- // FINAL MAP: DOES ACTUAL DRAWING
- put("drawing", new LinkedHashMap<String, DisplayModifier>() {
- {
- put("roundRect", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRoundRect(gRect, 20, 20, paint);
- }
- });
- put("rect", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRect(gRect, paint);
- }
- @Override
- protected int mask() { return SWEEP_SHADER_BIT | SWEEP_STROKE_CAP_BIT; };
- });
- put("circle", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawCircle(100, 100, 75, paint);
- }
- });
- put("oval", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawOval(gRect, paint);
- }
- });
- put("lines", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawLines(gLinePts, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; };
- });
- put("plusPoints", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawPoints(gPts, paint);
- }
- });
- put("text", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
- canvas.drawText("TEXTTEST", 0, 50, paint);
- }
- });
- put("shadowtext", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
- paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
- canvas.drawText("TEXTTEST", 0, 50, paint);
- }
- });
- put("bitmapMesh", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawBitmapMesh(ResourceModifiers.instance().mBitmap, 3, 3,
- ResourceModifiers.instance().mBitmapVertices, 0, null, 0, null);
- }
- });
- put("arc", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, false, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_CAP_BIT; };
- });
- put("arcFromCenter", new DisplayModifier() {
- @Override
- public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, true, paint);
- }
- @Override
- protected int mask() { return SWEEP_STROKE_JOIN_BIT; };
- });
- }
- });
- // WARNING: DON'T PUT MORE MAPS BELOW THIS
- }
- };
-
- private static LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
- for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
- if (index == 0) {
- return map;
- }
- index--;
- }
- return null;
- }
-
- // indices instead of iterators for easier bidirectional traversal
- private static final int mIndices[] = new int[gMaps.size()];
- private static final String[] mLastAppliedModifications = new String[gMaps.size()];
-
- private static boolean stepInternal(boolean forward) {
- int modifierMapIndex = gMaps.size() - 1;
- while (modifierMapIndex >= 0) {
- LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
- mIndices[modifierMapIndex] += (forward ? 1 : -1);
-
- if (mIndices[modifierMapIndex] >= 0 && mIndices[modifierMapIndex] < map.size()) {
- break;
- }
-
- mIndices[modifierMapIndex] = (forward ? 0 : map.size() - 1);
- modifierMapIndex--;
- }
- return modifierMapIndex < 0; // true if resetting
- }
-
- public static boolean step() {
- boolean ret = false;
- do {
- ret |= stepInternal(true);
- } while (!checkModificationStateMask());
- return ret;
- }
-
- public static boolean stepBack() {
- boolean ret = false;
- do {
- ret |= stepInternal(false);
- } while (!checkModificationStateMask());
- return ret;
- }
-
- private static boolean checkModificationStateMask() {
- int operatorMask = 0x0;
- int mapIndex = 0;
- for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
- int displayModifierIndex = mIndices[mapIndex];
- for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
- if (displayModifierIndex == 0) {
- mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
- operatorMask |= modifierEntry.getValue().mask();
- break;
- }
- displayModifierIndex--;
- }
- mapIndex++;
- }
- return operatorMask == TOTAL_MASK;
- }
-
- public static void apply(Paint paint, Canvas canvas) {
- int mapIndex = 0;
- for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
- int displayModifierIndex = mIndices[mapIndex];
- for (Entry<String, DisplayModifier> modifierEntry : map.entrySet()) {
- if (displayModifierIndex == 0) {
- mLastAppliedModifications[mapIndex] = modifierEntry.getKey();
- modifierEntry.getValue().modifyDrawing(paint, canvas);
- break;
- }
- displayModifierIndex--;
- }
- mapIndex++;
- }
- }
-
- public static String[] getLastAppliedModifications() {
- return mLastAppliedModifications.clone();
- }
-
- public static String[][] getStrings() {
- String[][] keys = new String[gMaps.size()][];
-
- int i = 0;
- for (LinkedHashMap<String, DisplayModifier> map : gMaps.values()) {
- keys[i] = new String[map.size()];
- int j = 0;
- for (String key : map.keySet()) {
- keys[i][j++] = key;
- }
- i++;
- }
-
- return keys;
- }
-
- public static void setIndex(int mapIndex, int newIndexValue) {
- mIndices[mapIndex] = newIndexValue;
- }
-
- public static int[] getIndices() {
- return mIndices;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
deleted file mode 100644
index d402699b0979..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ErrorCalculator.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.util.Log;
-
-public class ErrorCalculator {
- private static final String LOG_TAG = "ErrorCalculator";
- private static final int REGION_SIZE = 8;
-
- private static final boolean LOG_TIMING = false;
- private static final boolean LOG_CALC = false;
-
- private RenderScript mRS;
- private Allocation mIdealPixelsAllocation;
- private Allocation mGivenPixelsAllocation;
- private Allocation mOutputPixelsAllocation;
-
- private Allocation mInputRowsAllocation;
- private Allocation mOutputRegionsAllocation;
-
- private ScriptC_errorCalculator mScript;
-
- private int[] mOutputRowRegions;
-
- public ErrorCalculator(Context c, Resources resources) {
- int width = resources.getDimensionPixelSize(R.dimen.layer_width);
- int height = resources.getDimensionPixelSize(R.dimen.layer_height);
- mOutputRowRegions = new int[height / REGION_SIZE];
-
- mRS = RenderScript.create(c);
- int[] rowIndices = new int[height / REGION_SIZE];
- for (int i = 0; i < rowIndices.length; i++)
- rowIndices[i] = i * REGION_SIZE;
-
- mScript = new ScriptC_errorCalculator(mRS);
- mScript.set_HEIGHT(height);
- mScript.set_WIDTH(width);
- mScript.set_REGION_SIZE(REGION_SIZE);
-
- mInputRowsAllocation = Allocation.createSized(mRS, Element.I32(mRS), rowIndices.length,
- Allocation.USAGE_SCRIPT);
- mInputRowsAllocation.copyFrom(rowIndices);
- mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS),
- mOutputRowRegions.length, Allocation.USAGE_SCRIPT);
- }
-
-
- private static long startMillis, middleMillis;
-
- public float calcErrorRS(Bitmap ideal, Bitmap given) {
- if (LOG_TIMING) {
- startMillis = System.currentTimeMillis();
- }
-
- mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-
- mScript.set_ideal(mIdealPixelsAllocation);
- mScript.set_given(mGivenPixelsAllocation);
-
- mScript.forEach_countInterestingRegions(mInputRowsAllocation, mOutputRegionsAllocation);
- mOutputRegionsAllocation.copyTo(mOutputRowRegions);
-
- int regionCount = 0;
- for (int region : mOutputRowRegions) {
- regionCount += region;
- }
- int interestingPixels = Math.max(1, regionCount) * REGION_SIZE * REGION_SIZE;
-
- if (LOG_TIMING) {
- long startMillis2 = System.currentTimeMillis();
- }
-
- mScript.forEach_accumulateError(mInputRowsAllocation, mOutputRegionsAllocation);
- mOutputRegionsAllocation.copyTo(mOutputRowRegions);
- float totalError = 0;
- for (int row : mOutputRowRegions) {
- totalError += row;
- }
- totalError /= 1024.0f;
-
- if (LOG_TIMING) {
- long finalMillis = System.currentTimeMillis();
- Log.d(LOG_TAG, "rs: first part took " + (middleMillis - startMillis) + "ms");
- Log.d(LOG_TAG, "rs: last part took " + (finalMillis - middleMillis) + "ms");
- }
- if (LOG_CALC) {
- Log.d(LOG_TAG, "rs: error " + totalError + ", pixels " + interestingPixels);
- }
- return totalError / interestingPixels;
- }
-
- public void calcErrorHeatmapRS(Bitmap ideal, Bitmap given, Bitmap output) {
- mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
-
- mScript.set_ideal(mIdealPixelsAllocation);
- mScript.set_given(mGivenPixelsAllocation);
-
- mOutputPixelsAllocation = Allocation.createFromBitmap(mRS, output,
- Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mScript.forEach_displayDifference(mOutputPixelsAllocation, mOutputPixelsAllocation);
- mOutputPixelsAllocation.copyTo(output);
- }
-
- public static float calcError(Bitmap ideal, Bitmap given) {
- if (LOG_TIMING) {
- startMillis = System.currentTimeMillis();
- }
-
- int interestingRegions = 0;
- for (int x = 0; x < ideal.getWidth(); x += REGION_SIZE) {
- for (int y = 0; y < ideal.getWidth(); y += REGION_SIZE) {
- if (inspectRegion(ideal, x, y)) {
- interestingRegions++;
- }
- }
- }
-
- int interestingPixels = Math.max(1, interestingRegions) * REGION_SIZE * REGION_SIZE;
-
- if (LOG_TIMING) {
- long startMillis2 = System.currentTimeMillis();
- }
-
- float totalError = 0;
- for (int x = 0; x < ideal.getWidth(); x++) {
- for (int y = 0; y < ideal.getHeight(); y++) {
- int idealColor = ideal.getPixel(x, y);
- int givenColor = given.getPixel(x, y);
- if (idealColor == givenColor)
- continue;
- totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
- totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
- totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
- totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
- }
- }
- totalError /= 1024.0f;
- if (LOG_TIMING) {
- long finalMillis = System.currentTimeMillis();
- Log.d(LOG_TAG, "dvk: first part took " + (middleMillis - startMillis) + "ms");
- Log.d(LOG_TAG, "dvk: last part took " + (finalMillis - middleMillis) + "ms");
- }
- if (LOG_CALC) {
- Log.d(LOG_TAG, "dvk: error " + totalError + ", pixels " + interestingPixels);
- }
- return totalError / interestingPixels;
- }
-
- private static boolean inspectRegion(Bitmap ideal, int x, int y) {
- int regionColor = ideal.getPixel(x, y);
- for (int i = 0; i < REGION_SIZE; i++) {
- for (int j = 0; j < REGION_SIZE; j++) {
- if (ideal.getPixel(x + i, y + j) != regionColor)
- return true;
- }
- }
- return false;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
deleted file mode 100644
index 454fe7b50ad7..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/MainView.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-public class MainView extends View {
- Paint mPaint = new Paint();
-
- public MainView(Context context) {
- super(context);
- }
-
- public MainView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public MainView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- mPaint.reset();
- DisplayModifier.apply(mPaint, canvas);
-
- if (mDrawCallback != null) {
- mDrawCallback.run();
- }
- }
-
- private Runnable mDrawCallback;
- public void addDrawCallback(Runnable drawCallback) {
- mDrawCallback = drawCallback;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
deleted file mode 100644
index 405ff65a34fd..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import com.android.test.hwuicompare.R;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-public class ManualActivity extends CompareActivity {
- private static final String LOG_TAG = "ManualActivity";
- private ImageView mCompareImageView;
- private Bitmap mCompareBitmap;
- private TextView mErrorTextView;
- private MainView mSoftwareView;
-
- private static final int COMPARE_VIEW_UNINITIALIZED = -1;
- private static final int COMPARE_VIEW_HARDWARE = 0;
- private static final int COMPARE_VIEW_SOFTWARE = 1;
- private static final int COMPARE_VIEW_HEATMAP = 2; // TODO: add more like this? any ideas?
-
- private int mCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
- private int mLastCompareImageViewState = COMPARE_VIEW_UNINITIALIZED;
-
- Runnable mRunnable = new Runnable() {
- @Override
- public void run() {
- Log.d(LOG_TAG, "mRunnable running, mRedrewFlag = " + mRedrewFlag);
-
- if (mRedrewFlag) {
- loadBitmaps();
- // recalculate error
- float error = mErrorCalculator.calcErrorRS(mSoftwareBitmap, mHardwareBitmap);
- String modname = "";
- for (String s : DisplayModifier.getLastAppliedModifications()) {
- modname = modname.concat(s + ".");
- }
-
- Log.d(LOG_TAG, "error for " + modname + " is " + error);
- mErrorTextView.setText(String.format("%.4f", error));
- }
-
- if (mCompareImageViewState != mLastCompareImageViewState || mRedrewFlag) {
- switch (mCompareImageViewState) {
- case COMPARE_VIEW_UNINITIALIZED:
- // set to hardware
- case COMPARE_VIEW_HARDWARE:
- mCompareImageView.setImageBitmap(mHardwareBitmap);
- break;
- case COMPARE_VIEW_SOFTWARE:
- mCompareImageView.setImageBitmap(mSoftwareBitmap);
- break;
- case COMPARE_VIEW_HEATMAP:
- mErrorCalculator.calcErrorHeatmapRS(mSoftwareBitmap, mHardwareBitmap,
- mCompareBitmap);
- mCompareImageView.setImageBitmap(mCompareBitmap);
- break;
- }
- mCompareImageView.getDrawable().setFilterBitmap(false);
- mCompareImageView.invalidate();
- }
-
- mLastCompareImageViewState = mCompareImageViewState;
- mRedrewFlag = false;
- mHandler.removeCallbacks(mRunnable);
- }
- };
-
- private void redrawViews() {
- mHardwareView.invalidate();
- mSoftwareView.invalidate();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.manual_layout);
- onCreateCommon(mRunnable);
-
- mSoftwareView = (MainView) findViewById(R.id.software_view);
- mSoftwareView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- mSoftwareView.setBackgroundColor(Color.WHITE);
- mSoftwareView.addDrawCallback(mDrawCallback);
-
- mCompareImageView = (ImageView) findViewById(R.id.compare_image_view);
-
- int width = getResources().getDimensionPixelSize(R.dimen.layer_width);
- int height = getResources().getDimensionPixelSize(R.dimen.layer_height);
- mCompareBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- mErrorTextView = (TextView) findViewById(R.id.current_error);
- ((ImageButton) findViewById(R.id.next)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- DisplayModifier.step();
- updateSpinners();
- redrawViews();
- }
- });
- ((ImageButton) findViewById(R.id.previous)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- DisplayModifier.stepBack();
- updateSpinners();
- redrawViews();
- }
- });
- ((Button) findViewById(R.id.show_hardware_version))
- .setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_HARDWARE;
- mHandler.post(mRunnable);
- }
- });
- ((Button) findViewById(R.id.show_software_version))
- .setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_SOFTWARE;
- mHandler.post(mRunnable);
- }
- });
- ((Button) findViewById(R.id.show_error_heatmap)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mCompareImageViewState = COMPARE_VIEW_HEATMAP;
- mHandler.post(mRunnable);
- }
- });
-
- buildSpinnerLayout();
- }
-
- private class DisplayModifierSpinner extends Spinner {
- private final int mIndex;
-
- public DisplayModifierSpinner(int index) {
- super(ManualActivity.this);
- mIndex = index;
- setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parentView, View selectedItem,
- int position, long id) {
- DisplayModifier.setIndex(mIndex, position);
- redrawViews();
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parentView) {
- }
- });
- }
- }
-
- private Spinner[] mSpinners;
-
- private void buildSpinnerLayout() {
- LinearLayout layout = (LinearLayout) findViewById(R.id.spinner_layout);
- String[][] mapsStrings = DisplayModifier.getStrings();
- mSpinners = new Spinner[mapsStrings.length];
- int index = 0;
- for (String[] spinnerValues : mapsStrings) {
- mSpinners[index] = new DisplayModifierSpinner(index);
- mSpinners[index].setAdapter(new ArrayAdapter<String>(this,
- android.R.layout.simple_spinner_dropdown_item, spinnerValues));
- layout.addView(mSpinners[index]);
- index++;
- }
- Log.d(LOG_TAG, "created " + index + " spinners");
- }
-
- private void updateSpinners() {
- int[] indices = DisplayModifier.getIndices();
- for (int i = 0; i < mSpinners.length; i++) {
- mSpinners[i].setSelection(indices[i]);
- }
- }
-
- @Override
- protected boolean forceRecreateBitmaps() {
- // continually recreate bitmaps to avoid modifying bitmaps currently being drawn
- return true;
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
deleted file mode 100644
index d5224813c0bc..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ResourceModifiers.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.test.hwuicompare;
-
-import com.android.test.hwuicompare.R;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapShader;
-import android.graphics.Color;
-import android.graphics.ComposeShader;
-import android.graphics.LinearGradient;
-import android.graphics.PorterDuff;
-import android.graphics.RadialGradient;
-import android.graphics.SweepGradient;
-import android.graphics.Matrix;
-import android.graphics.Shader;
-
-public class ResourceModifiers {
- public final BitmapShader mRepeatShader;
- public final BitmapShader mTranslatedShader;
- public final BitmapShader mScaledShader;
- private final int mTexWidth;
- private final int mTexHeight;
- private final float mDrawWidth;
- private final float mDrawHeight;
- public final LinearGradient mHorGradient;
- public final LinearGradient mDiagGradient;
- public final LinearGradient mVertGradient;
- public final RadialGradient mRadGradient;
- public final SweepGradient mSweepGradient;
- public final ComposeShader mComposeShader;
- public final ComposeShader mBadComposeShader;
- public final ComposeShader mAnotherBadComposeShader;
- public final Bitmap mBitmap;
- private final Matrix mMtx1;
- private final Matrix mMtx2;
- private final Matrix mMtx3;
-
- public final float[] mBitmapVertices;
- public final int[] mBitmapColors;
-
- private static ResourceModifiers sInstance = null;
- public static ResourceModifiers instance() { return sInstance; }
- public static void init(Resources resources) {
- sInstance = new ResourceModifiers(resources);
- }
-
- public ResourceModifiers(Resources resources) {
- mBitmap = BitmapFactory.decodeResource(resources, R.drawable.sunset1);
- mTexWidth = mBitmap.getWidth();
- mTexHeight = mBitmap.getHeight();
-
- mDrawWidth = resources.getDimensionPixelSize(R.dimen.layer_width);
- mDrawHeight = resources.getDimensionPixelSize(R.dimen.layer_height);
-
- mRepeatShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
- Shader.TileMode.REPEAT);
-
- mTranslatedShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
- Shader.TileMode.REPEAT);
- mMtx1 = new Matrix();
- mMtx1.setTranslate(mTexWidth / 2.0f, mTexHeight / 2.0f);
- mMtx1.postRotate(45, 0, 0);
- mTranslatedShader.setLocalMatrix(mMtx1);
-
- mScaledShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR,
- Shader.TileMode.MIRROR);
- mMtx2 = new Matrix();
- mMtx2.setScale(0.5f, 0.5f);
- mScaledShader.setLocalMatrix(mMtx2);
-
- mHorGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
- Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
- mMtx3 = new Matrix();
- mMtx3.setScale(mDrawHeight, 1.0f);
- mMtx3.postRotate(-90.0f);
- mMtx3.postTranslate(0.0f, mDrawHeight);
- mHorGradient.setLocalMatrix(mMtx3);
-
- mDiagGradient = new LinearGradient(0.0f, 0.0f, mDrawWidth / 2.0f, mDrawHeight / 2.0f,
- Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
-
- mVertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, mDrawHeight / 2.0f,
- Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
-
- mSweepGradient = new SweepGradient(mDrawWidth / 2.0f, mDrawHeight / 2.0f,
- Color.YELLOW, Color.MAGENTA);
-
- mComposeShader = new ComposeShader(mRepeatShader, mHorGradient,
- PorterDuff.Mode.MULTIPLY);
-
- final float width = mBitmap.getWidth() / 8.0f;
- final float height = mBitmap.getHeight() / 8.0f;
-
- mBitmapVertices = new float[] {
- 0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f,
- 0.0f, height, width, height, width * 2, height, width * 4, height,
- 0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2,
- 0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4,
- };
-
- mBitmapColors = new int[] {
- 0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000,
- 0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00,
- 0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
- 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
- };
-
- // Use a repeating gradient with many colors to test the non simple case.
- mRadGradient = new RadialGradient(mDrawWidth / 4.0f, mDrawHeight / 4.0f, 4.0f,
- mBitmapColors, null, Shader.TileMode.REPEAT);
-
- mBadComposeShader = new ComposeShader(mRadGradient, mComposeShader,
- PorterDuff.Mode.MULTIPLY);
-
- mAnotherBadComposeShader = new ComposeShader(mRadGradient, mVertGradient,
- PorterDuff.Mode.MULTIPLY);
- }
-
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
deleted file mode 100644
index 1ff153c003c1..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/Test.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.android.test.hwuicompare;
-
-import com.android.test.hwuicompare.AutomaticActivity.FinalCallback;
-
-import android.os.Bundle;
-import android.test.ActivityInstrumentationTestCase2;
-
-public class Test extends ActivityInstrumentationTestCase2<AutomaticActivity> {
- AutomaticActivity mActivity;
- private Bundle mBundle;
-
- public Test() {
- super(AutomaticActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mBundle = new Bundle();
- mActivity = getActivity();
- mActivity.setFinalCallback(new FinalCallback() {
-
- @Override
- void report(String key, float value) {
- mBundle.putFloat(key, value);
- }
- @Override
- void complete() {
- synchronized(mBundle) {
- mBundle.notify();
- }
- }
- });
- }
-
- public void testCanvas() {
- synchronized(mBundle) {
- try {
- mBundle.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- getInstrumentation().sendStatus(0, mBundle);
- }
-}
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
deleted file mode 100644
index 0a1742ef3867..000000000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript
+++ /dev/null
@@ -1,61 +0,0 @@
-#pragma version(1)
-#pragma rs java_package_name(com.android.test.hwuicompare)
-
-int REGION_SIZE;
-int WIDTH;
-int HEIGHT;
-
-rs_allocation ideal;
-rs_allocation given;
-
-void countInterestingRegions(const int32_t *v_in, int32_t *v_out) {
- int y = v_in[0];
- v_out[0] = 0;
-
- for (int x = 0; x < HEIGHT; x += REGION_SIZE) {
- bool interestingRegion = false;
- uchar4 regionColor = rsGetElementAt_uchar4(ideal, x, y);
- for (int i = 0; i < REGION_SIZE && !interestingRegion; i++) {
- for (int j = 0; j < REGION_SIZE && !interestingRegion; j++) {
- uchar4 testVal = rsGetElementAt_uchar4(ideal, x + j, y + i);
- interestingRegion |= (testVal.r != regionColor.r);
- interestingRegion |= (testVal.g != regionColor.g);
- interestingRegion |= (testVal.b != regionColor.b);
- interestingRegion |= (testVal.a != regionColor.a);
- }
- }
- if (interestingRegion) {
- v_out[0]++;
- }
- }
-}
-
-void accumulateError(const int32_t *v_in, int32_t *v_out) {
- int startY = v_in[0];
- int error = 0;
- for (int y = startY; y < startY + REGION_SIZE; y++) {
- for (int x = 0; x < HEIGHT; x++) {
- uchar4 idealPixel = rsGetElementAt_uchar4(ideal, x, y);
- uchar4 givenPixel = rsGetElementAt_uchar4(given, x, y);
-
- error += abs(idealPixel.x - givenPixel.x);
- error += abs(idealPixel.y - givenPixel.y);
- error += abs(idealPixel.z - givenPixel.z);
- error += abs(idealPixel.w - givenPixel.w);
- }
- }
- v_out[0] = error;
-}
-
-void displayDifference(const uchar4 *v_in, uchar4 *v_out, uint32_t x, uint32_t y) {
- float4 idealPixel = rsGetElementAt_float4(ideal, x, y);
- float4 givenPixel = rsGetElementAt_float4(given, x, y);
-
- float4 diff = idealPixel - givenPixel;
- float totalDiff = diff.x + diff.y + diff.z + diff.w;
- if (totalDiff < 0) {
- v_out[0] = rsPackColorTo8888(0, 0, clamp(-totalDiff/2.f, 0.f, 1.f));
- } else {
- v_out[0] = rsPackColorTo8888(clamp(totalDiff/2.f, 0.f, 1.f), 0, 0);
- }
-}
diff --git a/tests/ChoreographerTests/Android.bp b/tests/ChoreographerTests/Android.bp
new file mode 100644
index 000000000000..ca3026705c63
--- /dev/null
+++ b/tests/ChoreographerTests/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 {
+ name: "ChoreographerTests",
+ 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",
+ "truth-prebuilt",
+ ],
+ jni_libs: [
+ "libchoreographertests_jni",
+ ],
+ resource_dirs: ["src/main/res"],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/tests/ChoreographerTests/AndroidManifest.xml b/tests/ChoreographerTests/AndroidManifest.xml
new file mode 100644
index 000000000000..3283c90bd2e5
--- /dev/null
+++ b/tests/ChoreographerTests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?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="android.view.choreographertests">
+
+ <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="android.view.choreographertests"
+ android:label="Tests of android.view.ChoreographerTests">
+ </instrumentation>
+</manifest> \ No newline at end of file
diff --git a/tests/ChoreographerTests/AndroidTest.xml b/tests/ChoreographerTests/AndroidTest.xml
new file mode 100644
index 000000000000..e7176996a49a
--- /dev/null
+++ b/tests/ChoreographerTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?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 ChoreographerTests cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="test-tag" value="ChoreographerTests"/>
+ <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="ChoreographerTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.view.choreographertests" />
+ <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/ChoreographerTests/OWNERS b/tests/ChoreographerTests/OWNERS
new file mode 100644
index 000000000000..2b7de2555587
--- /dev/null
+++ b/tests/ChoreographerTests/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+include platform/frameworks/native:/services/surfaceflinger/OWNERS \ No newline at end of file
diff --git a/tests/ChoreographerTests/TEST_MAPPING b/tests/ChoreographerTests/TEST_MAPPING
new file mode 100644
index 000000000000..16a48eadc0cd
--- /dev/null
+++ b/tests/ChoreographerTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "ChoreographerTests"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/ChoreographerTests/jni/Android.bp b/tests/ChoreographerTests/jni/Android.bp
new file mode 100644
index 000000000000..7198c511489b
--- /dev/null
+++ b/tests/ChoreographerTests/jni/Android.bp
@@ -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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test_library {
+ name: "libchoreographertests_jni",
+ cflags: [
+ "-Werror",
+ "-Wthread-safety",
+ ],
+
+ gtest: false,
+
+ srcs: [
+ "ChoreographerTestsJniOnLoad.cpp",
+ "android_view_tests_ChoreographerNativeTest.cpp",
+ ],
+
+ shared_libs: [
+ "libandroid",
+ "libnativehelper",
+ "liblog",
+ ],
+
+ header_libs: [
+ "libandroid_headers_private",
+ ],
+
+ stl: "c++_static",
+}
diff --git a/tests/ChoreographerTests/jni/ChoreographerTestsJniOnLoad.cpp b/tests/ChoreographerTests/jni/ChoreographerTestsJniOnLoad.cpp
new file mode 100644
index 000000000000..447376fca78f
--- /dev/null
+++ b/tests/ChoreographerTests/jni/ChoreographerTestsJniOnLoad.cpp
@@ -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.
+ */
+#include <jni.h>
+
+#define LOG_TAG "ChoreographerTestsJniOnLoad"
+
+extern int register_android_android_view_tests_ChoreographerNativeTest(JNIEnv* env);
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv* env = NULL;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ return JNI_ERR;
+ }
+
+ if (register_android_android_view_tests_ChoreographerNativeTest(env)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+} \ No newline at end of file
diff --git a/tests/ChoreographerTests/jni/android_view_tests_ChoreographerNativeTest.cpp b/tests/ChoreographerTests/jni/android_view_tests_ChoreographerNativeTest.cpp
new file mode 100644
index 000000000000..27f4bae9e65a
--- /dev/null
+++ b/tests/ChoreographerTests/jni/android_view_tests_ChoreographerNativeTest.cpp
@@ -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.
+ *
+ */
+
+#include <android/choreographer.h>
+#include <android/log.h>
+#include <android/surface_control_jni.h>
+#include <jni.h>
+#include <private/surface_control_private.h>
+#include <time.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <chrono>
+#include <cmath>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+#undef LOG_TAG
+#define LOG_TAG "AttachedChoreographerNativeTest"
+
+// Copied from cts/tests/tests/view/jni/jniAssert.h, to be removed when integrated in CTS.
+#define ASSERT(condition, format, args...) \
+ if (!(condition)) { \
+ fail(env, format, ##args); \
+ return; \
+ }
+
+using namespace std::chrono_literals;
+
+static constexpr std::chrono::nanoseconds kMaxRuntime{1s};
+static constexpr float kFpsTolerance = 5.0f;
+
+static constexpr int kNumOfFrames = 20;
+
+struct {
+ struct {
+ jclass clazz;
+ jmethodID endTest;
+ } attachedChoreographerNativeTest;
+} gJni;
+
+struct CallbackData {
+ std::mutex mutex;
+
+ // Condition to signal callbacks are done running and test can be verified.
+ std::condition_variable_any condition;
+
+ // Flag to ensure not to lock on the condition if notify is called before wait_for.
+ bool callbacksComplete = false;
+
+ AChoreographer* choreographer = nullptr;
+ int count GUARDED_BY(mutex){0};
+ std::chrono::nanoseconds frameTime GUARDED_BY(mutex){0};
+ std::chrono::nanoseconds startTime;
+ std::chrono::nanoseconds endTime GUARDED_BY(mutex){0};
+};
+
+static std::chrono::nanoseconds now() {
+ return std::chrono::steady_clock::now().time_since_epoch();
+}
+
+static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
+ ALOGI("%s: Vsync callback running", __func__);
+ long frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData);
+
+ auto* cb = static_cast<CallbackData*>(data);
+ {
+ std::lock_guard<std::mutex> _l(cb->mutex);
+ cb->count++;
+ cb->endTime = now();
+ cb->frameTime = std::chrono::nanoseconds{frameTimeNanos};
+
+ ALOGI("%s: ran callback now %ld, frameTimeNanos %ld, new count %d", __func__,
+ static_cast<long>(cb->endTime.count()), frameTimeNanos, cb->count);
+ if (cb->endTime - cb->startTime > kMaxRuntime) {
+ cb->callbacksComplete = true;
+ cb->condition.notify_all();
+ return;
+ }
+ }
+
+ ALOGI("%s: Posting next callback", __func__);
+ AChoreographer_postVsyncCallback(cb->choreographer, vsyncCallback, data);
+}
+
+static void fail(JNIEnv* env, const char* format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ char* msg;
+ int rc = vasprintf(&msg, format, args);
+ va_end(args);
+
+ jclass exClass;
+ const char* className = "java/lang/AssertionError";
+ exClass = env->FindClass(className);
+ env->ThrowNew(exClass, msg);
+ free(msg);
+}
+
+jlong SurfaceControl_getChoreographer(JNIEnv* env, jclass, jobject surfaceControlObj) {
+ return reinterpret_cast<jlong>(
+ ASurfaceControl_getChoreographer(ASurfaceControl_fromJava(env, surfaceControlObj)));
+}
+
+static bool frameRateEquals(float fr1, float fr2) {
+ return std::abs(fr1 - fr2) <= kFpsTolerance;
+}
+
+static void endTest(JNIEnv* env, jobject clazz) {
+ env->CallVoidMethod(clazz, gJni.attachedChoreographerNativeTest.endTest);
+}
+
+static void android_view_ChoreographerNativeTest_testPostVsyncCallbackAtFrameRate(
+ JNIEnv* env, jobject clazz, jlong choreographerPtr, jfloat expectedFrameRate) {
+ AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
+ CallbackData cb;
+ cb.choreographer = choreographer;
+ cb.startTime = now();
+ ALOGI("%s: Post first callback at %ld", __func__, static_cast<long>(cb.startTime.count()));
+ AChoreographer_postVsyncCallback(choreographer, vsyncCallback, &cb);
+
+ std::scoped_lock<std::mutex> conditionLock(cb.mutex);
+ ASSERT(cb.condition.wait_for(cb.mutex, 2 * kMaxRuntime, [&cb] { return cb.callbacksComplete; }),
+ "Never received callbacks!");
+
+ float actualFrameRate = static_cast<float>(cb.count) /
+ (static_cast<double>((cb.endTime - cb.startTime).count()) / 1'000'000'000.0);
+ ALOGI("%s: callback called %d times with final start time %ld, end time %ld, effective "
+ "frame rate %f",
+ __func__, cb.count, static_cast<long>(cb.startTime.count()),
+ static_cast<long>(cb.endTime.count()), actualFrameRate);
+ ASSERT(frameRateEquals(actualFrameRate, expectedFrameRate),
+ "Effective frame rate is %f but expected to be %f", actualFrameRate, expectedFrameRate);
+
+ endTest(env, clazz);
+}
+
+static JNINativeMethod gMethods[] = {
+ {"nativeSurfaceControl_getChoreographer", "(Landroid/view/SurfaceControl;)J",
+ (void*)SurfaceControl_getChoreographer},
+ {"nativeTestPostVsyncCallbackAtFrameRate", "(JF)V",
+ (void*)android_view_ChoreographerNativeTest_testPostVsyncCallbackAtFrameRate},
+};
+
+int register_android_android_view_tests_ChoreographerNativeTest(JNIEnv* env) {
+ jclass clazz =
+ env->FindClass("android/view/choreographertests/AttachedChoreographerNativeTest");
+ gJni.attachedChoreographerNativeTest.clazz = static_cast<jclass>(env->NewGlobalRef(clazz));
+ gJni.attachedChoreographerNativeTest.endTest = env->GetMethodID(clazz, "endTest", "()V");
+ return env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerNativeTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerNativeTest.java
new file mode 100644
index 000000000000..1118eb3bfc6e
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerNativeTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.view.choreographertests;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.hardware.display.DisplayManager;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class AttachedChoreographerNativeTest {
+ private static final String TAG = "AttachedChoreographerNativeTest";
+
+ static {
+ System.loadLibrary("choreographertests_jni");
+ }
+
+ private final CountDownLatch mSurfaceCreationCountDown = new CountDownLatch(1);
+ private CountDownLatch mTestCompleteSignal;
+ private long mChoreographerPtr;
+ private SurfaceView mSurfaceView;
+ private SurfaceHolder mSurfaceHolder;
+ private ActivityScenario<GraphicsActivity> mScenario;
+ private int mInitialMatchContentFrameRate;
+ private DisplayManager mDisplayManager;
+
+ private static native long nativeSurfaceControl_getChoreographer(SurfaceControl surfaceControl);
+ private native void nativeTestPostVsyncCallbackAtFrameRate(
+ long choreographerPtr, float expectedFrameRate);
+
+ @Before
+ public void setup() throws Exception {
+ mScenario = ActivityScenario.launch(GraphicsActivity.class);
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ mScenario.onActivity(activity -> {
+ mSurfaceView = activity.findViewById(R.id.surface);
+ mSurfaceHolder = mSurfaceView.getHolder();
+ mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(
+ SurfaceHolder holder, int format, int width, int height) {}
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurfaceCreationCountDown.countDown();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {}
+ });
+ });
+
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ uiDevice.wakeUp();
+ uiDevice.executeShellCommand("wm dismiss-keyguard");
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
+ android.Manifest.permission.LOG_COMPAT_CHANGE,
+ android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+ android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
+ Manifest.permission.MANAGE_GAME_MODE);
+ mScenario.onActivity(activity -> {
+ mDisplayManager = activity.getSystemService(DisplayManager.class);
+ mInitialMatchContentFrameRate =
+ toSwitchingType(mDisplayManager.getMatchContentFrameRateUserPreference());
+ mDisplayManager.setRefreshRateSwitchingType(
+ DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
+ });
+ }
+
+ @After
+ public void tearDown() {
+ mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
+ mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void test_choreographer_callbacksForVariousFrameRates() {
+ for (int divisor : new int[] {2, 3}) {
+ mTestCompleteSignal = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds= */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+
+ SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
+ mChoreographerPtr = nativeSurfaceControl_getChoreographer(surfaceControl);
+ Log.i(TAG, "mChoreographerPtr value " + mChoreographerPtr);
+
+ float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate();
+ float expectedFrameRate = displayRefreshRate / divisor;
+
+ SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction
+ .setFrameRate(surfaceControl, expectedFrameRate,
+ Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
+ .addTransactionCommittedListener(Runnable::run,
+ () -> {
+ assertTrue(mChoreographerPtr != 0L);
+ Log.i(TAG, "Testing frame rate of " + expectedFrameRate);
+ nativeTestPostVsyncCallbackAtFrameRate(
+ mChoreographerPtr, expectedFrameRate);
+ })
+ .apply();
+ });
+ // wait for the previous callbacks to finish before moving to the next divisor
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds= */ 5L)) {
+ fail("Test for divisor " + divisor + " not finished in 5 seconds");
+ }
+ }
+ }
+
+ /** Call from native to trigger test completion. */
+ private void endTest() {
+ Log.i(TAG, "Signal test completion!");
+ mTestCompleteSignal.countDown();
+ }
+
+ private boolean waitForCountDown(CountDownLatch countDownLatch, long timeoutInSeconds) {
+ try {
+ return !countDownLatch.await(timeoutInSeconds, TimeUnit.SECONDS);
+ } catch (InterruptedException ex) {
+ throw new AssertionError("Test interrupted", ex);
+ }
+ }
+
+ private int toSwitchingType(int matchContentFrameRateUserPreference) {
+ switch (matchContentFrameRateUserPreference) {
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
+ return DisplayManager.SWITCHING_TYPE_NONE;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
+ return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
+ return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+ default:
+ return -1;
+ }
+ }
+}
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
new file mode 100644
index 000000000000..48d050ce4391
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -0,0 +1,481 @@
+/*
+ * 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.choreographertests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+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 android.Manifest;
+import android.app.compat.CompatChanges;
+import android.hardware.display.DisplayManager;
+import android.os.Looper;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import android.view.Choreographer;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+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 int FRAME_ITERATIONS = 21;
+ private static final int CALLBACK_MISSED_THRESHOLD = 2;
+
+ private final CountDownLatch mTestCompleteSignal = new CountDownLatch(2);
+ private final CountDownLatch mSurfaceCreationCountDown = new CountDownLatch(1);
+ private final CountDownLatch mNoCallbackSignal = new CountDownLatch(1);
+
+ private ActivityScenario<GraphicsActivity> mScenario;
+ private int mInitialMatchContentFrameRate;
+ private DisplayManager mDisplayManager;
+ private SurfaceView mSurfaceView;
+ private SurfaceHolder mSurfaceHolder;
+ private Choreographer mChoreographer;
+ private boolean mIsFirstCallback = true;
+ private int mCallbackMissedCounter = 0;
+
+ @Before
+ public void setUp() throws Exception {
+ mScenario = ActivityScenario.launch(GraphicsActivity.class);
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ mScenario.onActivity(activity -> {
+ mSurfaceView = activity.findViewById(R.id.surface);
+ mSurfaceHolder = mSurfaceView.getHolder();
+ mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurfaceCreationCountDown.countDown();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+ });
+ });
+
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ uiDevice.wakeUp();
+ uiDevice.executeShellCommand("wm dismiss-keyguard");
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
+ Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
+ Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
+ Manifest.permission.MANAGE_GAME_MODE);
+ mScenario.onActivity(activity -> {
+ mDisplayManager = activity.getSystemService(DisplayManager.class);
+ mInitialMatchContentFrameRate = toSwitchingType(
+ mDisplayManager.getMatchContentFrameRateUserPreference());
+ 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);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testCreateChoreographer() {
+ Looper testLooper = Looper.myLooper();
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ mChoreographer = sc.getChoreographer();
+ mTestCompleteSignal.countDown();
+ SurfaceControl sc1 = new SurfaceControl(sc, "AttachedChoreographerTests");
+ // Create attached choreographer with getChoreographer
+ Choreographer choreographer1 = sc1.getChoreographer();
+ assertTrue(sc1.hasChoreographer());
+ assertTrue(sc1.isValid());
+ assertEquals(choreographer1, sc1.getChoreographer());
+ assertEquals(choreographer1, sc1.getChoreographer(Looper.myLooper()));
+ assertEquals(choreographer1, sc1.getChoreographer(Looper.getMainLooper()));
+ assertThrows(IllegalStateException.class, () -> sc1.getChoreographer(testLooper));
+
+ SurfaceControl sc2 = new SurfaceControl(sc, "AttachedChoreographerTests");
+ // Create attached choreographer with Looper.myLooper
+ Choreographer choreographer2 = sc2.getChoreographer(Looper.myLooper());
+ assertTrue(sc2.hasChoreographer());
+ assertTrue(sc2.isValid());
+ assertEquals(choreographer2, sc2.getChoreographer(Looper.myLooper()));
+ assertEquals(choreographer2, sc2.getChoreographer(Looper.getMainLooper()));
+ assertEquals(choreographer2, sc2.getChoreographer());
+ assertThrows(IllegalStateException.class, () -> sc2.getChoreographer(testLooper));
+
+ SurfaceControl sc3 = new SurfaceControl(sc, "AttachedChoreographerTests");
+ // Create attached choreographer with Looper.myLooper
+ Choreographer choreographer3 = sc3.getChoreographer(Looper.getMainLooper());
+ assertTrue(sc3.hasChoreographer());
+ assertTrue(sc3.isValid());
+ assertEquals(choreographer3, sc3.getChoreographer(Looper.getMainLooper()));
+ assertEquals(choreographer3, sc3.getChoreographer(Looper.myLooper()));
+ assertEquals(choreographer3, sc3.getChoreographer());
+ assertThrows(IllegalStateException.class, () -> sc3.getChoreographer(testLooper));
+
+ assertNotEquals(choreographer1, choreographer2);
+ assertNotEquals(choreographer1, choreographer3);
+ assertNotEquals(choreographer2, choreographer3);
+ sc1.release();
+ sc2.release();
+ sc3.release();
+ mTestCompleteSignal.countDown();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
+ assertTrue(surfaceControl.hasChoreographer());
+ assertEquals(mChoreographer, surfaceControl.getChoreographer());
+ assertThrows(IllegalStateException.class,
+ () -> surfaceControl.getChoreographer(testLooper));
+ }
+
+ @Test
+ public void testCopySurfaceControl() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ // Create attached choreographer
+ sc.getChoreographer();
+ assertTrue(sc.hasChoreographer());
+
+ // Use copy constructor
+ SurfaceControl copyConstructorSc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ //Choreographer isn't copied over.
+ assertFalse(copyConstructorSc.hasChoreographer());
+ copyConstructorSc.getChoreographer();
+ assertTrue(copyConstructorSc.hasChoreographer());
+ mTestCompleteSignal.countDown();
+
+ // Use copyFrom
+ SurfaceControl copyFromSc = new SurfaceControl();
+ copyFromSc.copyFrom(sc, "AttachedChoreographerTests");
+ //Choreographer isn't copied over.
+ assertFalse(copyFromSc.hasChoreographer());
+ copyFromSc.getChoreographer();
+ assertTrue(copyFromSc.hasChoreographer());
+ mTestCompleteSignal.countDown();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testMirrorSurfaceControl() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ // Create attached choreographer
+ sc.getChoreographer();
+ assertTrue(sc.hasChoreographer());
+ mTestCompleteSignal.countDown();
+
+ // Use mirrorSurface
+ SurfaceControl mirrorSc = SurfaceControl.mirrorSurface(sc);
+ //Choreographer isn't copied over.
+ assertFalse(mirrorSc.hasChoreographer());
+ mirrorSc.getChoreographer();
+ assertTrue(mirrorSc.hasChoreographer());
+ // make SurfaceControl invalid by releasing it.
+ mirrorSc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(mirrorSc.isValid());
+ assertFalse(mirrorSc.hasChoreographer());
+ assertThrows(NullPointerException.class, mirrorSc::getChoreographer);
+ mTestCompleteSignal.countDown();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testPostFrameCallback() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ sc.getChoreographer().postFrameCallback(
+ frameTimeNanos -> mTestCompleteSignal.countDown());
+
+ SurfaceControl copySc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ Choreographer copyChoreographer = copySc.getChoreographer();
+ // make SurfaceControl invalid by releasing it.
+ copySc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(copySc.isValid());
+ copyChoreographer.postFrameCallback(frameTimeNanos -> mNoCallbackSignal.countDown());
+ assertDoesReceiveCallback();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testPostFrameCallbackDelayed() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ sc.getChoreographer(Looper.getMainLooper()).postFrameCallbackDelayed(
+ callback -> mTestCompleteSignal.countDown(),
+ /* delayMillis */ 5);
+
+ SurfaceControl copySc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ Choreographer copyChoreographer = copySc.getChoreographer();
+ // make SurfaceControl invalid by releasing it.
+ copySc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(copySc.isValid());
+ copyChoreographer.postFrameCallbackDelayed(
+ frameTimeNanos -> mNoCallbackSignal.countDown(), /* delayMillis */5);
+ assertDoesReceiveCallback();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testPostCallback() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ sc.getChoreographer().postCallback(Choreographer.CALLBACK_COMMIT,
+ mTestCompleteSignal::countDown, /* token */ this);
+
+ SurfaceControl copySc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ Choreographer copyChoreographer = copySc.getChoreographer();
+ // make SurfaceControl invalid by releasing it.
+ copySc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(copySc.isValid());
+ copyChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
+ mNoCallbackSignal::countDown, /* token */ this);
+ assertDoesReceiveCallback();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testPostCallbackDelayed() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ sc.getChoreographer().postCallbackDelayed(Choreographer.CALLBACK_COMMIT,
+ mTestCompleteSignal::countDown, /* token */ this, /* delayMillis */ 5);
+
+ SurfaceControl copySc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ Choreographer copyChoreographer = copySc.getChoreographer();
+ // make SurfaceControl invalid by releasing it.
+ copySc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(copySc.isValid());
+ copyChoreographer.postCallbackDelayed(Choreographer.CALLBACK_COMMIT,
+ mNoCallbackSignal::countDown, /* token */ this, /* delayMillis */ 5);
+ assertDoesReceiveCallback();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testPostVsyncCallback() {
+ mScenario.onActivity(activity -> {
+ if (waitForCountDown(mSurfaceCreationCountDown, /* timeout */ 1L)) {
+ fail("Unable to create surface within 1 Second");
+ }
+ SurfaceControl sc = mSurfaceView.getSurfaceControl();
+ sc.getChoreographer().postVsyncCallback(data -> mTestCompleteSignal.countDown());
+
+ SurfaceControl copySc = new SurfaceControl(sc, "AttachedChoreographerTests");
+ Choreographer copyChoreographer = copySc.getChoreographer();
+ // make SurfaceControl invalid by releasing it.
+ copySc.release();
+
+ assertTrue(sc.isValid());
+ assertFalse(copySc.isValid());
+ copyChoreographer.postVsyncCallback(data -> mNoCallbackSignal.countDown());
+ assertDoesReceiveCallback();
+ });
+ if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
+ fail("Test not finished in 2 Seconds");
+ }
+ }
+
+ @Test
+ public void testChoreographerDivisorRefreshRate() {
+ for (int divisor : new int[]{2, 3}) {
+ 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 = displayRefreshRate / divisor;
+ long callbackDurationMs = Math.round(1000 / fps);
+ 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");
+ }
+ }
+ }
+
+ private void verifyVsyncCallbacks(Choreographer choreographer, long callbackDurationMs,
+ CountDownLatch continueLatch, int frameCount) {
+ long callbackRequestedTimeNs = System.nanoTime();
+ choreographer.postVsyncCallback(frameData -> {
+ 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) {
+ mCallbackMissedCounter++;
+ Log.e(TAG, "Frame #" + Math.abs(frameCount - FRAME_ITERATIONS)
+ + " vsync callback failed, expected callback in "
+ + callbackDurationMs
+ + " With threshold of " + THRESHOLD_MS
+ + " but actual duration difference is " + callbackDurationDiffMs);
+ }
+ }
+ mIsFirstCallback = false;
+ verifyVsyncCallbacks(choreographer, callbackDurationMs,
+ continueLatch, frameCount - 1);
+ } else {
+ 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);
+ } catch (InterruptedException ex) {
+ throw new AssertionError("Test interrupted", ex);
+ }
+ }
+
+ private int toSwitchingType(int matchContentFrameRateUserPreference) {
+ switch (matchContentFrameRateUserPreference) {
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
+ return DisplayManager.SWITCHING_TYPE_NONE;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
+ return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
+ return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+ default:
+ return -1;
+ }
+ }
+
+ private void assertDoesReceiveCallback() {
+ try {
+ if (mNoCallbackSignal.await(/* timeout */ 50L, TimeUnit.MILLISECONDS)) {
+ fail("Callback not supposed to be generated");
+ } else {
+ mTestCompleteSignal.countDown();
+ }
+ } catch (InterruptedException e) {
+ fail("Callback wait is interrupted " + e);
+ }
+ }
+}
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/GraphicsActivity.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/GraphicsActivity.java
new file mode 100644
index 000000000000..50a68508334e
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/GraphicsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.choreographertests;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class GraphicsActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_attached_choreographer);
+ }
+}
diff --git a/tests/ChoreographerTests/src/main/res/layout/activity_attached_choreographer.xml b/tests/ChoreographerTests/src/main/res/layout/activity_attached_choreographer.xml
new file mode 100644
index 000000000000..d6c821228c9a
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/res/layout/activity_attached_choreographer.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/ChoreographerTests/src/main/res/mipmap-hdpi/ic_launcher.png b/tests/ChoreographerTests/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000000..cde69bcccec6
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/ChoreographerTests/src/main/res/mipmap-mdpi/ic_launcher.png b/tests/ChoreographerTests/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..c133a0cbd379
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/ChoreographerTests/src/main/res/mipmap-xhdpi/ic_launcher.png b/tests/ChoreographerTests/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..bfa42f0e7b91
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/ChoreographerTests/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tests/ChoreographerTests/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..324e72cdd748
--- /dev/null
+++ b/tests/ChoreographerTests/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/CanvasCompare/res/values/values.xml b/tests/ChoreographerTests/src/main/res/values/strings.xml
index f69378d34f65..e66b001e105e 100644
--- a/tests/CanvasCompare/res/values/values.xml
+++ b/tests/ChoreographerTests/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
@@ -14,12 +14,5 @@
limitations under the License.
-->
<resources>
-
- <!-- NOTE: the below MUST be multiples of 64 -->
- <dimen name="layer_height">320px</dimen>
- <dimen name="layer_width">320px</dimen>
-
- <dimen name="layer_height_double">640px</dimen>
- <dimen name="layer_width_double">640px</dimen>
-
+ <string name="app_name">ChoreographerTests</string>
</resources>
diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp
index ddbf16817b94..7fbe3b37f99e 100644
--- a/tests/Codegen/Android.bp
+++ b/tests/Codegen/Android.bp
@@ -24,6 +24,14 @@ android_test {
plugins: [
"staledataclass-annotation-processor",
],
+ // Exports needed for staledataclass-annotation-processor, see b/139342589.
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
static_libs: [
"junit",
"hamcrest",
diff --git a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
index 5430dee5ca31..4299e0d616fb 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
+++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
@@ -19,8 +19,11 @@ package com.android.server.pm.dex;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeFalse;
+
import android.app.UiAutomation;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
@@ -84,7 +87,7 @@ public final class DynamicCodeLoggerIntegrationTests {
// avoid flakiness we run these tests multiple times, allowing progressively longer between
// code loading and checking the logs on each try.)
private static final int AUDIT_LOG_RETRIES = 10;
- private static final int RETRY_DELAY_MS = 2_000;
+ private static final int RETRY_DELAY_MS = 500;
private static Context sContext;
private static int sMyUid;
@@ -96,7 +99,12 @@ public final class DynamicCodeLoggerIntegrationTests {
}
@Before
- public void primeEventLog() {
+ public void setup() {
+ assumeFalse(sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+ primeEventLog();
+ }
+
+ private void primeEventLog() {
// Force a round trip to logd to make sure everything is up to date.
// Without this the first test passes and others don't - we don't see new events in the
// log. The exact reason is unclear.
@@ -245,7 +253,7 @@ public final class DynamicCodeLoggerIntegrationTests {
"/DynamicCodeLoggerNativeExecutable", privateCopyFile);
EventLog.writeEvent(EventLog.getTagCode("auditd"),
- "type=1400 avc: granted { execute_no_trans } "
+ "type=1400 avc: granted { execute_no_trans } "
+ "path=\"" + privateCopyFile + "\" "
+ "scontext=u:r:untrusted_app: "
+ "tcontext=u:object_r:app_data_file: "
diff --git a/tests/EnforcePermission/Android.bp b/tests/EnforcePermission/Android.bp
new file mode 100644
index 000000000000..719a89817a9d
--- /dev/null
+++ b/tests/EnforcePermission/Android.bp
@@ -0,0 +1,22 @@
+// 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"],
+}
+
+filegroup {
+ name: "frameworks-enforce-permission-test-aidl",
+ srcs: ["aidl/**/*.aidl"],
+}
diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS
new file mode 100644
index 000000000000..39550a394f33
--- /dev/null
+++ b/tests/EnforcePermission/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 315013
+tweek@google.com
+brufino@google.com
diff --git a/tests/EnforcePermission/TEST_MAPPING b/tests/EnforcePermission/TEST_MAPPING
new file mode 100644
index 000000000000..a1bf42a44e86
--- /dev/null
+++ b/tests/EnforcePermission/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "EnforcePermissionTests"
+ }
+ ]
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target01.java b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl
index 06f7a13f73d7..1eb773dc19b8 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/b/Target01.java
+++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/INested.aidl
@@ -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.
@@ -13,7 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.componentalias.tests.b;
-public class Target01 extends BaseReceiver {
+package android.tests.enforcepermission;
+
+interface INested {
+ @EnforcePermission("ACCESS_NETWORK_STATE")
+ void ProtectedByAccessNetworkState();
+
+ @EnforcePermission("READ_SYNC_SETTINGS")
+ void ProtectedByReadSyncSettings();
}
diff --git a/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
new file mode 100644
index 000000000000..18e3aecfa832
--- /dev/null
+++ b/tests/EnforcePermission/aidl/android/tests/enforcepermission/IProtected.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+interface IProtected {
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternet();
+
+ @EnforcePermission("VIBRATE")
+ void ProtectedByVibrate();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndVibrateImplicitly();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndAccessNetworkStateImplicitly();
+
+ @EnforcePermission("INTERNET")
+ void ProtectedByInternetAndReadSyncSettingsImplicitly();
+}
diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp
new file mode 100644
index 000000000000..a4ac1d7c6134
--- /dev/null
+++ b/tests/EnforcePermission/service-app/Android.bp
@@ -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 {
+ // 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_helper_app {
+ name: "EnforcePermissionTestHelper",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/EnforcePermission/service-app/AndroidManifest.xml b/tests/EnforcePermission/service-app/AndroidManifest.xml
new file mode 100644
index 000000000000..ddafe15ab88f
--- /dev/null
+++ b/tests/EnforcePermission/service-app/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.service">
+ <application>
+ <service
+ android:name=".TestService"
+ android:exported="true" />
+
+ <service
+ android:name=".NestedTestService"
+ android:exported="true" />
+ </application>
+</manifest>
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
new file mode 100644
index 000000000000..7879a1214c01
--- /dev/null
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/NestedTestService.java
@@ -0,0 +1,48 @@
+/**
+ * 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.service;
+
+import android.annotation.EnforcePermission;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.tests.enforcepermission.INested;
+import android.util.Log;
+
+public class NestedTestService extends Service {
+ private static final String TAG = "EnforcePermission.NestedTestService";
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind");
+ return mBinder;
+ }
+
+ private final INested.Stub mBinder = new INested.Stub() {
+ @Override
+ @EnforcePermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public void ProtectedByAccessNetworkState() {
+ ProtectedByAccessNetworkState_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.READ_SYNC_SETTINGS)
+ 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
new file mode 100644
index 000000000000..e9b897db1294
--- /dev/null
+++ b/tests/EnforcePermission/service-app/src/android/tests/enforcepermission/service/TestService.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 android.tests.enforcepermission.service;
+
+import android.annotation.EnforcePermission;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tests.enforcepermission.INested;
+import android.tests.enforcepermission.IProtected;
+import android.util.Log;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class TestService extends Service {
+
+ private static final String TAG = "EnforcePermission.TestService";
+ private volatile ServiceConnection mNestedServiceConnection;
+
+ @Override
+ public void onCreate() {
+ mNestedServiceConnection = new ServiceConnection();
+ Intent intent = new Intent(this, NestedTestService.class);
+ boolean bound = bindService(intent, mNestedServiceConnection, Context.BIND_AUTO_CREATE);
+ if (!bound) {
+ Log.wtf(TAG, "bindService() on NestedTestService failed");
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ unbindService(mNestedServiceConnection);
+ }
+
+ private static final class ServiceConnection implements android.content.ServiceConnection {
+ private volatile CompletableFuture<INested> mFuture = new CompletableFuture<>();
+
+ public INested get() {
+ try {
+ return mFuture.get(1, TimeUnit.SECONDS);
+ } catch (ExecutionException | InterruptedException | TimeoutException e) {
+ throw new RuntimeException("Unable to reach NestedTestService: " + e.getMessage());
+ }
+ }
+
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mFuture.complete(INested.Stub.asInterface(service));
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mFuture = new CompletableFuture<>();
+ }
+ };
+
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private final IProtected.Stub mBinder = new IProtected.Stub() {
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternet() {
+ ProtectedByInternet_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.VIBRATE)
+ public void ProtectedByVibrate() {
+ ProtectedByVibrate_enforcePermission();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndVibrateImplicitly() {
+ ProtectedByInternetAndVibrateImplicitly_enforcePermission();
+
+ ProtectedByVibrate();
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndAccessNetworkStateImplicitly() throws RemoteException {
+ ProtectedByInternetAndAccessNetworkStateImplicitly_enforcePermission();
+
+ mNestedServiceConnection.get().ProtectedByAccessNetworkState();
+
+ }
+
+ @Override
+ @EnforcePermission(android.Manifest.permission.INTERNET)
+ public void ProtectedByInternetAndReadSyncSettingsImplicitly() throws RemoteException {
+ ProtectedByInternetAndReadSyncSettingsImplicitly_enforcePermission();
+
+ mNestedServiceConnection.get().ProtectedByReadSyncSettings();
+ }
+ };
+}
diff --git a/tests/EnforcePermission/test-app/Android.bp b/tests/EnforcePermission/test-app/Android.bp
new file mode 100644
index 000000000000..cd53854189b7
--- /dev/null
+++ b/tests/EnforcePermission/test-app/Android.bp
@@ -0,0 +1,38 @@
+// 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: "EnforcePermissionTests",
+ srcs: [
+ "src/**/*.java",
+ ":frameworks-enforce-permission-test-aidl",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ ],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ data: [
+ ":EnforcePermissionTestHelper",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+ test_suites: ["device-tests"],
+}
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/EnforcePermission/test-app/AndroidManifest.xml
index f7454c752b7d..4a0c6a86628f 100644
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ b/tests/EnforcePermission/test-app/AndroidManifest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
@@ -13,17 +13,20 @@
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.hardware.soundtrigger">
- <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
+ package="android.tests.enforcepermission.tests">
+
+ <!-- Expected for the tests (not actually used) -->
<uses-permission android:name="android.permission.INTERNET" />
-
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+
+ <queries>
+ <package android:name="android.tests.enforcepermission.service" />
+ </queries>
+
<application>
<uses-library android:name="android.test.runner" />
</application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="android.hardware.soundtrigger"
- android:label="Tests for android.hardware.soundtrigger" />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.tests.enforcepermission.tests"/>
</manifest>
diff --git a/tests/EnforcePermission/test-app/AndroidTest.xml b/tests/EnforcePermission/test-app/AndroidTest.xml
new file mode 100644
index 000000000000..120381a7fb83
--- /dev/null
+++ b/tests/EnforcePermission/test-app/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?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 End-to-End Tests">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="EnforcePermissionTestHelper.apk"/>
+ <option name="test-file-name" value="EnforcePermissionTests.apk"/>
+ <option name="cleanup-apks" value="true" />
+ </target_preparer>
+
+ <option name="test-tag" value="EnforcePermissionTests"/>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="android.tests.enforcepermission.tests"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+</configuration>
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
new file mode 100644
index 000000000000..d2a4a037f125
--- /dev/null
+++ b/tests/EnforcePermission/test-app/src/android/tests/enforcepermission/tests/ServiceTest.java
@@ -0,0 +1,129 @@
+/**
+ * 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.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertThrows;
+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.os.RemoteException;
+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.Before;
+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;
+
+@RunWith(AndroidJUnit4.class)
+public class ServiceTest {
+
+ private static final String TAG = "EnforcePermission.Tests";
+ private static final String SERVICE_NAME = "android.tests.enforcepermission.service";
+ private static final int SERVICE_TIMEOUT_SEC = 5;
+
+ private Context mContext;
+ private volatile ServiceConnection mServiceConnection;
+
+ @Before
+ public void bindTestService() throws Exception {
+ Log.d(TAG, "bindTestService");
+ mContext = InstrumentationRegistry.getTargetContext();
+ mServiceConnection = new ServiceConnection();
+ Intent intent = new Intent();
+ intent.setClassName(SERVICE_NAME, SERVICE_NAME + ".TestService");
+ assertTrue(mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE));
+ }
+
+ @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 testImmediatePermissionGranted_succeeds()
+ throws RemoteException {
+ mServiceConnection.get().ProtectedByInternet();
+ }
+
+ @Test
+ public void testImmediatePermissionNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByVibrate());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedButImplicitLocalNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get().ProtectedByInternetAndVibrateImplicitly());
+ assertThat(ex.getMessage(), containsString("VIBRATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedButImplicitNestedNotGranted_fails()
+ throws RemoteException {
+ final Exception ex = assertThrows(SecurityException.class,
+ () -> mServiceConnection.get()
+ .ProtectedByInternetAndAccessNetworkStateImplicitly());
+ assertThat(ex.getMessage(), containsString("ACCESS_NETWORK_STATE"));
+ }
+
+ @Test
+ public void testImmediatePermissionGrantedAndImplicitNestedGranted_succeeds()
+ throws RemoteException {
+ mServiceConnection.get().ProtectedByInternetAndReadSyncSettingsImplicitly();
+ }
+}
diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
index 2e515705a253..761efe4a8484 100644
--- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
+++ b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
@@ -110,7 +110,7 @@ public class FixVibrateSetting extends Activity implements View.OnClickListener
private void test() {
Intent intent = new Intent(this, FixVibrateSetting.class);
- PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
+ PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(this)
.setSmallIcon(R.drawable.stat_sys_warning)
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 7731e098d9f5..4ba538ed9d45 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -42,10 +42,16 @@ android_test {
"androidx.test.ext.junit",
"flickertestapplib",
"flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"launcher-helper-lib",
"launcher-aosp-tapl",
"platform-test-annotations",
+ "wm-flicker-window-extensions",
+ ],
+ data: [
+ ":FlickerTestApp",
],
}
@@ -64,6 +70,7 @@ java_library {
],
static_libs: [
"flickerlib",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
],
@@ -79,9 +86,27 @@ java_library {
"**/helpers/*",
],
static_libs: [
- "flickerlib",
"flickertestapplib",
+ "flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
+ "wm-flicker-window-extensions",
+ ],
+}
+
+android_library_import {
+ name: "wm-flicker-window-extensions_nodeps",
+ aars: ["libs/window-extensions-release.aar"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "wm-flicker-window-extensions",
+ sdk_version: "current",
+ static_libs: [
+ "wm-flicker-window-extensions_nodeps",
],
+ installable: false,
}
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index fda609196456..462f91bc081c 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -40,9 +40,12 @@
<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 write directly to /sdcard/ -->
- <application android:requestLegacyExternalStorage="true">
+ <application android:requestLegacyExternalStorage="true" android:largeHeap="true">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 566c725a3414..32ff243921ec 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -13,8 +13,26 @@
<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" />
+ <!-- Ensure output directory is empty at the start -->
+ <option name="run-command" value="rm -rf /sdcard/flicker" />
+ <!-- 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" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <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"/>
diff --git a/tests/FlickerTests/libs/window-extensions-release.aar b/tests/FlickerTests/libs/window-extensions-release.aar
new file mode 100644
index 000000000000..918e514f4c89
--- /dev/null
+++ b/tests/FlickerTests/libs/window-extensions-release.aar
Binary files differ
diff --git a/tests/FlickerTests/res/anim/show_hide_show_3000ms.xml b/tests/FlickerTests/res/anim/show_hide_show_3000ms.xml
new file mode 100644
index 000000000000..7b3f07e3a2f5
--- /dev/null
+++ b/tests/FlickerTests/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/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
new file mode 100644
index 000000000000..efd9b004f8fd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -0,0 +1,209 @@
+/*
+ * 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.wm.flicker
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.Assume
+import org.junit.AssumptionViolatedException
+import org.junit.Test
+
+/**
+ * Base test class containing common assertions for [ComponentNameMatcher.NAV_BAR],
+ * [ComponentNameMatcher.TASK_BAR], [ComponentNameMatcher.STATUS_BAR], and general assertions
+ * (layers visible in consecutive states, entire screen covered, etc.)
+ */
+abstract class BaseTest
+@JvmOverloads
+constructor(
+ protected val flicker: FlickerTest,
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+ protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
+) {
+ init {
+ tapl.setExpectedRotationCheckEnabled(true)
+ }
+
+ private val logTag = this::class.java.simpleName
+
+ /** Specification of the test transition to execute */
+ abstract val transition: FlickerBuilder.() -> Unit
+
+ /**
+ * Entry point for the test runner. It will use this method to initialize and cache flicker
+ * executions
+ */
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup { flicker.scenario.setIsTablet(wmHelper.currentState.wmState.isTablet) }
+ transition()
+ }
+ }
+
+ /** Checks that all parts of the screen are covered during the transition */
+ @Presubmit @Test open fun entireScreenCovered() = flicker.entireScreenCovered()
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ open fun navBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
+ * transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ open fun navBarLayerPositionAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerPositionAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ open fun navBarWindowIsAlwaysVisible() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ flicker.navBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible at the start and end of the
+ * transition
+ *
+ * Note: Phones only
+ */
+ @Presubmit
+ @Test
+ open fun navBarWindowIsVisibleAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarWindowIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of the
+ * transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
+ @Test
+ open fun taskBarLayerIsVisibleAtStartAndEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
+ @Test
+ open fun taskBarWindowIsAlwaysVisible() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarWindowIsAlwaysVisible()
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of
+ * the transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarLayerIsVisibleAtStartAndEnd() = flicker.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarLayerPositionAtStartAndEnd() = flicker.statusBarLayerPositionAtStartAndEnd()
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
+ * transition
+ */
+ @Presubmit
+ @Test
+ open fun statusBarWindowIsAlwaysVisible() = flicker.statusBarWindowIsAlwaysVisible()
+
+ /**
+ * Checks that all layers that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() }
+ }
+
+ /**
+ * Checks that all windows that are visible on the trace, are visible for at least 2 consecutive
+ * entries.
+ */
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm { this.visibleWindowsShownMoreThanOneConsecutiveEntry() }
+ }
+
+ open fun cujCompleted() {
+ runAndIgnoreAssumptionViolation { entireScreenCovered() }
+ runAndIgnoreAssumptionViolation { statusBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { statusBarLayerPositionAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { statusBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { visibleLayersShownMoreThanOneConsecutiveEntry() }
+ runAndIgnoreAssumptionViolation { visibleWindowsShownMoreThanOneConsecutiveEntry() }
+ runAndIgnoreAssumptionViolation { taskBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsVisibleAtStartAndEnd() }
+ }
+
+ protected fun runAndIgnoreAssumptionViolation(predicate: () -> Unit) {
+ try {
+ predicate()
+ } catch (e: AssumptionViolatedException) {
+ Log.e(logTag, "Assumption violation on CUJ complete", e)
+ }
+ }
+}
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 7f309e1974e1..ed9e14fd86da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -15,185 +15,301 @@
*/
@file:JvmName("CommonAssertions")
+
package com.android.server.wm.flicker
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.traces.common.FlickerComponentName
+import android.tools.common.PlatformConsts
+import android.tools.common.flicker.subject.region.RegionSubject
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.IComponentNameMatcher
+import android.tools.common.traces.wm.WindowManagerTrace
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.helpers.WindowUtils
-val LAUNCHER_COMPONENT = FlickerComponentName("com.google.android.apps.nexuslauncher",
- "com.google.android.apps.nexuslauncher.NexusLauncherActivity")
+/**
+ * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all
+ * WM trace entries
+ */
+fun FlickerTest.statusBarWindowIsAlwaysVisible() {
+ assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) }
+}
/**
- * Checks that [FlickerComponentName.STATUS_BAR] window is visible and above the app windows in
- * all WM trace entries
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in all WM
+ * trace entries
*/
-fun FlickerTestParameter.statusBarWindowIsVisible() {
- assertWm {
- this.isAboveAppWindowVisible(FlickerComponentName.STATUS_BAR)
- }
+fun FlickerTest.navBarWindowIsAlwaysVisible() {
+ assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
}
/**
- * Checks that [FlickerComponentName.NAV_BAR] window is visible and above the app windows in
- * all WM trace entries
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
+ * start and end of the WM trace
*/
-fun FlickerTestParameter.navBarWindowIsVisible() {
- assertWm {
- this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
- }
+fun FlickerTest.navBarWindowIsVisibleAtStartAndEnd() {
+ this.navBarWindowIsVisibleAtStart()
+ this.navBarWindowIsVisibleAtEnd()
+}
+
+/**
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
+ * start of the WM trace
+ */
+fun FlickerTest.navBarWindowIsVisibleAtStart() {
+ assertWmStart { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
+}
+
+/**
+ * Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the end
+ * of the WM trace
+ */
+fun FlickerTest.navBarWindowIsVisibleAtEnd() {
+ assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
+}
+
+/**
+ * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
+ * trace entries
+ */
+fun FlickerTest.taskBarWindowIsAlwaysVisible() {
+ assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
}
/**
- * If [allStates] is true, checks if the stack space of all displays is fully covered
- * by any visible layer, during the whole transitions
+ * Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
+ * trace entries
+ */
+fun FlickerTest.taskBarWindowIsVisibleAtEnd() {
+ assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
+}
+
+/**
+ * If [allStates] is true, checks if the stack space of all displays is fully covered by any visible
+ * layer, during the whole transitions
*
- * Otherwise, checks if the stack space of all displays is fully covered
- * by any visible layer, at the start and end of the transition
+ * Otherwise, checks if the stack space of all displays is fully covered by any visible layer, at
+ * the start and end of the transition
*
* @param allStates if all states should be checked, othersie, just initial and final
*/
@JvmOverloads
-fun FlickerTestParameter.entireScreenCovered(allStates: Boolean = true) {
+fun FlickerTest.entireScreenCovered(allStates: Boolean = true) {
if (allStates) {
assertLayers {
this.invoke("entireScreenCovered") { entry ->
- entry.entry.displays.forEach { display ->
+ entry.entry.displays.filter { it.isOn }.forEach { display ->
entry.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
}
} else {
assertLayersStart {
- this.entry.displays.forEach { display ->
+ this.entry.displays.filter { it.isOn }.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
assertLayersEnd {
- this.entry.displays.forEach { display ->
+ this.entry.displays.filter { it.isOn }.forEach { display ->
this.visibleRegion().coversAtLeast(display.layerStackSpace)
}
}
}
}
+/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace */
+fun FlickerTest.navBarLayerIsVisibleAtStart() {
+ assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
+}
+
+/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace */
+fun FlickerTest.navBarLayerIsVisibleAtEnd() {
+ assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
+}
+
/**
- * Checks that [FlickerComponentName.NAV_BAR] layer is visible at the start and end of the SF
- * trace
+ * Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF trace
*/
-fun FlickerTestParameter.navBarLayerIsVisible() {
- assertLayersStart {
- this.isVisible(FlickerComponentName.NAV_BAR)
- }
- assertLayersEnd {
- this.isVisible(FlickerComponentName.NAV_BAR)
- }
+fun FlickerTest.navBarLayerIsVisibleAtStartAndEnd() {
+ this.navBarLayerIsVisibleAtStart()
+ this.navBarLayerIsVisibleAtEnd()
}
/**
- * Checks that [FlickerComponentName.STATUS_BAR] layer is visible at the start and end of the SF
+ * Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF trace
+ */
+fun FlickerTest.taskBarLayerIsVisibleAtStartAndEnd() {
+ this.taskBarLayerIsVisibleAtStart()
+ this.taskBarLayerIsVisibleAtEnd()
+}
+
+/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF trace */
+fun FlickerTest.taskBarLayerIsVisibleAtStart() {
+ assertLayersStart { this.isVisible(ComponentNameMatcher.TASK_BAR) }
+}
+
+/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF trace */
+fun FlickerTest.taskBarLayerIsVisibleAtEnd() {
+ assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
+}
+
+/**
+ * Checks that [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of the SF
* trace
*/
-fun FlickerTestParameter.statusBarLayerIsVisible() {
- assertLayersStart {
- this.isVisible(FlickerComponentName.STATUS_BAR)
- }
- assertLayersEnd {
- this.isVisible(FlickerComponentName.STATUS_BAR)
- }
+fun FlickerTest.statusBarLayerIsVisibleAtStartAndEnd() {
+ assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
+ assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
}
/**
- * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
- * of the SF trace
+ * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start of
+ * the SF trace
*/
-fun FlickerTestParameter.navBarLayerPositionStart() {
+fun FlickerTest.navBarLayerPositionAtStart() {
assertLayersStart {
- val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
- this.visibleRegion(FlickerComponentName.NAV_BAR)
- .coversExactly(WindowUtils.getNavigationBarPosition(display))
+ val display =
+ this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!")
+ this.visibleRegion(ComponentNameMatcher.NAV_BAR)
+ .coversExactly(
+ WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
+ )
}
}
/**
- * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the end
- * of the SF trace
+ * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end of
+ * the SF trace
*/
-fun FlickerTestParameter.navBarLayerPositionEnd() {
+fun FlickerTest.navBarLayerPositionAtEnd() {
assertLayersEnd {
- val display = this.entry.displays.minByOrNull { it.id }
+ val display =
+ this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
- this.visibleRegion(FlickerComponentName.NAV_BAR)
- .coversExactly(WindowUtils.getNavigationBarPosition(display))
+ this.visibleRegion(ComponentNameMatcher.NAV_BAR)
+ .coversExactly(
+ WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation)
+ )
}
}
/**
- * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
- * and end of the SF trace
+ * Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start and
+ * end of the SF trace
*/
-fun FlickerTestParameter.navBarLayerRotatesAndScales() {
- navBarLayerPositionStart()
- navBarLayerPositionEnd()
+fun FlickerTest.navBarLayerPositionAtStartAndEnd() {
+ navBarLayerPositionAtStart()
+ navBarLayerPositionAtEnd()
}
/**
- * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
* of the SF trace
*/
-fun FlickerTestParameter.statusBarLayerPositionStart() {
+fun FlickerTest.statusBarLayerPositionAtStart(
+ wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
+) {
+ // collect navbar position for the equivalent WM state
+ val state = wmTrace?.entries?.firstOrNull() ?: error("WM state missing in $this")
+ val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
+ val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
assertLayersStart {
- val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
- this.visibleRegion(FlickerComponentName.STATUS_BAR)
- .coversExactly(WindowUtils.getStatusBarPosition(display))
+ this.visibleRegion(ComponentNameMatcher.STATUS_BAR).coversExactly(navBarPosition)
}
}
/**
- * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the end
- * of the SF trace
+ * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end of
+ * the SF trace
*/
-fun FlickerTestParameter.statusBarLayerPositionEnd() {
+fun FlickerTest.statusBarLayerPositionAtEnd(
+ wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
+) {
+ // collect navbar position for the equivalent WM state
+ val state = wmTrace?.entries?.lastOrNull() ?: error("WM state missing in $this")
+ val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found")
+ val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display)
assertLayersEnd {
- val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
- this.visibleRegion(FlickerComponentName.STATUS_BAR)
- .coversExactly(WindowUtils.getStatusBarPosition(display))
+ this.visibleRegion(ComponentNameMatcher.STATUS_BAR).coversExactly(navBarPosition)
}
}
/**
- * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
* and end of the SF trace
*/
-fun FlickerTestParameter.statusBarLayerRotatesScales() {
- statusBarLayerPositionStart()
- statusBarLayerPositionEnd()
+fun FlickerTest.statusBarLayerPositionAtStartAndEnd() {
+ statusBarLayerPositionAtStart()
+ statusBarLayerPositionAtEnd()
+}
+
+/**
+ * Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover the
+ * visibleRegion of the given app component exactly
+ */
+fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: IComponentNameMatcher) {
+ assertLayers {
+ invoke("snapshotStartingWindowLayerCoversExactlyOnApp") {
+ val snapshotLayers =
+ it.subjects.filter { subject ->
+ subject.name.contains(ComponentNameMatcher.SNAPSHOT.toLayerName()) &&
+ subject.isVisible
+ }
+ // Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
+ if (snapshotLayers.isNotEmpty()) {
+ val visibleAreas =
+ snapshotLayers
+ .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
+ .toTypedArray()
+ val snapshotRegion = RegionSubject(visibleAreas, timestamp)
+ val appVisibleRegion = it.visibleRegion(component)
+ if (snapshotRegion.region.isNotEmpty) {
+ snapshotRegion.coversExactly(appVisibleRegion.region)
+ }
+ }
+ }
+ }
}
/**
* Asserts that:
+ * ```
* [originalLayer] is visible at the start of the trace
* [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
* becomes visible
* [newLayer] remains visible until the end of the trace
*
- * @param originalLayer Layer that should be visible at the start
+ * @param originalLayer
+ * ```
+ *
+ * Layer that should be visible at the start
+ *
* @param newLayer Layer that should be visible at the end
* @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ *
+ * ```
* when checking the transition. If true we will not fail the assertion if a rotation layer is
* visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
* visible.
- * @param ignoreSnapshot If the snapshot layer should be ignored during the transition
+ * @param ignoreSnapshot
+ * ```
+ *
+ * If the snapshot layer should be ignored during the transition
+ *
+ * ```
* (useful mostly for app launch)
- * @param ignoreSplashscreen If the splashscreen layer should be ignored during the transition.
+ * @param ignoreSplashscreen
+ * ```
+ *
+ * If the splashscreen layer should be ignored during the transition.
+ *
+ * ```
* If true then we will allow for a splashscreen to be shown before the layer is shown,
* otherwise we won't and the layer must appear immediately.
+ * ```
*/
-fun FlickerTestParameter.replacesLayer(
- originalLayer: FlickerComponentName,
- newLayer: FlickerComponentName,
+fun FlickerTest.replacesLayer(
+ originalLayer: IComponentNameMatcher,
+ newLayer: IComponentNameMatcher,
ignoreEntriesWithRotationLayer: Boolean = false,
ignoreSnapshot: Boolean = false,
ignoreSplashscreen: Boolean = true
@@ -202,10 +318,10 @@ fun FlickerTestParameter.replacesLayer(
val assertion = this.isVisible(originalLayer)
if (ignoreEntriesWithRotationLayer) {
- assertion.then().isVisible(FlickerComponentName.ROTATION, isOptional = true)
+ assertion.then().isVisible(ComponentNameMatcher.ROTATION, isOptional = true)
}
if (ignoreSnapshot) {
- assertion.then().isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ assertion.then().isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
}
if (ignoreSplashscreen) {
assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
@@ -214,13 +330,7 @@ fun FlickerTestParameter.replacesLayer(
assertion.then().isVisible(newLayer)
}
- assertLayersStart {
- this.isVisible(originalLayer)
- .isInvisible(newLayer)
- }
+ assertLayersStart { this.isVisible(originalLayer).isInvisible(newLayer) }
- assertLayersEnd {
- this.isInvisible(originalLayer)
- .isVisible(newLayer)
- }
+ assertLayersEnd { this.isInvisible(originalLayer).isVisible(newLayer) }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
new file mode 100644
index 000000000000..7ef4d939fee2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.wm.flicker.activityembedding
+
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.Before
+
+abstract class ActivityEmbeddingTestBase(flicker: FlickerTest) : BaseTest(flicker) {
+ val testApp = ActivityEmbeddingAppHelper(instrumentation)
+
+ @Before
+ fun assumeActivityEmbeddingSupported() {
+ // The test should only be run on devices that support ActivityEmbedding.
+ ActivityEmbeddingAppHelper.assumeActivityEmbeddingSupportedDevice()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
new file mode 100644
index 000000000000..ed17059e79e7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+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 opening an activity that will launch another activity as ActivityEmbedding placeholder in
+ * split.
+ *
+ * To run this test: `atest FlickerTests:OpenActivityEmbeddingPlaceholderSplitTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenActivityEmbeddingPlaceholderSplitTest(flicker: FlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions { testApp.launchPlaceholderSplit(wmHelper) }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /** Main activity should become invisible after launching the placeholder primary activity. */
+ @Presubmit
+ @Test
+ fun mainActivityWindowBecomesInvisible() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+ }
+
+ /** Main activity should become invisible after launching the placeholder primary activity. */
+ @Presubmit
+ @Test
+ fun mainActivityLayerBecomesInvisible() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .then()
+ .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+ }
+
+ /**
+ * Placeholder primary and secondary should become visible after launch. The windows are not
+ * necessarily to become visible at the same time.
+ */
+ @Presubmit
+ @Test
+ fun placeholderSplitWindowsBecomeVisible() {
+ flicker.assertWm {
+ notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ .then()
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ }
+ flicker.assertWm {
+ notContains(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ .then()
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ }
+ }
+
+ /** Placeholder primary and secondary should become visible together after launch. */
+ @Presubmit
+ @Test
+ fun placeholderSplitLayersBecomeVisible() {
+ flicker.assertLayers {
+ isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT)
+ .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
new file mode 100644
index 000000000000..863828881d36
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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
+
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+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 opening a secondary activity that will split with the main activity.
+ *
+ * To run this test: `atest FlickerTests:OpenActivityEmbeddingSecondaryToSplitTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ }
+ transitions { testApp.launchSecondaryActivity(wmHelper) }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /** Main activity should remain visible when enter split from fullscreen. */
+ @Presubmit
+ @Test
+ fun mainActivityWindowIsAlwaysVisible() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ }
+
+ /**
+ * Main activity surface is animated from fullscreen to ActivityEmbedding split. During the
+ * transition, there is a period of time that it is covered by a snapshot of itself.
+ */
+ @Presubmit
+ @Test
+ fun mainActivityLayerIsAlwaysVisible() {
+ flicker.assertLayers {
+ isVisible(
+ ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT.or(
+ ComponentNameMatcher.TRANSITION_SNAPSHOT
+ )
+ )
+ }
+ flicker.assertLayersEnd {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ .isInvisible(ComponentNameMatcher.TRANSITION_SNAPSHOT)
+ }
+ }
+
+ /** Secondary activity should become visible after launching into split. */
+ @Presubmit
+ @Test
+ fun secondaryActivityWindowBecomesVisible() {
+ flicker.assertWm {
+ notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ /** Secondary activity should become visible after launching into split. */
+ @Presubmit
+ @Test
+ fun secondaryActivityLayerBecomesVisible() {
+ flicker.assertLayers {
+ isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 20a2e2228d7a..10b71ff1efd7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2020 The Android Open Source Project
*
@@ -17,16 +16,13 @@
package com.android.server.wm.flicker.close
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,83 +35,74 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:CloseAppBackButtonTest`
*
* Actions:
+ * ```
* Make sure no apps are running on the device
* Launch an app [testApp] and wait animation to complete
* Press back button
+ * ```
*
* To run only the presubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
*
* To run only the postsubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
*
* To run only the flaky assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [CloseAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+open class CloseAppBackButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
- device.pressBack()
- wmHelper.waitForHomeActivityVisible()
+ tapl.pressBack()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
}
}
/** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- /** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() {
- // This test doesn't work in shell transitions because of b/206753786
- assumeFalse(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
-
- @FlakyTest(bugId = 214452854)
- @Test
- fun statusBarLayerRotatesScales_shellTransit() {
- assumeTrue(isShellTransitionsEnabled)
- super.statusBarLayerRotatesScales()
- }
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 229762973)
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.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
new file mode 100644
index 000000000000..9fa840190fbf
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) : CloseAppBackButtonTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index d00fd7d7f09b..e5bd350019c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,14 +16,13 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,88 +35,74 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:CloseAppHomeButtonTest`
*
* Actions:
+ * ```
* Make sure no apps are running on the device
* Launch an app [testApp] and wait animation to complete
* Press home button
+ * ```
*
* To run only the presubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
*
* To run only the postsubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
*
* To run only the flaky assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [CloseAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+open class CloseAppHomeButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
+ setup { tapl.setExpectedRotationCheckEnabled(false) }
transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
+ // Can't use TAPL at the moment because of rotation test issues
+ // When pressing home, TAPL expects the orientation to remain constant
+ // However, when closing a landscape app back to a portrait-only launcher
+ // this causes an error in verifyActiveContainer();
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
}
}
/** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 227430489)
- @Test
- override fun statusBarLayerRotatesScales() {
- super.statusBarLayerRotatesScales()
- }
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun launcherLayerReplacesApp() {
- super.launcherLayerReplacesApp()
- }
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun entireScreenCovered() {
- super.entireScreenCovered()
- }
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 229762973)
+ @FlakyTest(bugId = 206753786)
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
- */
+ /** Creates the test configurations. */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
-} \ No newline at end of file
+}
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
new file mode 100644
index 000000000000..136995a78fd7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) : CloseAppHomeButtonTest(flicker) {
+ companion object {
+ /** Creates the test configurations. */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index aaa2db768792..4570fa23dc43 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -16,176 +16,59 @@
package com.android.server.wm.flicker.close
-import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.flicker.replacesLayer
import org.junit.Test
-/**
- * Base test class for transitions that close an app back to the launcher screen
- */
-abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+/** Base test class for transitions that close an app back to the launcher screen */
+abstract class CloseAppTransition(flicker: FlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
- /**
- * Specification of the test transition to execute
- */
- protected open val transition: FlickerBuilder.() -> Unit = {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- test {
- testApp.exit(wmHelper)
- }
- }
- }
-
- /**
- * Entry point for the test runner. It will use this method to initialize and cache
- * flicker executions
- */
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition()
- }
- }
-
- /**
- * Checks that the navigation bar window is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun navBarWindowIsVisible() {
- testSpec.navBarWindowIsVisible()
- }
-
- /**
- * Checks that the status bar window is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun statusBarWindowIsVisible() {
- testSpec.statusBarWindowIsVisible()
- }
-
- /**
- * Checks that the navigation bar layer is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun navBarLayerIsVisible() {
- testSpec.navBarLayerIsVisible()
- }
-
- /**
- * Checks that the status bar layer is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun statusBarLayerIsVisible() {
- testSpec.statusBarLayerIsVisible()
- }
-
- /**
- * Checks the position of the navigation bar at the start and end of the transition
- */
- @Presubmit
- @Test
- open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- /**
- * Checks the position of the status bar at the start and end of the transition
- */
- @Presubmit
- @Test
- open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- /**
- * Checks that all windows that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ testApp.launchViaIntent(wmHelper)
+ this.setRotation(flicker.scenario.startRotation)
}
+ teardown { testApp.exit(wmHelper) }
}
/**
- * Checks that all layers that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- /**
- * Checks that all parts of the screen are covered during the transition
- */
- @Presubmit
- @Test
- open fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- /**
- * Checks that [testApp] is the top visible app window at the start of the transition and
- * that it is replaced by [LAUNCHER_COMPONENT] during the transition
+ * Checks that [testApp] is the top visible app window at the start of the transition and that
+ * it is replaced by [LAUNCHER] during the transition
*/
@Presubmit
@Test
open fun launcherReplacesAppWindowAsTopWindow() {
- testSpec.assertWm {
- this.isAppWindowOnTop(testApp.component)
- .then()
- .isAppWindowOnTop(LAUNCHER_COMPONENT)
- }
+ flicker.assertWm { this.isAppWindowOnTop(testApp).then().isAppWindowOnTop(LAUNCHER) }
}
/**
- * Checks that [LAUNCHER_COMPONENT] is invisible at the start of the transition and that
- * it becomes visible during the transition
+ * Checks that [LAUNCHER] is invisible at the start of the transition and that it becomes
+ * visible during the transition
*/
@Presubmit
@Test
open fun launcherWindowBecomesVisible() {
- testSpec.assertWm {
- this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
- .then()
- .isAppWindowOnTop(LAUNCHER_COMPONENT)
- }
+ flicker.assertWm { this.isAppWindowNotOnTop(LAUNCHER).then().isAppWindowOnTop(LAUNCHER) }
}
- /**
- * Checks that [LAUNCHER_COMPONENT] layer becomes visible when [testApp] becomes invisible
- */
+ /** Checks that [LAUNCHER] layer becomes visible when [testApp] becomes invisible */
@Presubmit
@Test
open fun launcherLayerReplacesApp() {
- testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
+ flicker.replacesLayer(
+ testApp,
+ LAUNCHER,
+ ignoreEntriesWithRotationLayer = flicker.scenario.isLandscapeOrSeascapeAtStart
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
new file mode 100644
index 000000000000..daecfe7e4c4d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -0,0 +1,120 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.tools.common.PlatformConsts
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.util.Log
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import androidx.window.extensions.WindowExtensions
+import androidx.window.extensions.WindowExtensionsProvider
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Assume.assumeNotNull
+
+class ActivityEmbeddingAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.ActivityEmbedding.MainActivity.LABEL,
+ component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT
+) : StandardAppHelper(instr, launcherName, component) {
+
+ /**
+ * Clicks the button to launch the secondary activity, which should split with the main activity
+ * based on the split pair rule.
+ */
+ fun launchSecondaryActivity(wmHelper: WindowManagerStateHelper) {
+ val launchButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "launch_secondary_activity_button")),
+ FIND_TIMEOUT
+ )
+ require(launchButton != null) { "Can't find launch secondary activity button on screen." }
+ launchButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .waitForAndVerify()
+ }
+
+ /**
+ * Clicks the button to launch the placeholder primary activity, which should launch the
+ * placeholder secondary activity based on the placeholder rule.
+ */
+ fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) {
+ val launchButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")),
+ FIND_TIMEOUT
+ )
+ require(launchButton != null) { "Can't find launch placeholder split button on screen." }
+ launchButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .waitForAndVerify()
+ }
+
+ companion object {
+ private const val TAG = "ActivityEmbeddingAppHelper"
+
+ val MAIN_ACTIVITY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT.toFlickerComponent()
+
+ val SECONDARY_ACTIVITY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT.toFlickerComponent()
+
+ val PLACEHOLDER_PRIMARY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT
+ .toFlickerComponent()
+
+ val PLACEHOLDER_SECONDARY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT
+ .toFlickerComponent()
+
+ @JvmStatic
+ fun getWindowExtensions(): WindowExtensions? {
+ try {
+ return WindowExtensionsProvider.getWindowExtensions()
+ } catch (e: NoClassDefFoundError) {
+ Log.d(TAG, "Extension implementation not found")
+ } catch (e: UnsupportedOperationException) {
+ Log.d(TAG, "Stub Extension")
+ }
+ return null
+ }
+
+ @JvmStatic
+ fun getActivityEmbeddingComponent(): ActivityEmbeddingComponent? {
+ return getWindowExtensions()?.activityEmbeddingComponent
+ }
+
+ @JvmStatic
+ fun assumeActivityEmbeddingSupportedDevice() {
+ assumeNotNull(getActivityEmbeddingComponent())
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
new file mode 100644
index 000000000000..94ac1a6e1e02
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
@@ -0,0 +1,27 @@
+/*
+ * 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 com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+
+class AppPairsHelper(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ component: ComponentNameMatcher
+) : StandardAppHelper(instrumentation, activityLabel, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
new file mode 100644
index 000000000000..fde098199042
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.ComponentName
+import android.provider.Settings
+import android.tools.device.helpers.FIND_TIMEOUT
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.Assert.assertNotNull
+
+class AssistantAppHelper
+@JvmOverloads
+constructor(
+ val instr: Instrumentation,
+ val component: ComponentName = ActivityOptions.ASSISTANT_SERVICE_COMPONENT_NAME,
+) {
+ private val uiDevice: UiDevice = UiDevice.getInstance(instr)
+ private val defaultAssistant: String? =
+ Settings.Secure.getString(instr.targetContext.contentResolver, Settings.Secure.ASSISTANT)
+ private val defaultVoiceInteractionService: String? =
+ Settings.Secure.getString(
+ instr.targetContext.contentResolver,
+ Settings.Secure.VOICE_INTERACTION_SERVICE
+ )
+
+ fun setDefaultAssistant() {
+ Settings.Secure.putString(
+ instr.targetContext.contentResolver,
+ Settings.Secure.VOICE_INTERACTION_SERVICE,
+ component.flattenToString()
+ )
+ Settings.Secure.putString(
+ instr.targetContext.contentResolver,
+ Settings.Secure.ASSISTANT,
+ component.flattenToString()
+ )
+ }
+
+ fun resetDefaultAssistant() {
+ Settings.Secure.putString(
+ instr.targetContext.contentResolver,
+ Settings.Secure.VOICE_INTERACTION_SERVICE,
+ defaultVoiceInteractionService
+ )
+ Settings.Secure.putString(
+ instr.targetContext.contentResolver,
+ Settings.Secure.ASSISTANT,
+ defaultAssistant
+ )
+ }
+
+ /**
+ * Open Assistance UI.
+ *
+ * @param longpress open the UI by long pressing power button. Otherwise open the UI through
+ * vioceinteraction shell command directly.
+ */
+ @JvmOverloads
+ fun openUI(longpress: Boolean = false) {
+ if (longpress) {
+ uiDevice.executeShellCommand("input keyevent --longpress KEYCODE_POWER")
+ } else {
+ uiDevice.executeShellCommand("cmd voiceinteraction show")
+ }
+ val ui =
+ uiDevice.wait(
+ Until.findObject(By.res(ActivityOptions.FLICKER_APP_PACKAGE, "vis_frame")),
+ FIND_TIMEOUT
+ )
+ assertNotNull("Can't find Assistant UI after long pressing power button.", ui)
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index 2dbf304a0f23..c6fa1bb89220 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -17,18 +17,16 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
- class FixedOrientationAppHelper @JvmOverloads constructor(
- instr: Instrumentation,
- launcherName: String = ActivityOptions.PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
- ) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+class FixedOrientationAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.PortraitOnlyActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 75900df978df..9227e07f5b11 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -15,15 +15,22 @@
*/
@file:JvmName("FlickerExtensions")
+
package com.android.server.wm.flicker.helpers
-import com.android.server.wm.flicker.Flicker
-import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.common.Rotation
+import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
/**
* Changes the device [rotation] and wait for the rotation animation to complete
*
* @param rotation New device rotation
*/
-fun Flicker.setRotation(rotation: Int) =
- ChangeDisplayOrientationRule.setRotation(rotation, instrumentation, wmHelper)
+fun IFlickerTestData.setRotation(rotation: Rotation) =
+ ChangeDisplayOrientationRule.setRotation(
+ rotation,
+ instrumentation,
+ clearCacheAfterParsing = false,
+ wmHelper = wmHelper
+ )
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
new file mode 100644
index 000000000000..747cf3742bf7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.helpers
+
+import android.app.Instrumentation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class GameAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.Game.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.Game.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+
+ /**
+ * Swipes down in the mock game app.
+ *
+ * @return true if the swipe operation is successful.
+ */
+ fun swipeDown(): Boolean {
+ val gameView =
+ uiDevice.wait(Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS)
+ require(gameView != null) { "Mock game app view not found." }
+
+ val bound = gameView.getVisibleBounds()
+ return uiDevice.swipe(
+ bound.centerX(),
+ bound.top,
+ bound.centerX(),
+ bound.centerY(),
+ SWIPE_STEPS
+ )
+ }
+
+ /**
+ * Switches to a recent app by quick switch gesture. This function can be used in both portrait
+ * and landscape mode.
+ *
+ * @param wmHelper Helper used to get window region.
+ * @param direction UiAutomator Direction enum to indicate the swipe direction.
+ * @return true if the swipe operation is successful.
+ */
+ fun switchToPreviousAppByQuickSwitchGesture(
+ wmHelper: WindowManagerStateHelper,
+ direction: Direction
+ ): Boolean {
+ val ratioForScreenBottom = 0.99
+ val fullView = wmHelper.getWindowRegion(componentMatcher)
+ require(!fullView.isEmpty) { "Target $componentMatcher view not found." }
+
+ val bound = fullView.bounds
+ val targetYPos = bound.bottom * ratioForScreenBottom
+ val endX =
+ when (direction) {
+ Direction.LEFT -> bound.left
+ Direction.RIGHT -> bound.right
+ else -> {
+ throw IllegalStateException("Only left or right direction is allowed.")
+ }
+ }
+ return uiDevice.swipe(
+ bound.centerX(),
+ targetYPos.toInt(),
+ endX,
+ targetYPos.toInt(),
+ SWIPE_STEPS
+ )
+ }
+
+ /**
+ * Waits for view idel with timeout, then checkes the target object whether visible on screen.
+ *
+ * @param packageName The targe application's package name.
+ * @param identifier The resource id of the target object.
+ * @param timeout The timeout duration in milliseconds.
+ * @return true if the target object exists.
+ */
+ @JvmOverloads
+ fun isTargetObjVisible(
+ packageName: String,
+ identifier: String,
+ timeout: Long = WAIT_TIME_MS
+ ): Boolean {
+ uiDevice.waitForIdle(timeout)
+ return uiDevice.hasObject(By.res(packageName, identifier))
+ }
+
+ companion object {
+ private const val GAME_APP_VIEW_RES = "container"
+ private const val WAIT_TIME_MS = 3_000L
+ private const val SWIPE_STEPS = 30
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
new file mode 100644
index 000000000000..a8f1b3de564e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
@@ -0,0 +1,279 @@
+/*
+ * 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.wm.flicker.helpers;
+
+import android.annotation.NonNull;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Injects gestures given an {@link Instrumentation} object.
+ */
+public class GestureHelper {
+ // Inserted after each motion event injection.
+ private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
+
+ private final UiAutomation mUiAutomation;
+
+ /**
+ * Primary pointer should be cached here for separate release
+ */
+ @Nullable private PointerProperties mPrimaryPtrProp;
+ @Nullable private PointerCoords mPrimaryPtrCoord;
+ private long mPrimaryPtrDownTime;
+
+ /**
+ * A pair of floating point values.
+ */
+ public static class Tuple {
+ public float x;
+ public float y;
+
+ public Tuple(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+ }
+
+ public GestureHelper(Instrumentation instrumentation) {
+ mUiAutomation = instrumentation.getUiAutomation();
+ }
+
+ /**
+ * Injects a series of {@link MotionEvent}s to simulate a drag gesture without pointer release.
+ *
+ * Simulates a drag gesture without releasing the primary pointer. The primary pointer info
+ * will be cached for potential release later on by {@code releasePrimaryPointer()}
+ *
+ * @param startPoint initial coordinates of the primary pointer
+ * @param endPoint final coordinates of the primary pointer
+ * @param steps number of steps to take to animate dragging
+ * @return true if gesture is injected successfully
+ */
+ public boolean dragWithoutRelease(@NonNull Tuple startPoint,
+ @NonNull Tuple endPoint, int steps) {
+ PointerProperties ptrProp = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
+ PointerCoords ptrCoord = getPointerCoord(startPoint.x, startPoint.y, 1, 1);
+
+ PointerProperties[] ptrProps = new PointerProperties[] { ptrProp };
+ PointerCoords[] ptrCoords = new PointerCoords[] { ptrCoord };
+
+ long downTime = SystemClock.uptimeMillis();
+
+ if (!primaryPointerDown(ptrProp, ptrCoord, downTime)) {
+ return false;
+ }
+
+ // cache the primary pointer info for later potential release
+ mPrimaryPtrProp = ptrProp;
+ mPrimaryPtrCoord = ptrCoord;
+ mPrimaryPtrDownTime = downTime;
+
+ return movePointers(ptrProps, ptrCoords, new Tuple[] { endPoint }, downTime, steps);
+ }
+
+ /**
+ * Release primary pointer if previous gesture has cached the primary pointer info.
+ *
+ * @return true if the release was injected successfully
+ */
+ public boolean releasePrimaryPointer() {
+ if (mPrimaryPtrProp != null && mPrimaryPtrCoord != null) {
+ return primaryPointerUp(mPrimaryPtrProp, mPrimaryPtrCoord, mPrimaryPtrDownTime);
+ }
+
+ return false;
+ }
+
+ /**
+ * Injects a series of {@link MotionEvent} objects to simulate a pinch gesture.
+ *
+ * @param startPoint1 initial coordinates of the first pointer
+ * @param startPoint2 initial coordinates of the second pointer
+ * @param endPoint1 final coordinates of the first pointer
+ * @param endPoint2 final coordinates of the second pointer
+ * @param steps number of steps to take to animate pinching
+ * @return true if gesture is injected successfully
+ */
+ public boolean pinch(@NonNull Tuple startPoint1, @NonNull Tuple startPoint2,
+ @NonNull Tuple endPoint1, @NonNull Tuple endPoint2, int steps) {
+ PointerProperties ptrProp1 = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
+ PointerProperties ptrProp2 = getPointerProp(1, MotionEvent.TOOL_TYPE_FINGER);
+
+ PointerCoords ptrCoord1 = getPointerCoord(startPoint1.x, startPoint1.y, 1, 1);
+ PointerCoords ptrCoord2 = getPointerCoord(startPoint2.x, startPoint2.y, 1, 1);
+
+ PointerProperties[] ptrProps = new PointerProperties[] {
+ ptrProp1, ptrProp2
+ };
+
+ PointerCoords[] ptrCoords = new PointerCoords[] {
+ ptrCoord1, ptrCoord2
+ };
+
+ long downTime = SystemClock.uptimeMillis();
+
+ if (!primaryPointerDown(ptrProp1, ptrCoord1, downTime)) {
+ return false;
+ }
+
+ if (!nonPrimaryPointerDown(ptrProps, ptrCoords, downTime, 1)) {
+ return false;
+ }
+
+ if (!movePointers(ptrProps, ptrCoords, new Tuple[] { endPoint1, endPoint2 },
+ downTime, steps)) {
+ return false;
+ }
+
+ if (!nonPrimaryPointerUp(ptrProps, ptrCoords, downTime, 1)) {
+ return false;
+ }
+
+ return primaryPointerUp(ptrProp1, ptrCoord1, downTime);
+ }
+
+ private boolean primaryPointerDown(@NonNull PointerProperties prop,
+ @NonNull PointerCoords coord, long downTime) {
+ MotionEvent event = getMotionEvent(downTime, downTime, MotionEvent.ACTION_DOWN, 1,
+ new PointerProperties[]{ prop }, new PointerCoords[]{ coord });
+
+ return injectEventSync(event);
+ }
+
+ private boolean nonPrimaryPointerDown(@NonNull PointerProperties[] props,
+ @NonNull PointerCoords[] coords, long downTime, int index) {
+ // at least 2 pointers are needed
+ if (props.length != coords.length || coords.length < 2) {
+ return false;
+ }
+
+ long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_POINTER_DOWN
+ + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT), coords.length, props, coords);
+
+ return injectEventSync(event);
+ }
+
+ private boolean movePointers(@NonNull PointerProperties[] props,
+ @NonNull PointerCoords[] coords, @NonNull Tuple[] endPoints, long downTime, int steps) {
+ // the number of endpoints should be the same as the number of pointers
+ if (props.length != coords.length || coords.length != endPoints.length) {
+ return false;
+ }
+
+ // prevent division by 0 and negative number of steps
+ if (steps < 1) {
+ steps = 1;
+ }
+
+ // save the starting points before updating any pointers
+ Tuple[] startPoints = new Tuple[coords.length];
+
+ for (int i = 0; i < coords.length; i++) {
+ startPoints[i] = new Tuple(coords[i].x, coords[i].y);
+ }
+
+ MotionEvent event;
+ long eventTime;
+
+ for (int i = 0; i < steps; i++) {
+ // inject a delay between movements
+ SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
+
+ // update the coordinates
+ for (int j = 0; j < coords.length; j++) {
+ coords[j].x += (endPoints[j].x - startPoints[j].x) / steps;
+ coords[j].y += (endPoints[j].y - startPoints[j].y) / steps;
+ }
+
+ eventTime = SystemClock.uptimeMillis();
+
+ event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_MOVE,
+ coords.length, props, coords);
+
+ boolean didInject = injectEventSync(event);
+
+ if (!didInject) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean primaryPointerUp(@NonNull PointerProperties prop,
+ @NonNull PointerCoords coord, long downTime) {
+ long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_UP, 1,
+ new PointerProperties[]{ prop }, new PointerCoords[]{ coord });
+
+ return injectEventSync(event);
+ }
+
+ private boolean nonPrimaryPointerUp(@NonNull PointerProperties[] props,
+ @NonNull PointerCoords[] coords, long downTime, int index) {
+ // at least 2 pointers are needed
+ if (props.length != coords.length || coords.length < 2) {
+ return false;
+ }
+
+ long eventTime = SystemClock.uptimeMillis();
+
+ MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_POINTER_UP
+ + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT), coords.length, props, coords);
+
+ return injectEventSync(event);
+ }
+
+ private PointerCoords getPointerCoord(float x, float y, float pressure, float size) {
+ PointerCoords ptrCoord = new PointerCoords();
+ ptrCoord.x = x;
+ ptrCoord.y = y;
+ ptrCoord.pressure = pressure;
+ ptrCoord.size = size;
+ return ptrCoord;
+ }
+
+ private PointerProperties getPointerProp(int id, int toolType) {
+ PointerProperties ptrProp = new PointerProperties();
+ ptrProp.id = id;
+ ptrProp.toolType = toolType;
+ return ptrProp;
+ }
+
+ private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
+ int pointerCount, PointerProperties[] ptrProps, PointerCoords[] ptrCoords) {
+ return MotionEvent.obtain(downTime, eventTime, action, pointerCount,
+ ptrProps, ptrCoords, 0, 0, 1.0f, 1.0f,
+ 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
+ }
+
+ private boolean injectEventSync(InputEvent event) {
+ return mUiAutomation.injectInputEvent(event, true);
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
deleted file mode 100644
index aacc17a49a24..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.helpers
-
-import android.view.WindowInsets.Type.ime
-import android.view.WindowInsets.Type.navigationBars
-import android.view.WindowInsets.Type.statusBars
-
-import android.app.Instrumentation
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-
-import java.util.regex.Pattern
-
-class ImeAppAutoFocusHelper @JvmOverloads constructor(
- instr: Instrumentation,
- private val rotation: Int,
- private val imePackageName: String = IME_PACKAGE,
- launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
-) : ImeAppHelper(instr, launcherName, component) {
- override fun openIME(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper?
- ) {
- // do nothing (the app is focused automatically)
- waitIMEShown(device, wmHelper)
- }
-
- override fun launchViaIntent(
- wmHelper: WindowManagerStateHelper,
- expectedWindowName: String,
- action: String?,
- stringExtras: Map<String, String>
- ) {
- super.launchViaIntent(wmHelper, expectedWindowName, action, stringExtras)
- waitIMEShown(uiDevice, wmHelper)
- }
-
- override fun open() {
- val expectedPackage = if (rotation.isRotated()) {
- imePackageName
- } else {
- getPackage()
- }
- launcherStrategy.launch(appName, expectedPackage)
- }
-
- fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
- val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
- "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
-
- require(button != null) {
- "Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. Screen turned off)"
- }
- button.click()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(
- ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
- mInstrumentation.waitForIdleSync()
- }
- fun dismissDialog(wmHelper: WindowManagerStateHelper) {
- val dialog = uiDevice.wait(
- Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
-
- // Pressing back key to dismiss the dialog
- if (dialog != null) {
- uiDevice.pressBack()
- wmHelper.waitForAppTransitionIdle()
- }
- }
- fun getInsetsVisibleFromDialog(type: Int): Boolean {
- var insetsVisibilityTextView = uiDevice.wait(
- Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT)
- if (insetsVisibilityTextView != null) {
- var visibility = insetsVisibilityTextView.text.toString()
- val matcher = when (type) {
- ime() -> {
- Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility)
- }
- statusBars() -> {
- Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
- }
- navigationBars() -> {
- Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
- }
- else -> null
- }
- if (matcher != null && matcher.find()) {
- return matcher.group(1).equals("VISIBLE")
- }
- }
- return false
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
index 5bd365c7eefd..d1722521bba8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt
@@ -17,86 +17,63 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-open class ImeAppHelper @JvmOverloads constructor(
+open class ImeAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+ launcherName: String = ActivityOptions.Ime.Default.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
/**
* Opens the IME and wait for it to be displayed
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
- @JvmOverloads
- open fun openIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
- val editText = device.wait(
- Until.findObject(By.res(getPackage(), "plain_text_input")),
- FIND_TIMEOUT)
+ open fun openIME(wmHelper: WindowManagerStateHelper) {
+ val editText =
+ uiDevice.wait(Until.findObject(By.res(getPackage(), "plain_text_input")), FIND_TIMEOUT)
- require(editText != null) {
+ requireNotNull(editText) {
"Text field not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitIMEShown(device, wmHelper)
+ waitIMEShown(wmHelper)
}
- protected fun waitIMEShown(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper? = null
- ) {
- if (wmHelper == null) {
- device.waitForIdle()
- } else {
- wmHelper.waitImeShown()
- }
+ protected fun waitIMEShown(wmHelper: WindowManagerStateHelper) {
+ wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
}
/**
* Opens the IME and wait for it to be gone
*
- * @param device UIDevice instance to interact with the device
* @param wmHelper Helper used to wait for WindowManager states
*/
- @JvmOverloads
- open fun closeIME(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
- device.pressBack()
- // Using only the AccessibilityInfo it is not possible to identify if the IME is active
- if (wmHelper == null) {
- device.waitForIdle()
- } else {
- wmHelper.waitImeGone()
- }
+ open fun closeIME(wmHelper: WindowManagerStateHelper) {
+ uiDevice.pressBack()
+ wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify()
}
- @JvmOverloads
- open fun finishActivity(device: UiDevice, wmHelper: WindowManagerStateHelper? = null) {
- val finishButton = device.wait(
+ open fun finishActivity(wmHelper: WindowManagerStateHelper) {
+ val finishButton =
+ uiDevice.wait(
Until.findObject(By.res(getPackage(), "finish_activity_btn")),
- FIND_TIMEOUT)
- require(finishButton != null) {
- "Finish activity button not found, probably IME activity is not on the screen ?"
+ FIND_TIMEOUT
+ )
+ requireNotNull(finishButton) {
+ "Finish activity button not found, probably IME activity is not on the screen?"
}
finishButton.click()
- if (wmHelper == null) {
- device.waitForIdle()
- } else {
- wmHelper.waitForActivityRemoved(component)
- }
+ wmHelper.StateSyncBuilder().withActivityRemoved(this).waitForAndVerify()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index 172c4330c3c6..7a8d780c3d9f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -17,44 +17,40 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
+class ImeEditorPopupDialogAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- private val rotation: Int,
- private val imePackageName: String = IME_PACKAGE,
- launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ launcherName: String = ActivityOptions.Ime.EditorPopupDialogActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.Ime.EditorPopupDialogActivity.COMPONENT.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
- override fun openIME(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper?
- ) {
- val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
+ override fun openIME(wmHelper: WindowManagerStateHelper) {
+ val editText = uiDevice.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
- require(editText != null) {
+ requireNotNull(editText) {
"Text field not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
editText.click()
- waitIMEShown(device, wmHelper)
+ waitIMEShown(wmHelper)
}
fun dismissDialog(wmHelper: WindowManagerStateHelper) {
- val dismissButton = uiDevice.wait(
- Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
+ val dismissButton = uiDevice.wait(Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
// Pressing back key to dismiss the dialog
if (dismissButton != null) {
dismissButton.click()
- wmHelper.waitForAppTransitionIdle()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
new file mode 100644
index 000000000000..83a41abbd4bd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt
@@ -0,0 +1,147 @@
+/*
+ * 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 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
+import android.tools.device.helpers.IME_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.view.WindowInsets.Type.ime
+import android.view.WindowInsets.Type.navigationBars
+import android.view.WindowInsets.Type.statusBars
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import java.util.regex.Pattern
+
+class ImeShownOnAppStartHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ private val rotation: Rotation,
+ private val imePackageName: String = IME_PACKAGE,
+ launcherName: String = ActivityOptions.Ime.AutoFocusActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.Ime.AutoFocusActivity.COMPONENT.toFlickerComponent()
+) : ImeAppHelper(instr, launcherName, component) {
+ override fun openIME(wmHelper: WindowManagerStateHelper) {
+ // do nothing (the app is focused automatically)
+ waitIMEShown(wmHelper)
+ }
+
+ override fun launchViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ launchedAppComponentMatcherOverride: IComponentMatcher?,
+ action: String?,
+ stringExtras: Map<String, String>,
+ waitConditions: Array<Condition<DeviceStateDump>>
+ ) {
+ super.launchViaIntent(
+ wmHelper,
+ launchedAppComponentMatcherOverride,
+ action,
+ stringExtras,
+ waitConditions
+ )
+ waitIMEShown(wmHelper)
+ }
+
+ override fun open() {
+ val expectedPackage =
+ if (rotation.isRotated()) {
+ imePackageName
+ } else {
+ getPackage()
+ }
+ open(expectedPackage)
+ }
+
+ fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
+ val button =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "start_dialog_themed_activity_btn")),
+ FIND_TIMEOUT
+ )
+
+ requireNotNull(button) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. Screen turned off)"
+ }
+ button.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent())
+ .waitForAndVerify()
+ }
+
+ fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+ val dialog = uiDevice.wait(Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
+
+ // Pressing back key to dismiss the dialog
+ if (dialog != null) {
+ uiDevice.pressBack()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+ }
+
+ fun getInsetsVisibleFromDialog(type: Int): Boolean {
+ val insetsVisibilityTextView =
+ uiDevice.wait(Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT)
+ if (insetsVisibilityTextView != null) {
+ val visibility = insetsVisibilityTextView.text.toString()
+ val matcher =
+ when (type) {
+ ime() -> {
+ Pattern.compile("IME\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ statusBars() -> {
+ Pattern.compile("StatusBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ navigationBars() -> {
+ Pattern.compile("NavBar\\: (VISIBLE|INVISIBLE)").matcher(visibility)
+ }
+ else -> null
+ }
+ if (matcher != null && matcher.find()) {
+ return matcher.group(1).equals("VISIBLE")
+ }
+ }
+ return false
+ }
+
+ fun toggleFixPortraitOrientation(wmHelper: WindowManagerStateHelper) {
+ val button =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "toggle_fixed_portrait_btn")),
+ FIND_TIMEOUT
+ )
+ require(button != null) {
+ "Button not found, this usually happens when the device " +
+ "was left in an unknown state (e.g. Screen turned off)"
+ }
+ button.click()
+ mInstrumentation.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/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index 16c4c254f9e3..b2aeb14aaf78 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -17,18 +17,16 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-class ImeStateInitializeHelper @JvmOverloads constructor(
+class ImeStateInitializeHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.IME_ACTIVITY_INITIALIZE_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+ launcherName: String = ActivityOptions.Ime.StateInitializeActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.Ime.StateInitializeActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasTestCommon.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
index 165d728c92a6..b95d86b72f34 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasTestCommon.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
@@ -13,16 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.componentalias.tests;
-public final class ComponentAliasTestCommon {
- private ComponentAliasTestCommon() {
- }
+package com.android.server.wm.flicker.helpers
- public static final String TAG = "ComponentAliasTest";
+import android.app.Instrumentation
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.testapp.ActivityOptions
- public static final String MAIN_PACKAGE = "android.content.componentalias.tests";
-
- public static final String SUB1_PACKAGE = "android.content.componentalias.tests.sub1";
- public static final String SUB2_PACKAGE = "android.content.componentalias.tests.sub2";
-}
+class LaunchBubbleHelper(instrumentation: Instrumentation) :
+ StandardAppHelper(
+ instrumentation,
+ ActivityOptions.Bubbles.LaunchBubble.LABEL,
+ ActivityOptions.Bubbles.LaunchBubble.COMPONENT.toFlickerComponent()
+ )
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
new file mode 100644
index 000000000000..a670d68cabd9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class LetterboxAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+
+ fun clickRestart(wmHelper: WindowManagerStateHelper) {
+ val restartButton =
+ uiDevice.wait(
+ Until.findObject(By.res(SYSTEMUI_PACKAGE, "size_compat_restart_button")),
+ FIND_TIMEOUT
+ )
+ restartButton?.run { restartButton.click() } ?: error("Restart button not found")
+
+ // size compat mode restart confirmation dialog button
+ val restartDialogButton =
+ uiDevice.wait(
+ Until.findObject(
+ By.res(SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")
+ ),
+ FIND_TIMEOUT
+ )
+ restartDialogButton?.run { restartDialogButton.click() }
+ ?: error("Restart dialog button not found")
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
new file mode 100644
index 000000000000..c98f1c4b4d29
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+class MailAppHelper
+@JvmOverloads
+constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.Mail.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.Mail.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+
+ fun openMail(rowIdx: Int) {
+ val rowSel =
+ By.res(getPackage(), "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)
+ if (row != null) break
+ scrollDown()
+ }
+ require(row != null) { "" }
+ row.click()
+ uiDevice.wait(Until.gone(By.res(getPackage(), MAIL_LIST_RES_ID)), FIND_TIMEOUT)
+ }
+
+ fun scrollDown() {
+ val listView = waitForMailList()
+ listView.scroll(Direction.DOWN, 1.0f)
+ }
+
+ fun waitForMailList(): UiObject2 {
+ val sel = By.res(getPackage(), 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
+ }
+
+ companion object {
+ private const val SHORT_WAIT_TIME_MS = 5000L
+ private const val MAIL_LIST_RES_ID = "mail_recycle_view"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
new file mode 100644
index 000000000000..65175ef33afb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.Context
+import android.provider.Settings
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil
+import java.io.IOException
+
+class MultiWindowUtils(
+ instrumentation: Instrumentation,
+ activityLabel: String,
+ componentsInfo: ComponentNameMatcher
+) : StandardAppHelper(instrumentation, activityLabel, componentsInfo) {
+
+ companion object {
+ fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e(MultiWindowUtils::class.simpleName, "executeShellCommand error! $e")
+ }
+ }
+
+ fun getDevEnableNonResizableMultiWindow(context: Context): Int =
+ Settings.Global.getInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW
+ )
+
+ fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
+ Settings.Global.putInt(
+ context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW,
+ configValue
+ )
+
+ fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
+ executeShellCommand(
+ instrumentation,
+ createConfigSupportsNonResizableMultiWindowCommand(configValue)
+ )
+
+ fun resetMultiWindowConfig(instrumentation: Instrumentation) =
+ executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
+
+ private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
+ "wm set-multi-window-config --supportsNonResizable $configValue"
+
+ private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index be68704fc32d..5b3d3083fe1c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -17,36 +17,32 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-class NewTasksAppHelper @JvmOverloads constructor(
+class NewTasksAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+ launcherName: String = ActivityOptions.LaunchNewTask.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.LaunchNewTask.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
fun openNewTask(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val button = device.wait(
- Until.findObject(By.res(getPackage(), "launch_new_task")),
- FIND_TIMEOUT)
+ val button =
+ device.wait(Until.findObject(By.res(getPackage(), "launch_new_task")), FIND_TIMEOUT)
- require(button != null) {
+ requireNotNull(button) {
"Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(component)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
index f7ca5ce1c001..ee65004e9e78 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -17,18 +17,16 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-class NonResizeableAppHelper @JvmOverloads constructor(
+class NonResizeableAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+ launcherName: String = ActivityOptions.NonResizeableActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.NonResizeableActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index 4e360f98723e..7665690a3122 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -17,37 +17,34 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-class NotificationAppHelper @JvmOverloads constructor(
+class NotificationAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.NOTIFICATION_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.NOTIFICATION_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
- fun postNotification(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val button = device.wait(
- Until.findObject(By.res(getPackage(), "post_notification")),
- FIND_TIMEOUT)
+ launcherName: String = ActivityOptions.Notification.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.Notification.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
+ fun postNotification(wmHelper: WindowManagerStateHelper) {
+ val button =
+ uiDevice.wait(Until.findObject(By.res(getPackage(), "post_notification")), FIND_TIMEOUT)
- require(button != null) {
+ requireNotNull(button) {
"Post notification button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
- device.wait(Until.findObject(By.text("Flicker Test Notification")), FIND_TIMEOUT)
- ?: error("Flicker Notification not found")
+ uiDevice.wait(Until.findObject(By.text("Flicker Test Notification")), FIND_TIMEOUT)
+ ?: error("Flicker Notification not found")
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
new file mode 100644
index 000000000000..24e231c73a0f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -0,0 +1,426 @@
+/*
+ * 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 com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.media.session.MediaController
+import android.media.session.MediaSessionManager
+import android.tools.common.datatypes.Rect
+import android.tools.common.datatypes.Region
+import android.tools.common.traces.ConditionsFactory
+import android.tools.common.traces.component.IComponentMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.helpers.SYSTEMUI_PACKAGE
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
+import android.util.Log
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+
+open class PipAppHelper(instrumentation: Instrumentation) :
+ StandardAppHelper(
+ instrumentation,
+ ActivityOptions.Pip.LABEL,
+ ActivityOptions.Pip.COMPONENT.toFlickerComponent()
+ ) {
+ private val mediaSessionManager: MediaSessionManager
+ get() =
+ context.getSystemService(MediaSessionManager::class.java)
+ ?: error("Could not get MediaSessionManager")
+
+ private val mediaController: MediaController?
+ get() =
+ mediaSessionManager.getActiveSessions(null).firstOrNull { it.packageName == `package` }
+
+ private val gestureHelper: GestureHelper = GestureHelper(mInstrumentation)
+
+ open fun clickObject(resId: String) {
+ val selector = By.res(`package`, resId)
+ val obj = uiDevice.findObject(selector) ?: error("Could not find `$resId` object")
+
+ obj.click()
+ }
+
+ /** Drags the PIP window to the provided final coordinates without releasing the pointer. */
+ fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
+ val initWindowRect = getWindowRect(wmHelper).clone()
+
+ // initial pointer at the center of the window
+ val initialCoord =
+ GestureHelper.Tuple(
+ initWindowRect.centerX().toFloat(),
+ initWindowRect.centerY().toFloat()
+ )
+
+ // the offset to the right (or left) of the window center to drag the window to
+ val offset = 50
+
+ // the actual final x coordinate with the offset included;
+ // if the pip window is closer to the right edge of the display the offset is negative
+ // otherwise the offset is positive
+ val endX =
+ initWindowRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
+ val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
+
+ // drag to the final coordinate
+ gestureHelper.dragWithoutRelease(initialCoord, finalCoord, steps)
+ }
+
+ /**
+ * Releases the primary pointer.
+ *
+ * Injects the release of the primary pointer if the primary pointer info was cached after
+ * another gesture was injected without pointer release.
+ */
+ fun releasePipAfterDragging() {
+ gestureHelper.releasePrimaryPointer()
+ }
+
+ /**
+ * Drags the PIP window away from the screen edge while not crossing the display center.
+ *
+ * @throws IllegalStateException if default display bounds are not available
+ */
+ fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int) {
+ val initWindowRect = getWindowRect(wmHelper).clone()
+
+ // initial pointer at the center of the window
+ val startX = initWindowRect.centerX()
+ val y = initWindowRect.centerY()
+
+ val displayRect =
+ wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ ?: throw IllegalStateException("Default display is null")
+
+ // the offset to the right (or left) of the display center to drag the window to
+ val offset = 20
+
+ // the actual final x coordinate with the offset included;
+ // if the pip window is closer to the right edge of the display the offset is positive
+ // otherwise the offset is negative
+ val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)
+
+ // drag the window to the left but not beyond the center of the display
+ uiDevice.drag(startX, y, endX, y, steps)
+ }
+
+ /**
+ * Returns true if PIP window is closer to the right edge of the display than left.
+ *
+ * @throws IllegalStateException if default display bounds are not available
+ */
+ fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
+ val windowRect = getWindowRect(wmHelper)
+
+ val displayRect =
+ wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ ?: throw IllegalStateException("Default display is null")
+
+ return windowRect.centerX() > displayRect.centerX()
+ }
+
+ /**
+ * Expands the PIP window by using the pinch out gesture.
+ *
+ * @param percent The percentage by which to increase the pip window size.
+ * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
+ */
+ fun pinchOpenPipWindow(wmHelper: WindowManagerStateHelper, percent: Float, steps: Int) {
+ // the percentage must be between 0.0f and 1.0f
+ if (percent <= 0.0f || percent > 1.0f) {
+ throw IllegalArgumentException("Percent must be between 0.0f and 1.0f")
+ }
+
+ val windowRect = getWindowRect(wmHelper)
+
+ // first pointer's initial x coordinate is halfway between the left edge and the center
+ val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat()
+ // second pointer's initial x coordinate is halfway between the right edge and the center
+ val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat()
+
+ // horizontal distance the window should increase by
+ val distIncrease = windowRect.width * percent
+
+ // final x-coordinates
+ val finalLeftX = initLeftX - (distIncrease / 2)
+ val finalRightX = initRightX + (distIncrease / 2)
+
+ // y-coordinate is the same throughout this animation
+ val yCoord = windowRect.centerY().toFloat()
+
+ var adjustedSteps = MIN_STEPS_TO_ANIMATE
+
+ // if distance per step is at least 1, then we can use the number of steps requested
+ if (distIncrease.toInt() / (steps * 2) >= 1) {
+ adjustedSteps = steps
+ }
+
+ // if the distance per step is less than 1, carry out the animation in two steps
+ gestureHelper.pinch(
+ GestureHelper.Tuple(initLeftX, yCoord),
+ GestureHelper.Tuple(initRightX, yCoord),
+ GestureHelper.Tuple(finalLeftX, yCoord),
+ GestureHelper.Tuple(finalRightX, yCoord),
+ adjustedSteps
+ )
+
+ waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect))
+ }
+
+ /**
+ * Minimizes the PIP window by using the pinch in gesture.
+ *
+ * @param percent The percentage by which to decrease the pip window size.
+ * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
+ */
+ fun pinchInPipWindow(wmHelper: WindowManagerStateHelper, percent: Float, steps: Int) {
+ // the percentage must be between 0.0f and 1.0f
+ if (percent <= 0.0f || percent > 1.0f) {
+ throw IllegalArgumentException("Percent must be between 0.0f and 1.0f")
+ }
+
+ val windowRect = getWindowRect(wmHelper)
+
+ // first pointer's initial x coordinate is halfway between the left edge and the center
+ val initLeftX = (windowRect.centerX() - windowRect.width / 4).toFloat()
+ // second pointer's initial x coordinate is halfway between the right edge and the center
+ val initRightX = (windowRect.centerX() + windowRect.width / 4).toFloat()
+
+ // decrease by the distance specified through the percentage
+ val distDecrease = windowRect.width * percent
+
+ // get the final x-coordinates and make sure they are not passing the center of the window
+ val finalLeftX = Math.min(initLeftX + (distDecrease / 2), windowRect.centerX().toFloat())
+ val finalRightX = Math.max(initRightX - (distDecrease / 2), windowRect.centerX().toFloat())
+
+ // y-coordinate is the same throughout this animation
+ val yCoord = windowRect.centerY().toFloat()
+
+ var adjustedSteps = MIN_STEPS_TO_ANIMATE
+
+ // if distance per step is at least 1, then we can use the number of steps requested
+ if (distDecrease.toInt() / (steps * 2) >= 1) {
+ adjustedSteps = steps
+ }
+
+ // if the distance per step is less than 1, carry out the animation in two steps
+ gestureHelper.pinch(
+ GestureHelper.Tuple(initLeftX, yCoord),
+ GestureHelper.Tuple(initRightX, yCoord),
+ GestureHelper.Tuple(finalLeftX, yCoord),
+ GestureHelper.Tuple(finalRightX, yCoord),
+ adjustedSteps
+ )
+
+ waitForPipWindowToMinimizeFrom(wmHelper, Region.from(windowRect))
+ }
+
+ /**
+ * Launches the app through an intent instead of interacting with the launcher and waits until
+ * the app window is in PIP mode
+ */
+ @JvmOverloads
+ fun launchViaIntentAndWaitForPip(
+ wmHelper: WindowManagerStateHelper,
+ launchedAppComponentMatcherOverride: IComponentMatcher? = null,
+ action: String? = null,
+ stringExtras: Map<String, String>
+ ) {
+ launchViaIntentAndWaitShown(
+ wmHelper,
+ launchedAppComponentMatcherOverride,
+ action,
+ stringExtras,
+ waitConditions = arrayOf(ConditionsFactory.hasPipWindow())
+ )
+
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(this)
+ .withPipShown()
+ .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 clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
+ clickObject(ENTER_PIP_BUTTON_ID)
+
+ // Wait on WMHelper or simply wait for 3 seconds
+ wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify()
+ // when entering pip, the dismiss button is visible at the start. to ensure the pip
+ // animation is complete, wait until the pip dismiss button is no longer visible.
+ // b/176822698: dismiss-only state will be removed in the future
+ uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT)
+ }
+
+ fun enableEnterPipOnUserLeaveHint() {
+ clickObject(ENTER_PIP_ON_USER_LEAVE_HINT)
+ }
+
+ fun enableAutoEnterForPipActivity() {
+ clickObject(ENTER_PIP_AUTOENTER)
+ }
+
+ fun clickStartMediaSessionButton() {
+ clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID)
+ }
+
+ fun checkWithCustomActionsCheckbox() =
+ uiDevice
+ .findObject(By.res(`package`, WITH_CUSTOM_ACTIONS_BUTTON_ID))
+ ?.takeIf { it.isCheckable }
+ ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) }
+ ?: error("'With custom actions' checkbox not found")
+
+ fun pauseMedia() =
+ mediaController?.transportControls?.pause() ?: error("No active media session found")
+
+ fun stopMedia() =
+ mediaController?.transportControls?.stop() ?: error("No active media session found")
+
+ @Deprecated(
+ "Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)")
+ )
+ open fun closePipWindow() {
+ closePipWindow(WindowManagerStateHelper(mInstrumentation))
+ }
+
+ /** Returns the pip window bounds. */
+ fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
+ val windowRegion = wmHelper.getWindowRegion(this)
+ require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" }
+ return windowRegion.bounds
+ }
+
+ /** Taps the pip window and dismisses it by clicking on the X button. */
+ open fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ val dismissPipObject =
+ uiDevice.findObject(dismissSelector) ?: error("PIP window dismiss button not found")
+ val dismissButtonBounds = dismissPipObject.visibleBounds
+ uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
+
+ // Wait for animation to complete.
+ wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ /** Close the pip window by pressing the expand button */
+ fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // search and interact with the expand button
+ val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
+ uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
+ val expandPipObject =
+ uiDevice.findObject(expandSelector) ?: error("PIP window expand button not found")
+ val expandButtonBounds = expandPipObject.visibleBounds
+ uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
+ wmHelper.StateSyncBuilder().withPipGone().withFullScreenApp(this).waitForAndVerify()
+ }
+
+ /** Double click on the PIP window to expand it */
+ fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
+ Log.d(TAG, "First click")
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ Log.d(TAG, "Second click")
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ Log.d(TAG, "Wait for app transition to end")
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect))
+ }
+
+ private fun waitForPipWindowToExpandFrom(
+ wmHelper: WindowManagerStateHelper,
+ windowRect: Region
+ ) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipWindowExpanded") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegion = pipAppWindow.frameRegion
+ return@add pipRegion.coversMoreThan(windowRect)
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForPipWindowToMinimizeFrom(
+ wmHelper: WindowManagerStateHelper,
+ windowRect: Region
+ ) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipWindowMinimized") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegion = pipAppWindow.frameRegion
+ return@add windowRect.coversMoreThan(pipRegion)
+ }
+ .waitForAndVerify()
+ }
+
+ /**
+ * Waits until the PIP window snaps horizontally to the provided bounds.
+ *
+ * @param finalBounds the bounds to wait for PIP window to snap to
+ */
+ fun waitForPipToSnapTo(wmHelper: WindowManagerStateHelper, finalBounds: android.graphics.Rect) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("pipWindowSnapped") {
+ val pipAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val pipRegionBounds = pipAppWindow.frameRegion.bounds
+ return@add pipRegionBounds.left == finalBounds.left &&
+ pipRegionBounds.right == finalBounds.right
+ }
+ .add(ConditionsFactory.isWMStateComplete())
+ .waitForAndVerify()
+ }
+
+ companion object {
+ private const val TAG = "PipAppHelper"
+ private const val ENTER_PIP_BUTTON_ID = "enter_pip"
+ private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions"
+ private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
+ private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
+ private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
+ // minimum number of steps to take, when animating gestures, needs to be 2
+ // so that there is at least a single intermediate layer that flicker tests can check
+ private const val MIN_STEPS_TO_ANIMATE = 2
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 7bab981ce231..cac3530399de 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -17,18 +17,16 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-class SeamlessRotationAppHelper @JvmOverloads constructor(
+class SeamlessRotationAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+ launcherName: String = ActivityOptions.SeamlessRotation.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.SeamlessRotation.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index bd2e5756b4a9..8366a7a1fe41 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -17,18 +17,16 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-class ShowWhenLockedAppHelper @JvmOverloads constructor(
+class ShowWhenLockedAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+ launcherName: String = ActivityOptions.ShowWhenLockedActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.ShowWhenLockedActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index f6a8817e5b08..89c6c35af47d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -17,18 +17,15 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
+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
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.parser.toFlickerComponent
-class SimpleAppHelper @JvmOverloads constructor(
+class SimpleAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) \ No newline at end of file
+ launcherName: String = ActivityOptions.SimpleActivity.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index a135e0af067b..895725c1efef 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -17,50 +17,43 @@
package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
-import android.support.test.launcherhelper.ILauncherStrategy
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import android.view.Display
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-class TwoActivitiesAppHelper @JvmOverloads constructor(
+class TwoActivitiesAppHelper
+@JvmOverloads
+constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
- component: FlickerComponentName =
- ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
-) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+ launcherName: String = ActivityOptions.LaunchNewActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.LaunchNewActivity.COMPONENT.toFlickerComponent()
+) : StandardAppHelper(instr, launcherName, component) {
private val secondActivityComponent =
- ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+ ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
- require(button != null) {
+ requireNotNull(button) {
"Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
- wmHelper.waitFor(
- WindowManagerStateHelper.isAppFullScreen(secondActivityComponent),
- WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
- WindowManagerConditionsFactory.hasLayersAnimating().negate()
- )
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondActivityComponent).waitForAndVerify()
}
companion object {
private const val LAUNCH_SECOND_ACTIVITY = "launch_second_activity"
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
deleted file mode 100644
index dd5f33fb8669..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window closing back to app window transitions.
- *
- * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
- *
- * Don't show if this is not explicitly requested by the user and the input method
- * is fullscreen. That would be too disruptive.
- *
- * More details on b/190352379
- *
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.closeIME(device, wmHelper)
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- @Presubmit
- @Test
- fun imeAppWindowIsAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowOnTop(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun imeLayerVisibleStart() {
- testSpec.assertLayersStart {
- this.isVisible(FlickerComponentName.IME)
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerInvisibleEnd() {
- testSpec.assertLayersEnd {
- this.isInvisible(FlickerComponentName.IME)
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
-
- @Presubmit
- @Test
- fun imeAppLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3,
- // b/190352379 (IME doesn't show on app launch in 90 degrees)
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
- )
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
deleted file mode 100644
index 5606965a6d40..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window closing back to app window transitions.
- *
- * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
- *
- * Don't show if this is not explicitly requested by the user and the input method
- * is fullscreen. That would be too disruptive.
- *
- * More details on b/190352379
- *
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeGone()
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- @Presubmit
- @Test
- fun imeAppWindowBecomesInvisible() {
- testSpec.assertWm {
- this.isAppWindowOnTop(testApp.component)
- .then()
- .isAppWindowNotOnTop(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun imeLayerVisibleStart() {
- testSpec.assertLayersStart {
- this.isVisible(FlickerComponentName.IME)
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerInvisibleEnd() {
- testSpec.assertLayersEnd {
- this.isInvisible(FlickerComponentName.IME)
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
-
- @Presubmit
- @Test
- fun imeAppLayerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- .then()
- .isInvisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- FlickerComponentName.IME,
- FlickerComponentName.SPLASH_SCREEN))
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3,
- // b/190352379 (IME doesn't show on app launch in 90 degrees)
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)
- )
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
deleted file mode 100644
index 6257484be9bd..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-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)
-@Group4
-class CloseImeEditorPopupDialogTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation, testSpec.startRotation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- imeTestApp.launchViaIntent(wmHelper)
- imeTestApp.openIME(device, wmHelper)
- }
- }
- transitions {
- imeTestApp.dismissDialog(wmHelper)
- instrumentation.uiAutomation.syncInputTransactions()
- }
- teardown {
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- imeTestApp.exit()
- }
- }
- }
- }
-
- @Postsubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Postsubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Postsubmit
- @Test
- fun imeWindowBecameInvisible() = testSpec.imeWindowBecomesInvisible()
-
- @Postsubmit
- @Test
- fun imeLayerAndImeSnapshotVisibleOnScreen() {
- testSpec.assertLayers {
- this.isVisible(FlickerComponentName.IME)
- .then()
- .isVisible(FlickerComponentName.IME_SNAPSHOT)
- .then()
- .isInvisible(FlickerComponentName.IME_SNAPSHOT)
- .isInvisible(FlickerComponentName.IME)
- }
- }
-
- @Postsubmit
- @Test
- fun imeSnapshotAssociatedOnAppVisibleRegion() {
- testSpec.assertLayers {
- this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
- val imeSnapshotLayers = it.subjects.filter {
- subject -> subject.name.contains(
- FlickerComponentName.IME_SNAPSHOT.toLayerName()) && subject.isVisible
- }
- if (imeSnapshotLayers.isNotEmpty()) {
- val visibleAreas = imeSnapshotLayers.mapNotNull { imeSnapshotLayer ->
- imeSnapshotLayer.layer?.visibleRegion }.toTypedArray()
- val imeVisibleRegion = RegionSubject.assertThat(visibleAreas, this, timestamp)
- val appVisibleRegion = it.visibleRegion(imeTestApp.component)
- if (imeVisibleRegion.region.isNotEmpty) {
- imeVisibleRegion.coversAtMost(appVisibleRegion.region)
- }
- }
- }
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 2,
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- ),
- supportedRotations = listOf(Surface.ROTATION_0)
- )
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
new file mode 100644
index 000000000000..7e0632d216ea
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+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: FlickerTest) : BaseTest(flicker) {
+ private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ imeTestApp.launchViaIntent(wmHelper)
+ imeTestApp.openIME(wmHelper)
+ }
+ transitions {
+ imeTestApp.dismissDialog(wmHelper)
+ wmHelper.StateSyncBuilder().withImeGone().waitForAndVerify()
+ }
+ teardown {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ imeTestApp.exit(wmHelper)
+ }
+ }
+
+ @Presubmit @Test fun imeWindowBecameInvisible() = flicker.imeWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeLayerAndImeSnapshotVisibleOnScreen() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.IME)
+ .then()
+ .isVisible(ComponentNameMatcher.IME_SNAPSHOT, isOptional = true)
+ .then()
+ .isInvisible(ComponentNameMatcher.IME_SNAPSHOT, isOptional = true)
+ .isInvisible(ComponentNameMatcher.IME)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeSnapshotAssociatedOnAppVisibleRegion() {
+ flicker.assertLayers {
+ this.invoke("imeSnapshotAssociatedOnAppVisibleRegion") {
+ val imeSnapshotLayers =
+ it.subjects.filter { subject ->
+ subject.name.contains(ComponentNameMatcher.IME_SNAPSHOT.toLayerName()) &&
+ subject.isVisible
+ }
+ if (imeSnapshotLayers.isNotEmpty()) {
+ val visibleAreas =
+ imeSnapshotLayers
+ .mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion }
+ .toTypedArray()
+ val imeVisibleRegion = RegionSubject(visibleAreas, timestamp)
+ val appVisibleRegion = it.visibleRegion(imeTestApp)
+ if (imeVisibleRegion.region.isNotEmpty) {
+ imeVisibleRegion.coversAtMost(appVisibleRegion.region)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
index edd52b76cd5c..c355e2708657 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.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,34 +16,27 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume
-import org.junit.Before
-
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/**
- * Test IME windows switching with 2-Buttons or gestural navigation.
- * To run this test: `atest FlickerTests:SwitchImeWindowsFromGestureNavTest`
- */
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-@FlakyTest(bugId = 228012334)
-class SwitchImeWindowsFromGestureNavTest_ShellTransit(testSpec: FlickerTestParameter)
- : SwitchImeWindowsFromGestureNavTest(testSpec) {
- @Before
- override fun before() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+class CloseImeOnDismissPopupDialogTestCfArm(flicker: FlickerTest) :
+ CloseImeOnDismissPopupDialogTest(flicker) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
new file mode 100644
index 000000000000..537238b1d626
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -0,0 +1,124 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+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: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ }
+ transitions {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().withImeGone().waitForAndVerify()
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(
+ ComponentNameMatcher.IME,
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT
+ )
+ )
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(ComponentNameMatcher.IME, ComponentNameMatcher.SPLASH_SCREEN)
+ )
+ }
+ }
+
+ @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
+
+ @Presubmit @Test fun imeWindowBecomesInvisible() = flicker.imeWindowBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppWindowBecomesInvisible() {
+ flicker.assertWm { this.isAppWindowVisible(testApp).then().isAppWindowInvisible(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeAppLayerBecomesInvisible() {
+ flicker.assertLayers { this.isVisible(testApp).then().isInvisible(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ @PlatinumTest(focusArea = "ime")
+ override fun cujCompleted() {
+ super.cujCompleted()
+ imeLayerBecomesInvisible()
+ imeAppWindowBecomesInvisible()
+ imeWindowBecomesInvisible()
+ imeLayerBecomesInvisible()
+ runAndIgnoreAssumptionViolation { navBarLayerPositionAtStartAndEnd() }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
new file mode 100644
index 000000000000..0fe52df5bb25
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.FlickerTest
+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 CloseImeOnGoHomeTestCfArm(flicker: FlickerTest) : CloseImeOnGoHomeTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
new file mode 100644
index 000000000000..cbe03dcd932a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ * ```
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ * ```
+ *
+ * More details on b/190352379
+ *
+ * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ }
+ teardown { testApp.exit(wmHelper) }
+ transitions {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().withImeGone().waitForAndVerify()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeAppWindowBecomesInvisible() {
+ flicker.assertWm { this.isAppWindowOnTop(testApp).then().isAppWindowNotOnTop(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleStart() {
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppLayerBecomesInvisible() {
+ flicker.assertLayers { this.isVisible(testApp).then().isInvisible(testApp) }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ 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
new file mode 100644
index 000000000000..5aacb3011e79
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ CloseImeShownOnAppStartOnGoHomeTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
new file mode 100644
index 000000000000..82c390b77d59
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions.
+ *
+ * This test doesn't work on 90 degrees. According to the InputMethodService documentation:
+ * ```
+ * Don't show if this is not explicitly requested by the user and the input method
+ * is fullscreen. That would be too disruptive.
+ * ```
+ *
+ * More details on b/190352379
+ *
+ * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { testApp.launchViaIntent(wmHelper) }
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.closeIME(wmHelper) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeAppWindowIsAlwaysVisible() {
+ flicker.assertWm { this.isAppWindowOnTop(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleStart() {
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerInvisibleEnd() {
+ flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppLayerIsAlwaysVisible() {
+ flicker.assertLayers { this.isVisible(testApp) }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // b/190352379 (IME doesn't show on app launch in 90 degrees)
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..eb81aed35011
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ CloseImeShownOnAppStartToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
new file mode 100644
index 000000000000..8d8075927076
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.PlatinumTest
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window closing back to app window transitions. To run this test: `atest
+ * FlickerTests:CloseImeWindowToAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ }
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.closeIME(wmHelper) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ flicker.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(
+ ComponentNameMatcher.IME,
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT
+ )
+ )
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ flicker.navBarLayerPositionAtStartAndEnd()
+ }
+
+ @Presubmit
+ @Test
+ fun navBarLayerPositionAtStartAndEndLandscapeOrSeascapeAtStart() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ flicker.navBarLayerPositionAtStartAndEnd()
+ }
+
+ @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun imeAppLayerIsAlwaysVisible() {
+ flicker.assertLayers { this.isVisible(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeAppWindowIsAlwaysVisible() {
+ flicker.assertWm { this.isAppWindowOnTop(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ @PlatinumTest(focusArea = "ime")
+ override fun cujCompleted() {
+ super.cujCompleted()
+ imeLayerBecomesInvisible()
+ imeAppLayerIsAlwaysVisible()
+ imeAppWindowIsAlwaysVisible()
+ runAndIgnoreAssumptionViolation { navBarLayerPositionAtStartAndEnd() }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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
new file mode 100644
index 000000000000..db1440b0c5b8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ CloseImeToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
new file mode 100644
index 000000000000..15262959332e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.ime
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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.helpers.SimpleAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
+ * there is no flickering when back to the simple activity without requesting IME to show.
+ *
+ * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val testApp = ImeAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ simpleApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ }
+ transitions { testApp.finishActivity(wmHelper) }
+ teardown { simpleApp.exit(wmHelper) }
+ }
+
+ @Presubmit @Test fun imeWindowBecomesInvisible() = flicker.imeWindowBecomesInvisible()
+
+ @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
+
+ @FlakyTest(bugId = 246284124)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @Presubmit
+ @Test
+ @PlatinumTest(focusArea = "ime")
+ override fun cujCompleted() {
+ runAndIgnoreAssumptionViolation { entireScreenCovered() }
+ runAndIgnoreAssumptionViolation { statusBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { statusBarLayerPositionAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { statusBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { visibleWindowsShownMoreThanOneConsecutiveEntry() }
+ runAndIgnoreAssumptionViolation { taskBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsVisibleAtStartAndEnd() }
+ imeLayerBecomesInvisible()
+ imeWindowBecomesInvisible()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..405ab6bcd9d6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ CloseImeToHomeOnFinishActivityTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
deleted file mode 100644
index e7a1c50821b7..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window closing back to app window transitions.
- * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- testApp.openIME(device, wmHelper)
- }
- }
- teardown {
- test {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.closeIME(device, wmHelper)
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- FlickerComponentName.IME,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT))
- }
- }
-
- @Presubmit
- @Test
- fun imeAppWindowIsAlwaysVisible() {
- testSpec.assertWm {
- this.isAppWindowOnTop(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() {
- assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
- testSpec.navBarLayerRotatesAndScales()
- }
-
- @FlakyTest
- @Test
- fun navBarLayerRotatesAndScales_Flaky() {
- assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
- testSpec.navBarLayerRotatesAndScales()
- }
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
-
- @Presubmit
- @Test
- fun imeAppLayerIsAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
deleted file mode 100644
index b454f0155b3e..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-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)
-@Group2
-class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- }
- transitions {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- wmHelper.waitImeGone()
- }
- teardown {
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- }
- test {
- testApp.exit()
- }
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(
- FlickerComponentName.IME,
- FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT))
- }
- }
-
- @Presubmit
- @Test
- fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
-
- @Presubmit
- @Test
- fun imeAppWindowBecomesInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(testApp.component)
- .then()
- .isAppWindowInvisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
-
- @Presubmit
- @Test
- fun imeAppLayerBecomesInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- .then()
- .isInvisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(
- FlickerComponentName.IME,
- FlickerComponentName.SPLASH_SCREEN))
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index ba78e25580ec..8e3371986056 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -15,55 +15,50 @@
*/
@file:JvmName("CommonAssertions")
+
package com.android.server.wm.flicker.ime
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.traces.common.FlickerComponentName
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
-fun FlickerTestParameter.imeLayerBecomesVisible() {
+fun FlickerTest.imeLayerBecomesVisible() {
assertLayers {
- this.isInvisible(FlickerComponentName.IME)
- .then()
- .isVisible(FlickerComponentName.IME)
+ this.isInvisible(ComponentNameMatcher.IME).then().isVisible(ComponentNameMatcher.IME)
}
}
-fun FlickerTestParameter.imeLayerBecomesInvisible() {
+fun FlickerTest.imeLayerBecomesInvisible() {
assertLayers {
- this.isVisible(FlickerComponentName.IME)
- .then()
- .isInvisible(FlickerComponentName.IME)
+ this.isVisible(ComponentNameMatcher.IME).then().isInvisible(ComponentNameMatcher.IME)
}
}
-fun FlickerTestParameter.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
+fun FlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
- this.isNonAppWindowVisible(FlickerComponentName.IME)
+ this.isNonAppWindowVisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowInvisible(FlickerComponentName.IME)
+ .isNonAppWindowInvisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowVisible(FlickerComponentName.IME)
+ .isNonAppWindowVisible(ComponentNameMatcher.IME)
}
} else {
- assertWm {
- this.isNonAppWindowVisible(FlickerComponentName.IME)
- }
+ assertWm { this.isNonAppWindowVisible(ComponentNameMatcher.IME) }
}
}
-fun FlickerTestParameter.imeWindowBecomesVisible() {
+fun FlickerTest.imeWindowBecomesVisible() {
assertWm {
- this.isNonAppWindowInvisible(FlickerComponentName.IME)
+ this.isNonAppWindowInvisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowVisible(FlickerComponentName.IME)
+ .isNonAppWindowVisible(ComponentNameMatcher.IME)
}
}
-fun FlickerTestParameter.imeWindowBecomesInvisible() {
+fun FlickerTest.imeWindowBecomesInvisible() {
assertWm {
- this.isNonAppWindowVisible(FlickerComponentName.IME)
+ this.isNonAppWindowVisible(ComponentNameMatcher.IME)
.then()
- .isNonAppWindowInvisible(FlickerComponentName.IME)
+ .isNonAppWindowInvisible(ComponentNameMatcher.IME)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
deleted file mode 100644
index 2f8f9441a7b9..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * 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.ime
-
-import android.view.WindowInsets.Type.ime
-import android.view.WindowInsets.Type.navigationBars
-import android.view.WindowInsets.Type.statusBars
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-
-/**
- * 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)
-class LaunchAppShowImeAndDialogThemeAppTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitImeShown()
- testApp.startDialogThemedActivity(wmHelper)
- // Verify IME insets isn't visible on dialog since it's non-IME focusable window
- assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
- assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
- assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars()))
- }
- }
- teardown {
- eachRun {
- testApp.exit()
- }
- }
- transitions {
- testApp.dismissDialog(wmHelper)
- }
- }
- }
-
- /**
- * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
- */
- @FlakyTest(bugId = 215884488)
- @Test
- fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
-
- /**
- * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
- */
- @FlakyTest(bugId = 227142436)
- @Test
- fun imeLayerExistsEnd() {
- testSpec.assertLayersEnd {
- this.isVisible(FlickerComponentName.IME)
- }
- }
-
- /**
- * Checks that [FlickerComponentName.IME_SNAPSHOT] layer is invisible always.
- */
- @Presubmit
- @Test
- fun imeSnapshotNotVisible() {
- testSpec.assertLayers {
- this.isInvisible(FlickerComponentName.IME_SNAPSHOT)
- }
- }
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
deleted file mode 100644
index b897ca2a9c15..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Launch an app that automatically displays the IME
- *
- * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
- *
- * Actions:
- * Make sure no apps are running on the device
- * Launch an app [testApp] that automatically displays IME and wait animation to complete
- *
- * To run only the presubmit assertions add: `--
- * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
- * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
- *
- * To run only the postsubmit assertions add: `--
- * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
- * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
- *
- * To run only the flaky assertions add: `--
- * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
- *
- * Notes:
- * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
- * are inherited [CloseAppTransition]
- * 2. Part of the test setup occurs automatically via
- * [com.android.server.wm.flicker.TransitionRunnerWithRules],
- * including configuring navigation mode, initial orientation and ensuring no
- * apps are running before setup
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
- private val initializeApp = ImeStateInitializeHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- initializeApp.launchViaIntent()
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- initializeApp.exit()
- testApp.exit()
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitImeShown()
- }
- }
- }
-
- /**
- * Checks that [FlickerComponentName.IME] window becomes visible during the transition
- */
- @Presubmit
- @Test
- fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
-
- /**
- * Checks that [FlickerComponentName.IME] layer becomes visible during the transition
- */
- @Presubmit
- @Test
- fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
-
- /**
- * Checks that [FlickerComponentName.IME] layer is invisible at the start of the transition
- */
- @Presubmit
- @Test
- fun imeLayerNotExistsStart() {
- testSpec.assertLayersStart {
- this.isInvisible(FlickerComponentName.IME)
- }
- }
-
- /**
- * Checks that [FlickerComponentName.IME] layer is visible at the end of the transition
- */
- @Presubmit
- @Test
- fun imeLayerExistsEnd() {
- testSpec.assertLayersEnd {
- this.isVisible(FlickerComponentName.IME)
- }
- }
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
deleted file mode 100644
index 972918e28fa7..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowAndCloseTest.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.*
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
- * there is no flickering when back to the simple activity without requesting IME to show.
- *
- * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class OpenImeWindowAndCloseTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val simpleApp = SimpleAppHelper(instrumentation)
- private val testApp = ImeAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- simpleApp.launchViaIntent(wmHelper)
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- }
- transitions {
- testApp.finishActivity(device, wmHelper)
- }
- teardown {
- test {
- simpleApp.exit()
- }
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
deleted file mode 100644
index 78aea1f1fb17..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window layer will become visible when switching from the fixed orientation activity.
- * To run this test: `atest FlickerTests:OpenImeWindowFromFixedOrientationAppTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val fixedOrientationApp = FixedOrientationAppHelper(instrumentation)
- private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- fixedOrientationApp.launchViaIntent(wmHelper)
- this.setRotation(Surface.ROTATION_90)
- }
- }
- transitions {
- imeTestApp.launchViaIntent(wmHelper)
- }
- teardown {
- test {
- fixedOrientationApp.exit(wmHelper)
- }
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
deleted file mode 100644
index 8fcb4b7c03f1..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window opening transitions.
- * To run this test: `atest FlickerTests:OpenImeWindowTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppHelper(instrumentation)
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- testApp.openIME(device, wmHelper)
- }
- teardown {
- eachRun {
- testApp.closeIME(device, wmHelper)
- }
- test {
- testApp.exit()
- }
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
-
- @Presubmit
- @Test
- fun appWindowAlwaysVisibleOnTop() {
- testSpec.assertWm {
- this.isAppWindowOnTop(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
-
- @Presubmit
- @Test
- fun layerAlwaysVisible() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
new file mode 100644
index 000000000000..19bbf0cb92b0
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.common.NavBar
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest
+ * FlickerTests:OpenImeWindowToFixedPortraitAppTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ // Enable letterbox when the app calls setRequestedOrientation
+ device.executeShellCommand("cmd window set-ignore-orientation-request true")
+ }
+ transitions { testApp.toggleFixPortraitOrientation(wmHelper) }
+ teardown {
+ testApp.exit()
+ device.executeShellCommand("cmd window set-ignore-orientation-request false")
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleStart() {
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerVisibleRegionKeepsTheSame() {
+ var imeLayerVisibleRegionBeforeTransition: RegionSubject? = null
+ flicker.assertLayersStart {
+ imeLayerVisibleRegionBeforeTransition = this.visibleRegion(ComponentNameMatcher.IME)
+ }
+ flicker.assertLayersEnd {
+ this.visibleRegion(ComponentNameMatcher.IME)
+ .coversExactly(imeLayerVisibleRegionBeforeTransition!!.region)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun appWindowWithLetterboxCoversExactlyOnScreen() {
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
+ flicker.assertLayersEnd {
+ this.visibleRegion(testApp.or(ComponentNameMatcher.LETTERBOX))
+ .coversExactly(displayBounds)
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations =
+ listOf(
+ Rotation.ROTATION_90,
+ ),
+ supportedNavigationModes = listOf(NavBar.MODE_3BUTTON, NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
deleted file mode 100644
index 0454ca0a607e..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.FlakyTest
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import org.junit.Assume.assumeTrue
-import org.junit.Assume.assumeFalse
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window layer will be associated with the app task when going to the overview screen.
- * To run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class OpenImeWindowToOverViewTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
- private val statusBarInvisible = WindowManagerConditionsFactory.isStatusBarVisible().negate()
- private val navBarInvisible = WindowManagerConditionsFactory.isNavBarVisible().negate()
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- imeTestApp.launchViaIntent(wmHelper)
- }
- }
- transitions {
- device.pressRecentApps()
- waitForRecentsActivityVisible(wmHelper)
- waitNavStatusBarVisibility(wmHelper)
- }
- teardown {
- test {
- device.pressHome()
- imeTestApp.exit(wmHelper)
- }
- }
- }
- }
-
- /**
- * The bars (including status bar and navigation bar) are expected to be hidden while
- * entering overview in landscape if launcher is set to portrait only. Because
- * "showing portrait overview (launcher) in landscape display" is an intermediate state
- * depending on the touch-up to decide the intention of gesture, the display may keep in
- * landscape if return to app, or change to portrait if the gesture is to swipe-to-home.
- *
- * So instead of showing landscape bars with portrait launcher at the same time
- * (especially return-to-home that launcher workspace becomes visible), hide the bars until
- * leave overview to have cleaner appearance.
- *
- * b/227189877
- */
- private fun waitNavStatusBarVisibility(wmHelper: WindowManagerStateHelper) {
- when {
- testSpec.isLandscapeOrSeascapeAtStart && !testSpec.isGesturalNavigation ->
- wmHelper.waitFor(statusBarInvisible)
- testSpec.isLandscapeOrSeascapeAtStart ->
- wmHelper.waitFor(statusBarInvisible, navBarInvisible)
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun imeWindowIsAlwaysVisible() {
- testSpec.imeWindowIsAlwaysVisible()
- }
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible3Button() {
- assumeFalse(testSpec.isGesturalNavigation)
- testSpec.navBarLayerIsVisible()
- }
-
- /**
- * Bars are expected to be hidden while entering overview in landscape (b/227189877)
- */
- @Presubmit
- @Test
- fun navBarLayerIsVisibleInPortraitGestural() {
- assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
- assumeTrue(testSpec.isGesturalNavigation)
- testSpec.navBarLayerIsVisible()
- }
-
- /**
- * In the legacy transitions, the nav bar is not marked as invisible.
- * In the new transitions this is fixed and the nav bar shows as invisible
- */
- @Postsubmit
- @Test
- fun navBarLayerIsInvisibleInLandscapeGestural() {
- assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
- assumeTrue(testSpec.isGesturalNavigation)
- assumeTrue(isShellTransitionsEnabled)
- testSpec.assertLayersStart {
- this.isVisible(FlickerComponentName.NAV_BAR)
- }
- testSpec.assertLayersEnd {
- this.isInvisible(FlickerComponentName.NAV_BAR)
- }
- }
-
- @Postsubmit
- @Test
- fun statusBarLayerIsVisibleInPortrait() {
- assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
- testSpec.statusBarLayerIsVisible()
- }
-
- @Presubmit
- @Test
- fun statusBarLayerIsInvisibleInLandscape() {
- assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
- testSpec.assertLayersStart {
- this.isVisible(FlickerComponentName.STATUS_BAR)
- }
- testSpec.assertLayersEnd {
- this.isInvisible(FlickerComponentName.STATUS_BAR)
- }
- }
-
- @FlakyTest(bugId = 228011606)
- @Test
- fun imeLayerIsVisibleAndAssociatedWithAppWidow() {
- testSpec.assertLayersStart {
- isVisible(FlickerComponentName.IME).visibleRegion(FlickerComponentName.IME)
- .coversAtMost(isVisible(imeTestApp.component)
- .visibleRegion(imeTestApp.component).region)
- }
- testSpec.assertLayers {
- this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
- val imeVisibleRegion = it.visibleRegion(FlickerComponentName.IME)
- val appVisibleRegion = it.visibleRegion(imeTestApp.component)
- if (imeVisibleRegion.region.isNotEmpty) {
- it.isVisible(FlickerComponentName.IME)
- imeVisibleRegion.coversAtMost(appVisibleRegion.region)
- }
- }
- }
- }
-
- private fun waitForRecentsActivityVisible(
- wmHelper: WindowManagerStateHelper
- ) {
- val waitMsg = "state of Recents activity to be visible"
- require(
- wmHelper.waitFor(waitMsg) {
- it.wmState.homeActivity?.let { act ->
- it.wmState.isActivityVisible(act.name)
- } == true ||
- it.wmState.recentsActivity?.let { act ->
- it.wmState.isActivityVisible(act.name)
- } == true
- }
- ) { "Recents activity should be visible" }
- wmHelper.waitForAppTransitionIdle()
- // Ensure WindowManagerService wait until all animations have completed
- instrumentation.uiAutomation.syncInputTransactions()
- }
-
- companion object {
- /**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
- */
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 1,
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
- )
- }
- }
-} \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
deleted file mode 100644
index e7a33543a86b..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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 com.android.server.wm.flicker.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Display
-import android.view.Surface
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.ConditionList
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import org.junit.Assume.assumeFalse
-import org.junit.Assume.assumeTrue
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME window opening transitions.
- * To run this test: `atest FlickerTests:ReOpenImeWindowTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-open class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- private val waitConditionSetup = ConditionList(listOf(
- WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
- WindowManagerConditionsFactory.hasLayersAnimating().negate(),
- WindowManagerConditionsFactory.isHomeActivityVisible()
- ))
-
- @Before
- open fun before() {
- assumeFalse(isShellTransitionsEnabled)
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- eachRun {
- device.pressRecentApps()
- wmHelper.waitFor(waitConditionSetup)
- this.setRotation(testSpec.startRotation)
- }
- }
- transitions {
- device.reopenAppFromOverview(wmHelper)
- wmHelper.waitImeShown()
- }
- teardown {
- test {
- testApp.exit()
- }
- }
- }
- }
-
- @Presubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- @Presubmit
- @Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- val component = FlickerComponentName("", "RecentTaskScreenshotSurface")
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows = listOf(FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT,
- component)
- )
- }
- }
-
- @Presubmit
- @Test
- fun launcherWindowBecomesInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(LAUNCHER_COMPONENT)
- .then()
- .isAppWindowInvisible(LAUNCHER_COMPONENT)
- }
- }
-
- @Presubmit
- @Test
- fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
-
- @Presubmit
- @Test
- fun imeAppWindowVisibilityLegacy() {
- assumeFalse(isShellTransitionsEnabled)
- // the app starts visible in live tile, and stays visible for the duration of entering
- // and exiting overview. However, legacy transitions seem to have a bug which causes
- // everything to restart during the test, so expect the app to disappear and come back.
- // Since we log 1x per frame, sometimes the activity visibility and the app visibility
- // are updated together, sometimes not, thus ignore activity check at the start
- testSpec.assertWm {
- this.isAppWindowVisible(testApp.component)
- .then()
- .isAppWindowInvisible(testApp.component)
- .then()
- .isAppWindowVisible(testApp.component)
- }
- }
-
- @FlakyTest(bugId = 204570898)
- @Test
- fun imeAppWindowVisibility() {
- assumeTrue(isShellTransitionsEnabled)
- // the app starts visible in live tile, and stays visible for the duration of entering
- // and exiting overview. Since we log 1x per frame, sometimes the activity visibility
- // and the app visibility are updated together, sometimes not, thus ignore activity
- // check at the start
- testSpec.assertWm {
- this.isAppWindowVisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- // During testing the launcher is always in portrait mode
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- @Presubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
- @Presubmit
- @Test
- fun imeLayerIsBecomesVisibleLegacy() {
- assumeFalse(isShellTransitionsEnabled)
- testSpec.assertLayers {
- this.isVisible(FlickerComponentName.IME)
- .then()
- .isInvisible(FlickerComponentName.IME)
- .then()
- .isVisible(FlickerComponentName.IME)
- }
- }
-
- @FlakyTest(bugId = 204570898)
- @Test
- fun imeLayerIsBecomesVisible() {
- assumeTrue(isShellTransitionsEnabled)
- testSpec.assertLayers {
- this.isVisible(FlickerComponentName.IME)
- }
- }
-
- @Presubmit
- @Test
- fun appLayerReplacesLauncher() {
- testSpec.assertLayers {
- this.isVisible(LAUNCHER_COMPONENT)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(testApp.component)
- }
- }
-
- @Presubmit
- @Test
- fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- @Presubmit
- @Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- // depends on how much of the animation transactions are sent to SF at once
- // sometimes this layer appears for 2-3 frames, sometimes for only 1
- val recentTaskComponent = FlickerComponentName("", "RecentTaskScreenshotSurface")
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT, recentTaskComponent)
- )
- }
- }
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedRotations = listOf(Surface.ROTATION_0)
- )
- }
- }
-}
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
new file mode 100644
index 000000000000..03f21f95e61e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ ShowImeOnAppStartWhenLaunchingAppTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
new file mode 100644
index 000000000000..496165ab5b09
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Timestamp
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.flicker.subject.exceptions.ExceptionMessageBuilder
+import android.tools.common.flicker.subject.exceptions.InvalidPropertyException
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window layer will become visible when switching from the fixed orientation activity
+ * (e.g. Launcher activity). To run this test: `atest
+ * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: FlickerTest) :
+ BaseTest(flicker) {
+ private val imeTestApp =
+ ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+
+ // Launch the activity with expecting IME will be shown.
+ imeTestApp.launchViaIntent(wmHelper)
+
+ // Swiping out the IME activity to home.
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ // Bring the existing IME activity to the front in landscape mode device rotation.
+ setRotation(Rotation.ROTATION_90)
+ imeTestApp.launchViaIntent(wmHelper)
+ }
+ teardown { imeTestApp.exit(wmHelper) }
+ }
+
+ @Presubmit @Test fun imeWindowBecomesVisible() = flicker.imeWindowBecomesVisible()
+
+ @Presubmit @Test fun imeLayerBecomesVisible() = flicker.imeLayerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun snapshotStartingWindowLayerCoversExactlyOnApp() {
+ flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
+ }
+
+ @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)
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..3aca2a07f7cd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
new file mode 100644
index 000000000000..f64ad53bf2f7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -0,0 +1,149 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTest) :
+ BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(wmHelper)
+ this.setRotation(flicker.scenario.startRotation)
+ device.pressRecentApps()
+ wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify()
+ }
+ transitions {
+ device.reopenAppFromOverview(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).withImeShown().waitForAndVerify()
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ // depends on how much of the animation transactions are sent to SF at once
+ // sometimes this layer appears for 2-3 frames, sometimes for only 1
+ val recentTaskComponent = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ recentTaskComponent
+ )
+ )
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ val component = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
+ flicker.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ component
+ )
+ )
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun launcherWindowBecomesInvisible() {
+ flicker.assertWm {
+ this.isAppWindowVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ @Presubmit @Test fun imeWindowIsAlwaysVisible() = flicker.imeWindowIsAlwaysVisible()
+
+ @Presubmit
+ @Test
+ fun imeAppWindowIsAlwaysVisible() {
+ // the app starts visible in live tile, and stays visible for the duration of entering
+ // and exiting overview. Since we log 1x per frame, sometimes the activity visibility
+ // and the app visibility are updated together, sometimes not, thus ignore activity
+ // check at the start
+ flicker.assertWm { this.isAppWindowVisible(testApp) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerBecomesVisible() {
+ flicker.assertLayers { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun appLayerReplacesLauncher() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp)
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..e1aa4182c331
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) :
+ ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker) {
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
new file mode 100644
index 000000000000..11bc7b9f29c0
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest
+ * FlickerTests:SwitchImeWindowsFromGestureNavTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Presubmit
+open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: FlickerTest) :
+ BaseTest(flicker) {
+ private val testApp = SimpleAppHelper(instrumentation)
+ private val imeTestApp =
+ ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ tapl.setIgnoreTaskbarVisibility(true)
+ this.setRotation(flicker.scenario.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+
+ imeTestApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(imeTestApp).waitForAndVerify()
+
+ imeTestApp.openIME(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ testApp.exit(wmHelper)
+ imeTestApp.exit(wmHelper)
+ }
+ transitions {
+ // [Step1]: Swipe right from testApp task to imeTestApp
+ createTag(TAG_IME_VISIBLE)
+ // Expect taskBar invisible when switching to imeTestApp on the large screen device.
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ createTag(TAG_IME_INVISIBLE)
+ }
+ transitions {
+ // [Step2]: Swipe left to back to testApp task
+ // Expect taskBar visible when switching to testApp on the large screen device.
+ tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
+ wmHelper.StateSyncBuilder().withFullScreenApp(imeTestApp).waitForAndVerify()
+ }
+ }
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @Presubmit
+ @Test
+ fun imeAppWindowVisibility() {
+ flicker.assertWm {
+ isAppWindowVisible(imeTestApp)
+ .then()
+ .isAppSnapshotStartingWindowVisibleFor(testApp, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
+ .then()
+ .isAppSnapshotStartingWindowVisibleFor(imeTestApp, isOptional = true)
+ .then()
+ .isAppWindowVisible(imeTestApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun imeLayerIsVisibleWhenSwitchingToImeApp() {
+ flicker.assertLayersStart { isVisible(ComponentNameMatcher.IME) }
+ flicker.assertLayersTag(TAG_IME_VISIBLE) { isVisible(ComponentNameMatcher.IME) }
+ flicker.assertLayersEnd { isVisible(ComponentNameMatcher.IME) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
+ flicker.assertLayersTag(TAG_IME_INVISIBLE) { isInvisible(ComponentNameMatcher.IME) }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+
+ private const val TAG_IME_VISIBLE = "imeVisible"
+ private const val TAG_IME_INVISIBLE = "imeInVisible"
+ }
+}
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
new file mode 100644
index 000000000000..0f57467c0449
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.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.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+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: FlickerTest) :
+ ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
new file mode 100644
index 000000000000..46967db55086
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -0,0 +1,137 @@
+/*
+ * 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.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Launch an app that automatically displays the IME
+ *
+ * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
+ *
+ * Actions:
+ * ```
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] that automatically displays IME and wait animation to complete
+ * ```
+ *
+ * To run only the presubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
+ *
+ * To run only the postsubmit assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
+ * --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
+ *
+ * To run only the flaky assertions add: `--
+ *
+ * ```
+ * --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [CloseAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+ private val initializeApp = ImeStateInitializeHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ initializeApp.launchViaIntent(wmHelper)
+ this.setRotation(flicker.scenario.startRotation)
+ }
+ teardown {
+ initializeApp.exit(wmHelper)
+ testApp.exit(wmHelper)
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+ }
+ }
+
+ /** Checks that [ComponentNameMatcher.IME] window becomes visible during the transition */
+ @Presubmit @Test fun imeWindowBecomesVisible() = flicker.imeWindowBecomesVisible()
+
+ /** Checks that [ComponentNameMatcher.IME] layer becomes visible during the transition */
+ @Presubmit @Test fun imeLayerBecomesVisible() = flicker.imeLayerBecomesVisible()
+
+ /** Checks that [ComponentNameMatcher.IME] layer is invisible at the start of the transition */
+ @Presubmit
+ @Test
+ fun imeLayerNotExistsStart() {
+ flicker.assertLayersStart { this.isInvisible(ComponentNameMatcher.IME) }
+ }
+
+ /** Checks that [ComponentNameMatcher.IME] layer is visible at the end of the transition */
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
new file mode 100644
index 000000000000..827110dd4572
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -0,0 +1,88 @@
+/*
+ * 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 com.android.server.wm.flicker.ime
+
+import android.platform.test.annotations.PlatinumTest
+import android.platform.test.annotations.Presubmit
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { testApp.launchViaIntent(wmHelper) }
+ transitions { testApp.openIME(wmHelper) }
+ teardown {
+ testApp.closeIME(wmHelper)
+ testApp.exit(wmHelper)
+ }
+ }
+
+ @Presubmit
+ @Test
+ @PlatinumTest(focusArea = "ime")
+ override fun cujCompleted() {
+ super.cujCompleted()
+ imeWindowBecomesVisible()
+ appWindowAlwaysVisibleOnTop()
+ layerAlwaysVisible()
+ }
+
+ @Presubmit @Test fun imeWindowBecomesVisible() = flicker.imeWindowBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun appWindowAlwaysVisibleOnTop() {
+ flicker.assertWm { this.isAppWindowOnTop(testApp) }
+ }
+
+ @Presubmit @Test fun imeLayerBecomesVisible() = flicker.imeLayerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun layerAlwaysVisible() {
+ flicker.assertLayers { this.isVisible(testApp) }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..f5b22949e6b4
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ ShowImeWhenFocusingOnInputFieldTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
new file mode 100644
index 000000000000..277b91558416
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import org.junit.Assert.assertTrue
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+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: FlickerTest) : BaseTest(flicker) {
+ private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withImeShown().waitForAndVerify()
+ testApp.startDialogThemedActivity(wmHelper)
+ // Verify IME insets isn't visible on dialog since it's non-IME focusable window
+ assertFalse(testApp.getInsetsVisibleFromDialog(ime()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(statusBars()))
+ assertTrue(testApp.getInsetsVisibleFromDialog(navigationBars()))
+ }
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.dismissDialog(wmHelper) }
+ }
+
+ /** Checks that [ComponentNameMatcher.IME] layer becomes visible during the transition */
+ @Presubmit @Test fun imeWindowIsAlwaysVisible() = flicker.imeWindowIsAlwaysVisible()
+
+ /** Checks that [ComponentNameMatcher.IME] layer is visible at the end of the transition */
+ @Presubmit
+ @Test
+ fun imeLayerExistsEnd() {
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.IME) }
+ }
+
+ /** Checks that [ComponentNameMatcher.IME_SNAPSHOT] layer is invisible always. */
+ @Presubmit
+ @Test
+ fun imeSnapshotNotVisible() {
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.IME_SNAPSHOT) }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
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
new file mode 100644
index 000000000000..8891d26c9e54
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) :
+ ShowImeWhileDismissingThemedPopupDialogTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
new file mode 100644
index 000000000000..9275d6a2f17f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -0,0 +1,217 @@
+/*
+ * 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.wm.flicker.ime
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.ConditionsFactory
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test 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: FlickerTest) : BaseTest(flicker) {
+ private val imeTestApp =
+ ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { imeTestApp.launchViaIntent(wmHelper) }
+ transitions {
+ device.pressRecentApps()
+ val builder = wmHelper.StateSyncBuilder().withRecentsActivityVisible()
+ waitNavStatusBarVisibility(builder)
+ builder.waitForAndVerify()
+ }
+ teardown {
+ device.pressHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ imeTestApp.exit(wmHelper)
+ }
+ }
+
+ /**
+ * The bars (including [ComponentNameMatcher.STATUS_BAR] and [ComponentNameMatcher.NAV_BAR]) are
+ * expected to be hidden while entering overview in landscape if launcher is set to portrait
+ * only. Because "showing portrait overview (launcher) in landscape display" is an intermediate
+ * state depending on the touch-up to decide the intention of gesture, the display may keep in
+ * landscape if return to app, or change to portrait if the gesture is to swipe-to-home.
+ *
+ * So instead of showing landscape bars with portrait launcher at the same time (especially
+ * return-to-home that launcher workspace becomes visible), hide the bars until leave overview
+ * to have cleaner appearance.
+ *
+ * b/227189877
+ */
+ private fun waitNavStatusBarVisibility(stateSync: WindowManagerStateHelper.StateSyncBuilder) {
+ when {
+ flicker.scenario.isLandscapeOrSeascapeAtStart && !flicker.scenario.isTablet ->
+ stateSync.add(ConditionsFactory.isStatusBarVisible().negate())
+ else -> stateSync.withNavOrTaskBarVisible().withStatusBarVisible()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun imeWindowIsAlwaysVisible() {
+ flicker.imeWindowIsAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ fun navBarLayerIsVisibleAtStartAndEnd3Button() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ flicker.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /**
+ * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
+ * this is fixed and the nav bar shows as invisible
+ */
+ @Presubmit
+ @Test
+ fun navBarLayerIsInvisibleInLandscapeGestural() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
+ flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.NAV_BAR) }
+ }
+
+ /**
+ * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
+ * this is fixed and the nav bar shows as invisible
+ */
+ @Presubmit
+ @Test
+ fun statusBarLayerIsInvisibleInLandscapePhone() {
+ Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
+ flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
+ }
+
+ /**
+ * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
+ * this is fixed and the nav bar shows as invisible
+ */
+ @Presubmit
+ @Test
+ fun statusBarLayerIsInvisibleInLandscapeTablet() {
+ Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Visibility changes depending on orientation and navigation mode")
+ override fun navBarLayerIsVisibleAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Visibility changes depending on orientation and navigation mode")
+ override fun navBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Visibility changes depending on orientation and navigation mode")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Visibility changes depending on orientation and navigation mode")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
+
+ @Presubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsVisibleInPortrait() {
+ Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ flicker.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @Presubmit
+ @Test
+ fun statusBarLayerIsInvisibleInLandscape() {
+ Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
+ flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
+ }
+
+ @Presubmit
+ @Test
+ fun imeLayerIsVisibleAndAssociatedWithAppWidow() {
+ flicker.assertLayersStart {
+ isVisible(ComponentNameMatcher.IME)
+ .visibleRegion(ComponentNameMatcher.IME)
+ .coversAtMost(isVisible(imeTestApp).visibleRegion(imeTestApp).region)
+ }
+ flicker.assertLayers {
+ this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
+ val imeVisibleRegion = it.visibleRegion(ComponentNameMatcher.IME)
+ val appVisibleRegion = it.visibleRegion(imeTestApp)
+ if (imeVisibleRegion.region.isNotEmpty) {
+ it.isVisible(ComponentNameMatcher.IME)
+ imeVisibleRegion.coversAtMost(appVisibleRegion.region)
+ }
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
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
new file mode 100644
index 000000000000..fc3971351db7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.FlickerTest
+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: FlickerTest) :
+ ShowImeWhileEnteringOverviewTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
deleted file mode 100644
index 4b268a871fa0..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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.ime
-
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Display
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import org.junit.Assume
-import org.junit.Before
-
-import org.junit.FixMethodOrder
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test IME windows switching with 2-Buttons or gestural navigation.
- * To run this test: `atest FlickerTests:SwitchImeWindowsFromGestureNavTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-@Presubmit
-open class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = SimpleAppHelper(instrumentation)
- private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
-
- @Before
- open fun before() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- this.setRotation(testSpec.startRotation)
- testApp.launchViaIntent(wmHelper)
- val testAppVisible = wmHelper.waitFor(
- WindowManagerStateHelper.isAppFullScreen(testApp.component),
- WindowManagerConditionsFactory.isAppTransitionIdle(
- Display.DEFAULT_DISPLAY))
- require(testAppVisible) {
- "Expected ${testApp.component.toWindowName()} to be visible"
- }
-
- imeTestApp.launchViaIntent(wmHelper)
- val imeAppVisible = wmHelper.waitFor(
- WindowManagerStateHelper.isAppFullScreen(imeTestApp.component),
- WindowManagerConditionsFactory.isAppTransitionIdle(
- Display.DEFAULT_DISPLAY))
- require(imeAppVisible) {
- "Expected ${imeTestApp.component.toWindowName()} to be visible"
- }
-
- imeTestApp.openIME(device, wmHelper)
- }
- }
- teardown {
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- testApp.exit()
- imeTestApp.exit()
- }
- }
- transitions {
- // [Step1]: Swipe right from imeTestApp to testApp task
- createTag(TAG_IME_VISIBLE)
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(0, displayBounds.bounds.height,
- displayBounds.bounds.width, displayBounds.bounds.height, 50)
-
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
- createTag(TAG_IME_INVISIBLE)
- }
- transitions {
- // [Step2]: Swipe left to back to imeTestApp task
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
- device.swipe(displayBounds.bounds.width, displayBounds.bounds.height,
- 0, displayBounds.bounds.height, 50)
- wmHelper.waitForFullScreenApp(imeTestApp.component)
- }
- }
- }
-
- @Test
- fun imeAppWindowVisibility() {
- testSpec.assertWm {
- isAppWindowVisible(imeTestApp.component)
- .then()
- .isAppSnapshotStartingWindowVisibleFor(testApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(testApp.component)
- .then()
- .isAppSnapshotStartingWindowVisibleFor(imeTestApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(imeTestApp.component)
- }
- }
-
- @Test
- fun navBarLayerIsVisibleAroundSwitching() {
- testSpec.assertLayersStart {
- isVisible(FlickerComponentName.NAV_BAR)
- }
- testSpec.assertLayersEnd {
- isVisible(FlickerComponentName.NAV_BAR)
- }
- }
-
- @Test
- fun statusBarLayerIsVisibleAroundSwitching() {
- testSpec.assertLayersStart {
- isVisible(FlickerComponentName.STATUS_BAR)
- }
- testSpec.assertLayersEnd {
- isVisible(FlickerComponentName.STATUS_BAR)
- }
- }
-
- @Test
- fun imeLayerIsVisibleWhenSwitchingToImeApp() {
- testSpec.assertLayersStart {
- isVisible(FlickerComponentName.IME)
- }
- testSpec.assertLayersTag(TAG_IME_VISIBLE) {
- isVisible(FlickerComponentName.IME)
- }
- testSpec.assertLayersEnd {
- isVisible(FlickerComponentName.IME)
- }
- }
-
- @Test
- fun imeLayerIsInvisibleWhenSwitchingToTestApp() {
- testSpec.assertLayersTag(TAG_IME_INVISIBLE) {
- isInvisible(FlickerComponentName.IME)
- }
- }
-
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- companion object {
- @Parameterized.Parameters(name = "{0}")
- @JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- ),
- supportedRotations = listOf(Surface.ROTATION_0)
- )
- }
-
- private const val TAG_IME_VISIBLE = "imeVisible"
- private const val TAG_IME_INVISIBLE = "imeInVisible"
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index cc808a0ce871..e6594c969373 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -16,24 +16,17 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import android.view.Display
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,69 +39,54 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:ActivitiesTransitionTest`
*
* Actions:
+ * ```
* Launch an app
* Launch a secondary activity within the app
* Close the secondary activity back to the initial one
+ * ```
*
* Notes:
+ * ```
* 1. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+open class ActivitiesTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
- /**
- * Entry point for the test runner. It will use this method to initialize and cache
- * flicker executions
- */
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- }
- teardown {
- test {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.openSecondActivity(device, wmHelper)
- device.pressBack()
- val firstActivityVisible = wmHelper.waitFor(
- WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
- WindowManagerStateHelper.isAppFullScreen(testApp.component))
- require(firstActivityVisible) { "Expected ${testApp.component} to be visible" }
- }
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ testApp.launchViaIntent(wmHelper)
+ }
+ teardown { testApp.exit(wmHelper) }
+ transitions {
+ testApp.openSecondActivity(device, wmHelper)
+ tapl.pressBack()
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
}
}
/**
- * Checks that the [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] activity is visible at
- * the start of the transition, that
- * [ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME] becomes visible during the
- * transition, and that [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] is again visible
- * at the end
+ * Checks that the [ActivityOptions.LaunchNewActivity] activity is visible at the start of the
+ * transition, that [ActivityOptions.SimpleActivity] becomes visible during the transition, and
+ * that [ActivityOptions.LaunchNewActivity] is again visible at the end
*/
@Presubmit
@Test
fun finishSubActivity() {
- val buttonActivityComponent = ActivityOptions
- .BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
- val imeAutoFocusActivityComponent = ActivityOptions
- .SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
- testSpec.assertWm {
+ val buttonActivityComponent =
+ ActivityOptions.LaunchNewActivity.COMPONENT.toFlickerComponent()
+ val imeAutoFocusActivityComponent =
+ ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
+ flicker.assertWm {
this.isAppWindowOnTop(buttonActivityComponent)
.then()
.isAppWindowOnTop(imeAutoFocusActivityComponent)
@@ -118,46 +96,36 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
}
/**
- * Checks that all parts of the screen are covered during the transition
- */
- @Presubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- /**
- * Checks that the [LAUNCHER_COMPONENT] window is not on top. The launcher cannot be
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is not on top. The launcher cannot be
* asserted with `isAppWindowVisible` because it contains 2 windows with the exact same name,
* and both are never simultaneously visible
*/
@Presubmit
@Test
fun launcherWindowNotOnTop() {
- testSpec.assertWm {
- this.isAppWindowNotOnTop(LAUNCHER_COMPONENT)
- }
+ flicker.assertWm { this.isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER) }
}
/**
- * Checks that the [LAUNCHER_COMPONENT] layer is never visible during the transition
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is never visible during the transition
*/
@Presubmit
@Test
fun launcherLayerNotVisible() {
- testSpec.assertLayers { this.isInvisible(LAUNCHER_COMPONENT) }
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.LAUNCHER) }
}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
new file mode 100644
index 000000000000..8b89a8b4c40d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
new file mode 100644
index 000000000000..549183f407e2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.wm.flicker.launch
+
+import android.tools.device.apphelpers.CameraAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app after cold opening camera
+ *
+ * To run this test: `atest FlickerTests:OpenAppAfterCameraTest`
+ *
+ * Notes: Some default assertions are inherited [OpenAppTransition]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class OpenAppAfterCameraTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+ private val cameraApp = CameraAppHelper(instrumentation)
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ // 1. Open camera - cold -> close it first
+ cameraApp.exit(wmHelper)
+ cameraApp.launchViaIntent(wmHelper)
+ // Can't use TAPL due to Recents not showing in 3 Button Nav in full screen mode
+ device.pressHome()
+ tapl.getWorkspace()
+ }
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.launchViaIntent(wmHelper) }
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
new file mode 100644
index 000000000000..ac05c7687311
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
new file mode 100644
index 000000000000..3a80c6649833
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.RequiresDevice
+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:OpenAppColdFromIcon`
+ *
+ * Actions:
+ * ```
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] by clicking it's icon on all apps and wait animation to complete
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class OpenAppColdFromIcon(flicker: FlickerTest) : 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) }
+ }
+
+ @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 appLayerReplacesLauncher() {
+ super.appLayerReplacesLauncher()
+ }
+
+ @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 [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
new file mode 100644
index 000000000000..d33a2724ca44
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) {
+ @Test
+ @FlakyTest
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 2f6b8f008119..26f88d23cda0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,16 +16,16 @@
package com.android.server.wm.flicker.launch
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,97 +38,57 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:OpenAppColdTest`
*
* Actions:
+ * ```
* Make sure no apps are running on the device
* Launch an app [testApp] and wait animation to complete
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-open class OpenAppColdTest(testSpec: FlickerTestParameter)
- : OpenAppFromLauncherTransition(testSpec) {
- /**
- * Defines the transition used to run the test
- */
+open class OpenAppColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- eachRun {
- removeAllTasksButHome()
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
+ removeAllTasksButHome()
+ this.setRotation(flicker.scenario.startRotation)
}
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.launchViaIntent(wmHelper) }
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- /** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
- }
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+ @Presubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appWindowReplacesLauncherAsTopWindow() =
- super.appWindowReplacesLauncherAsTopWindow()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
-
- /** {@inheritDoc} */
- @Presubmit
+ @Postsubmit
@Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests()
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
new file mode 100644
index 000000000000..d9a99dadbd3d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
@@ -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.launch
+
+import android.platform.test.annotations.FlakyTest
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 OpenAppColdTestCfArm(flicker: FlickerTest) : OpenAppColdTest(flicker) {
+ @FlakyTest(bugId = 273696733)
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
index c6e92adce8c7..3d5a8e309b10 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -17,55 +17,56 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerTest
import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
-/**
- * Base class for app launch tests
- */
-abstract class OpenAppFromLauncherTransition(testSpec: FlickerTestParameter)
- : OpenAppTransition(testSpec) {
+/** Base class for app launch tests */
+abstract class OpenAppFromLauncherTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
- /**
- * Checks that the focus changes from the launcher to [testApp]
- */
+ /** Checks that the focus changes from the [ComponentNameMatcher.LAUNCHER] to [testApp] */
@Presubmit
@Test
open fun focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges("NexusLauncherActivity", testApp.`package`)
- }
+ flicker.assertEventLog { this.focusChanges("NexusLauncherActivity", testApp.`package`) }
}
/**
- * Checks that [LAUNCHER_COMPONENT] layer is visible at the start of the transition, and
- * is replaced by [testApp], which remains visible until the end
+ * Checks that [ComponentNameMatcher.LAUNCHER] layer is visible at the start of the transition,
+ * and is replaced by [testApp], which remains visible until the end
*/
open fun appLayerReplacesLauncher() {
- testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component,
- ignoreEntriesWithRotationLayer = true, ignoreSnapshot = true,
- ignoreSplashscreen = true)
+ flicker.replacesLayer(
+ ComponentNameMatcher.LAUNCHER,
+ testApp,
+ ignoreEntriesWithRotationLayer = true,
+ ignoreSnapshot = true,
+ ignoreSplashscreen = true
+ )
}
/**
- * Checks that [LAUNCHER_COMPONENT] window is visible at the start of the transition, and
- * is replaced by a snapshot or splash screen (optional), and finally, is replaced by
- * [testApp], which remains visible until the end
+ * Checks that [ComponentNameMatcher.LAUNCHER] window is the top window at the start of the
+ * transition, and is replaced by a [ComponentNameMatcher.SNAPSHOT] or
+ * [ComponentNameMatcher.SPLASH_SCREEN], or [testApp], which remains visible until the end
*/
@Presubmit
@Test
open fun appWindowReplacesLauncherAsTopWindow() {
- testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(testApp.component)
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowOnTop(
+ testApp.or(ComponentNameMatcher.SNAPSHOT).or(ComponentNameMatcher.SPLASH_SCREEN)
+ )
}
}
-} \ No newline at end of file
+
+ /** Checks that [testApp] window is the top window at the en dof the trace */
+ @Presubmit
+ @Test
+ open fun appWindowAsTopWindowAtEnd() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index a8cbc5dc922c..b21777b30b21 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -17,14 +17,16 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.platform.test.rule.SettingOverrideRule
+import android.provider.Settings
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.ClassRule
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -41,10 +43,9 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
@Postsubmit
-open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter)
- : OpenAppFromNotificationCold(testSpec) {
+open class OpenAppFromLockNotificationCold(flicker: FlickerTest) :
+ OpenAppFromNotificationCold(flicker) {
override val openingNotificationsFromLockScreen = true
@@ -52,51 +53,83 @@ open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter)
get() = {
// Needs to run at start of transition,
// so before the transition defined in super.transition
- transitions {
- device.wakeUp()
- }
+ transitions { device.wakeUp() }
super.transition(this)
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
- eachRun {
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 229735718)
+ @Test @Ignore("Display is off at the start") override fun navBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is off at the start")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is off at the start")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test @Ignore("Display is off at the start") override fun taskBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Display is off at the start")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
@Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
- @FlakyTest(bugId = 203538234)
@Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsAlwaysVisible() {}
/** {@inheritDoc} */
- @FlakyTest(bugId = 203538234)
+ @Postsubmit
@Test
- override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
+
+ /**
+ * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+ * due to being marked as seen.
+ */
+ @ClassRule
+ @JvmField
+ val disableUnseenNotifFilterRule =
+ SettingOverrideRule(
+ Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+ /* value = */ "0",
+ )
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index cd8dea012db5..ec92ca65f80a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -16,16 +16,20 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.traces.common.FlickerComponentName
+import android.platform.test.annotations.FlakyTest
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
+import org.junit.ClassRule
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -42,10 +46,7 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-@Postsubmit
-open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter)
- : OpenAppFromNotificationWarm(testSpec) {
+class OpenAppFromLockNotificationWarm(flicker: FlickerTest) : OpenAppFromNotificationWarm(flicker) {
override val openingNotificationsFromLockScreen = true
@@ -53,76 +54,113 @@ open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter)
get() = {
// Needs to run at start of transition,
// so before the transition defined in super.transition
- transitions {
- device.wakeUp()
- }
+ transitions { device.wakeUp() }
super.transition(this)
// Needs to run at the end of the setup, so after the setup defined in super.transition
setup {
- eachRun {
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
}
/**
- * Checks that we start of with no top windows and then [testApp] becomes the first and
- * only top window of the transition, with snapshot or splash screen windows optionally showing
- * first.
+ * Checks that we start of with no top windows and then [testApp] becomes the first and only top
+ * window of the transition, with snapshot or splash screen windows optionally showing first.
*/
@Test
- @Postsubmit
- open fun appWindowBecomesFirstAndOnlyTopWindow() {
- testSpec.assertWm {
+ @Presubmit
+ fun appWindowBecomesFirstAndOnlyTopWindow() {
+ flicker.assertWm {
this.hasNoVisibleAppWindow()
- .then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(testApp.component)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp)
}
}
- /**
- * Checks that the screen is locked.
- */
+ /** Checks that the screen is locked at the start of the transition */
@Test
- @Postsubmit
+ @Presubmit
fun screenLockedStart() {
- testSpec.assertLayersStart {
- isEmpty()
- }
+ flicker.assertWmStart { isKeyguardShowing() }
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 229735718)
@Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun taskBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit @Test fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsAlwaysVisible() {}
/** {@inheritDoc} */
- @FlakyTest(bugId = 203538234)
+ @FlakyTest(bugId = 246284526)
@Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
+
+ /**
+ * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+ * due to being marked as seen.
+ */
+ @ClassRule
+ @JvmField
+ val disableUnseenNotifFilterRule =
+ SettingOverrideRule(
+ Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+ /* value= */ "0",
+ )
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
index bc637f8f9a63..009d61797fe0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -16,17 +16,17 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,8 +34,8 @@ import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
- * Test cold launching an app from a notification from the lock screen when there is an app
- * overlaid on the lock screen.
+ * Test cold launching an app from a notification from the lock screen when there is an app overlaid
+ * on the lock screen.
*
* To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
*/
@@ -43,12 +43,10 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
@Postsubmit
-class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParameter)
- : OpenAppFromLockNotificationCold(testSpec) {
- private val showWhenLockedApp: ShowWhenLockedAppHelper =
- ShowWhenLockedAppHelper(instrumentation)
+class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) :
+ OpenAppFromLockNotificationCold(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.
@@ -59,68 +57,80 @@ class OpenAppFromLockNotificationWithLockOverlayApp(testSpec: FlickerTestParamet
super.transition(this)
setup {
- eachRun {
- device.wakeUpAndGoToHomeScreen()
-
- // Launch an activity that is shown when the device is locked
- showWhenLockedApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(showWhenLockedApp.component)
-
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- }
- }
+ device.wakeUpAndGoToHomeScreen()
+
+ // Launch an activity that is shown when the device is locked
+ showWhenLockedApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(showWhenLockedApp).waitForAndVerify()
- teardown {
- test {
- showWhenLockedApp.exit(wmHelper)
- }
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+
+ teardown { showWhenLockedApp.exit(wmHelper) }
}
@Test
- @Postsubmit
+ @FlakyTest(bugId = 227143265)
fun showWhenLockedAppWindowBecomesVisible() {
- testSpec.assertWm {
+ flicker.assertWm {
this.hasNoVisibleAppWindow()
- .then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowOnTop(showWhenLockedApp.component)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(showWhenLockedApp)
}
}
@Test
- @Postsubmit
+ @FlakyTest(bugId = 227143265)
fun showWhenLockedAppLayerBecomesVisible() {
- testSpec.assertLayers {
- this.isInvisible(showWhenLockedApp.component)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(showWhenLockedApp.component)
+ flicker.assertLayers {
+ this.isInvisible(showWhenLockedApp)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(showWhenLockedApp)
}
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 229735718)
+ @Presubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 227143265)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 209599395)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @FlakyTest(bugId = 278227468)
@Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
index fe80162b5b81..eae9ca10c711 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -16,54 +16,36 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarLayerPositionEnd
-import com.android.server.wm.traces.common.FlickerComponentName
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import com.android.server.wm.flicker.navBarLayerPositionAtEnd
+import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
+import org.junit.Assume
+import org.junit.Ignore
import org.junit.Test
-/**
- * Base class for app launch tests from lock screen
- */
-abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter)
- : OpenAppTransition(testSpec) {
+/** Base class for app launch tests from lock screen */
+abstract class OpenAppFromLockTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
- /**
- * Defines the transition used to run the test
- */
- override val transition: FlickerBuilder.() -> Unit
- get() = {
- super.transition(this)
- setup {
- eachRun {
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- }
+ /** Defines the transition used to run the test */
+ override val transition: FlickerBuilder.() -> Unit = {
+ super.transition(this)
+ setup {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withoutTopVisibleAppWindows().waitForAndVerify()
}
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.launchViaIntent(wmHelper) }
+ }
- /**
- * Check that we go from no focus to focus on the [testApp]
- */
+ /** Check that we go from no focus to focus on the [testApp] */
@Presubmit
@Test
open fun focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges("", testApp.`package`)
- }
+ flicker.assertEventLog { this.focusChanges("", testApp.`package`) }
}
/**
@@ -73,27 +55,22 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter)
@FlakyTest(bugId = 203538234)
@Test
open fun appWindowBecomesFirstAndOnlyTopWindow() {
- testSpec.assertWm {
+ flicker.assertWm {
this.hasNoVisibleAppWindow()
- .then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(testApp.component)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp)
}
}
- /**
- * Checks that the screen is locked at the start of the transition ([colorFadComponent])
- * layer is visible
- */
+ /** Checks that the screen is locked at the start of the transition */
@Presubmit
@Test
fun screenLockedStart() {
- testSpec.assertLayersStart {
- isEmpty()
- }
+ flicker.assertLayersStart { isEmpty() }
}
/** {@inheritDoc} */
@@ -101,26 +78,50 @@ abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
- /**
- * Checks the position of the navigation bar at the start and end of the transition
- *
- * Differently from the normal usage of this assertion, check only the final state of the
- * transition because the display is off at the start and the NavBar is never visible
- */
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun navBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun taskBarWindowIsAlwaysVisible() {}
+
+ /** Checks the position of the [ComponentNameMatcher.NAV_BAR] at the end of the transition */
@Presubmit
@Test
- override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
+ open fun navBarLayerPositionAtEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerPositionAtEnd()
+ }
+
+ /** Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the end of the transition */
+ @Presubmit @Test fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
/**
- * Checks that the status bar layer is visible at the end of the trace
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the end of the trace
*
* It is not possible to check at the start because the screen is off
*/
@Presubmit
@Test
- override fun statusBarLayerIsVisible() {
- testSpec.assertLayersEnd {
- this.isVisible(FlickerComponentName.STATUS_BAR)
- }
+ fun statusBarLayerIsVisibleAtEnd() {
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
index 5022dd8f9bff..7bcb91070ecf 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -17,13 +17,16 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -40,45 +43,71 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
@Postsubmit
-open class OpenAppFromNotificationCold(testSpec: FlickerTestParameter)
- : OpenAppFromNotificationWarm(testSpec) {
+open class OpenAppFromNotificationCold(flicker: FlickerTest) :
+ OpenAppFromNotificationWarm(flicker) {
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- eachRun {
- // 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.
- taplInstrumentation.goHome()
- taplInstrumentation.workspace.switchToOverview()
- taplInstrumentation.overview.dismissAllTasks()
- }
+ // 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()
}
}
+ @Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+
+ @Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /**
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
+ * transition
+ */
+ @Presubmit @Test open fun statusBarLayerPositionAtEnd() = flicker.statusBarLayerPositionAtEnd()
+
+ /** {@inheritDoc} */
@Test
- @Postsubmit
- override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+ /** {@inheritDoc} */
@Test
- @Postsubmit
- override fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsAlwaysVisible() {}
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
index 7ffa51320487..8b4a613305c0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.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.
@@ -14,36 +14,34 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.ime
+package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
-import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group2
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume
-import org.junit.Before
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/**
- * Test IME window opening transitions.
- * To run this test: `atest FlickerTests:ReOpenImeWindowTest`
- */
-@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group2
-@FlakyTest(bugId = 221854428)
-class ReOpenImeWindowTest_ShellTransit(private val testSpec: FlickerTestParameter)
- : ReOpenImeWindowTest(testSpec) {
- @Before
- override fun before() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+@Postsubmit
+class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationCold(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 812f6859c7b0..425e674dec3a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -17,24 +17,28 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.RequiresDevice
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import android.view.WindowInsets
import android.view.WindowManager
-import androidx.test.filters.FlakyTest
+import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NotificationAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.navBarLayerPositionAtEnd
+import com.android.server.wm.flicker.navBarWindowIsVisibleAtEnd
+import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
+import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -51,30 +55,22 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-@Postsubmit
-open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter)
- : OpenAppTransition(testSpec) {
- protected val taplInstrumentation = LauncherInstrumentation()
-
+open class OpenAppFromNotificationWarm(flicker: FlickerTest) : OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
open val openingNotificationsFromLockScreen = false
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.startRotation)
- }
- eachRun {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- testApp.postNotification(device, wmHelper)
- device.pressHome()
- wmHelper.waitForAppTransitionIdle()
- }
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(flicker.scenario.startRotation)
+ testApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
+ testApp.postNotification(wmHelper)
+ device.pressHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
}
transitions {
@@ -82,11 +78,14 @@ open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter)
var endY = 3 * device.displayHeight / 4
var steps = 25
if (openingNotificationsFromLockScreen) {
- val wm = instrumentation.context.getSystemService(WindowManager::class.java)
+ 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())
+ val insets =
+ metricInsets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.statusBars() or WindowInsets.Type.displayCutout()
+ )
startY = insets.top + 100
endY = device.displayHeight / 2
@@ -100,98 +99,109 @@ open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter)
instrumentation.uiAutomation.syncInputTransactions()
// Launch the activity by clicking the notification
- val notification = device.wait(Until.findObject(
- By.text("Flicker Test Notification")), 2000L)
+ 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.waitForFullScreenApp(testApp.component)
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
}
- teardown {
- test {
- testApp.exit(wmHelper)
- }
- }
+ teardown { testApp.exit(wmHelper) }
}
- @Test
- @Postsubmit
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ @Presubmit @Test override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
+ @Presubmit @Test override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
+
+ @Presubmit
@Test
- @Postsubmit
- override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+ open fun notificationAppWindowVisibleAtEnd() {
+ flicker.assertWmEnd { this.isAppWindowVisible(testApp) }
+ }
+ @Presubmit
@Test
- @Postsubmit
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ open fun notificationAppWindowOnTopAtEnd() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
+ }
+ @Presubmit
@Test
- @Postsubmit
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ open fun notificationAppLayerVisibleAtEnd() {
+ flicker.assertLayersEnd { this.isVisible(testApp) }
+ }
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the end of the
+ * transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
@Test
- @Postsubmit
- override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
+ open fun taskBarWindowIsVisibleAtEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarWindowIsVisibleAtEnd()
+ }
+ /**
+ * Checks that the [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the transition
+ *
+ * Note: Large screen only
+ */
+ @Presubmit
@Test
- @Postsubmit
- override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
+ open fun taskBarLayerIsVisibleAtEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.taskBarLayerIsVisibleAtEnd()
+ }
+ /** Checks the position of the [ComponentNameMatcher.NAV_BAR] at the end of the transition */
+ @Presubmit
@Test
- @Postsubmit
- fun notificationAppWindowVisibleAtEnd() {
- testSpec.assertWmEnd {
- this.isAppWindowVisible(testApp.component)
- }
+ open fun navBarLayerPositionAtEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerPositionAtEnd()
}
+ /** {@inheritDoc} */
+ @Presubmit
@Test
- @Postsubmit
- fun notificationAppWindowOnTopAtEnd() {
- testSpec.assertWmEnd {
- this.isAppWindowOnTop(testApp.component)
- }
+ open fun navBarLayerIsVisibleAtEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarLayerIsVisibleAtEnd()
}
+ @Presubmit
@Test
- @Postsubmit
- fun notificationAppLayerVisibleAtEnd() {
- testSpec.assertLayersEnd {
- this.isVisible(testApp.component)
- }
+ open fun navBarWindowIsVisibleAtEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.navBarWindowIsVisibleAtEnd()
}
/** {@inheritDoc} */
- @Postsubmit
@Test
- override fun appWindowBecomesTopWindow() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- super.appWindowBecomesTopWindow()
- }
+ @Ignore("Display is off at the start")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
- @FlakyTest(bugId = 229738092)
+ /** {@inheritDoc} */
@Test
- fun appWindowBecomesTopWindow_ShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- super.appWindowBecomesTopWindow()
- }
+ @Postsubmit
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
new file mode 100644
index 000000000000..43d28fa60e51
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -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 com.android.server.wm.flicker.launch
+
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
+ OpenAppFromNotificationWarm(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 2226fd1d2155..8e1b059b2bc7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,21 +16,16 @@
package com.android.server.wm.flicker.launch
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Display
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.helpers.reopenAppFromOverview
+import android.tools.common.Rotation
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.traces.common.WindowManagerConditionsFactory
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -43,86 +38,59 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
*
* Actions:
+ * ```
* Launch [testApp]
* Press recents
* Relaunch an app [testApp] by selecting it in the overview screen, and wait animation to
* complete (only this action is traced)
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
- : OpenAppFromLauncherTransition(testSpec) {
+open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
- /**
- * Defines the transition used to run the test
- */
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- device.pressHome()
- wmHelper.waitForAppTransitionIdle()
- device.pressRecentApps()
- wmHelper.waitFor(
- WindowManagerConditionsFactory
- .isAppTransitionIdle(Display.DEFAULT_DISPLAY),
- WindowManagerConditionsFactory.isActivityVisible(LAUNCHER_COMPONENT),
- WindowManagerConditionsFactory.hasLayersAnimating().negate()
- )
- this.setRotation(testSpec.startRotation)
+ tapl.setExpectedRotationCheckEnabled(false)
+ testApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ // By default, launcher doesn't rotate on phones, but rotates on tablets
+ if (flicker.scenario.isTablet) {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ } else {
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
}
+ tapl.workspace.switchToOverview()
+ wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify()
+ this.setRotation(flicker.scenario.startRotation)
}
transitions {
- device.reopenAppFromOverview(wmHelper)
- wmHelper.waitFor(
- WindowManagerConditionsFactory.hasLayersAnimating().negate(),
- WindowManagerConditionsFactory.isWMStateComplete(),
- WindowManagerConditionsFactory.isLayerVisible(LAUNCHER_COMPONENT).negate(),
- WindowManagerConditionsFactory.isActivityVisible(LAUNCHER_COMPONENT).negate()
- )
- wmHelper.waitForFullScreenApp(testApp.component)
+ tapl.overview.currentTask.open()
+ wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify()
}
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
-
- /** {@inheritDoc} */
@FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Presubmit
@@ -134,53 +102,17 @@ open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
- /** {@inheritDoc} */
- @FlakyTest(bugId = 229735718)
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appWindowReplacesLauncherAsTopWindow() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- super.appWindowReplacesLauncherAsTopWindow()
- }
-
- @FlakyTest(bugId = 229738092)
- @Test
- fun appWindowReplacesLauncherAsTopWindow_ShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- super.appWindowReplacesLauncherAsTopWindow()
- }
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appWindowBecomesTopWindow() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- super.appWindowBecomesTopWindow()
- }
-
- @FlakyTest(bugId = 229738092)
- @Test
- fun appWindowBecomesTopWindow_ShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- super.appWindowBecomesTopWindow()
- }
-
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.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
new file mode 100644
index 000000000000..ff24190a7aef
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.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
+
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) : OpenAppFromOverviewTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 55eb3c3d1a0a..1383ae39f760 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -16,21 +16,20 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Postsubmit
-import androidx.test.filters.FlakyTest
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerPositionEnd
-import com.android.server.wm.traces.common.FlickerComponentName
+import org.junit.Assume
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -44,139 +43,182 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
*
* Actions:
+ * ```
* Lock the device.
* Launch an app on top of the lock screen [testApp] and wait animation to complete
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
- : OpenAppFromLockTransition(testSpec) {
+open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTransition(flicker) {
override val testApp = NonResizeableAppHelper(instrumentation)
- private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
/**
- * Checks that the nav bar layer starts invisible, becomes visible during unlocking animation
- * and remains visible at the end
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer starts invisible, becomes visible during
+ * unlocking animation and remains visible at the end
*/
- @FlakyTest(bugId = 227083463)
+ @Presubmit
@Test
fun navBarLayerVisibilityChanges() {
- testSpec.assertLayers {
- this.isInvisible(FlickerComponentName.NAV_BAR)
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.assertLayers {
+ this.isInvisible(ComponentNameMatcher.NAV_BAR)
.then()
- .isVisible(FlickerComponentName.NAV_BAR)
+ .isVisible(ComponentNameMatcher.NAV_BAR)
}
}
- /**
- * Checks if [testApp] is visible at the end of the transition
- */
+ /** Checks if [testApp] is visible at the end of the transition */
@Presubmit
@Test
fun appWindowBecomesVisibleAtEnd() {
- testSpec.assertWmEnd {
- this.isAppWindowVisible(testApp.component)
- }
+ flicker.assertWmEnd { this.isAppWindowVisible(testApp) }
}
/**
- * Checks that the nav bar starts the transition invisible, then becomes visible during
- * the unlocking animation and remains visible at the end of the transition
+ * 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
@Test
fun navBarWindowsVisibilityChanges() {
- testSpec.assertWm {
- this.isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.assertWm {
+ this.isNonAppWindowInvisible(ComponentNameMatcher.NAV_BAR)
.then()
- .isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
+ .isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR)
}
}
/**
- * Checks that the status bar layer is visible at the end of the trace
+ * Checks that the [ComponentNameMatcher.TASK_BAR] starts the transition invisible, then becomes
+ * visible during the unlocking animation and remains visible at the end of the transition
+ */
+ @Presubmit
+ @Test
+ fun taskBarLayerIsVisibleAtEnd() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the end of the trace
*
* It is not possible to check at the start because the screen is off
*/
@Presubmit
@Test
- override fun statusBarLayerIsVisible() {
- testSpec.assertLayersEnd {
- this.isVisible(FlickerComponentName.STATUS_BAR)
- }
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {}
/** {@inheritDoc} */
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerPositionAtEnd() {
- testSpec.assertLayersEnd {
- val display = this.entry.displays.minByOrNull { it.id }
- ?: error("There is no display!")
- this.visibleRegion(FlickerComponentName.STATUS_BAR)
- .coversExactly(WindowUtils.getStatusBarPosition(display))
- }
- }
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun navBarLayerIsVisibleAtStartAndEnd() {}
- /**
- * Checks the position of the navigation bar at the start and end of the transition
- *
- * Differently from the normal usage of this assertion, check only the final state of the
- * transition because the display is off at the start and the NavBar is never visible
- */
- @Postsubmit
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun taskBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun navBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
@Test
- override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
+ @Ignore("Not applicable to this CUJ. Display starts locked and app is full screen at the end")
+ override fun navBarWindowIsVisibleAtStartAndEnd() = super.navBarWindowIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. Display starts off and app is full screen at the end")
+ override fun statusBarWindowIsAlwaysVisible() {}
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowBecomesFirstAndOnlyTopWindow() =
+ super.appWindowBecomesFirstAndOnlyTopWindow()
+
+ /** {@inheritDoc} */
+ @Presubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ /** Checks the [ComponentNameMatcher.NAV_BAR] is visible at the end of the transition */
+ @Presubmit
+ @Test
+ fun navBarLayerIsVisibleAtEnd() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ flicker.assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
+ }
/** {@inheritDoc} */
@FlakyTest
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
- @FlakyTest
+ @Presubmit
+ @Test
+ override fun appLayerBecomesVisible() {
+ Assume.assumeFalse(flicker.scenario.isTablet)
+ super.appLayerBecomesVisible()
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 227143265)
@Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ fun appLayerBecomesVisibleTablet() {
+ Assume.assumeTrue(flicker.scenario.isTablet)
+ super.appLayerBecomesVisible()
+ }
- @FlakyTest(bugId = 218470989)
+ @Presubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 251217585)
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes =
- listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY),
- supportedRotations = listOf(Surface.ROTATION_0)
- )
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_0)
+ )
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 20e6d0222854..87a14c69e3ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -16,202 +16,95 @@
package com.android.server.wm.flicker.launch
-import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+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.FlickerTest
+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.StandardAppHelper
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
-/**
- * Base class for app launch tests
- */
-abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+/** Base class for app launch tests */
+abstract class OpenAppTransition(flicker: FlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
- /**
- * Defines the transition used to run the test
- */
- protected open val transition: FlickerBuilder.() -> Unit = {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- test {
- testApp.exit(wmHelper)
- }
- }
- }
-
- /**
- * Entry point for the test runner. It will use this method to initialize and cache
- * flicker executions
- */
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition()
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(flicker.scenario.startRotation)
}
+ teardown { testApp.exit(wmHelper) }
}
/**
- * Checks that the navigation bar window is visible during the whole transition
- */
- open fun navBarWindowIsVisible() {
- testSpec.navBarWindowIsVisible()
- }
-
- /**
- * Checks that the navigation bar layer is visible at the start and end of the trace
- */
- open fun navBarLayerIsVisible() {
- testSpec.navBarLayerIsVisible()
- }
-
- /**
- * Checks the position of the navigation bar at the start and end of the transition
+ * Checks that the [testApp] layer doesn't exist or is invisible at the start of the transition,
+ * but is created and/or becomes visible during the transition.
*/
@Presubmit
@Test
- open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- /**
- * Checks that the status bar window is visible during the whole transition
- */
- @Presubmit
- @Test
- open fun statusBarWindowIsVisible() {
- testSpec.statusBarWindowIsVisible()
+ open fun appLayerBecomesVisible() {
+ appLayerBecomesVisible_coldStart()
}
- /**
- * Checks that the status bar layer is visible at the start and end of the trace
- */
- @Presubmit
- @Test
- open fun statusBarLayerIsVisible() {
- testSpec.statusBarLayerIsVisible()
- }
-
- /**
- * Checks the position of the status bar at the start and end of the transition
- */
- @Presubmit
- @Test
- open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- /**
- * Checks that all windows that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- /**
- * Checks that all layers that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry()
- }
- }
-
- /**
- * Checks that all parts of the screen are covered during the transition
- */
- @Presubmit
- @Test
- open fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- /**
- * Checks that the app layer doesn't exist or is invisible at the start of the transition, but
- * is created and/or becomes visible during the transition.
- */
- @Presubmit
- @Test
- open fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
-
protected fun appLayerBecomesVisible_coldStart() {
- testSpec.assertLayers {
- this.notContains(testApp.component)
- .then()
- .isInvisible(testApp.component, isOptional = true)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isVisible(testApp.component)
+ flicker.assertLayers {
+ this.notContains(testApp)
+ .then()
+ .isInvisible(testApp, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isVisible(testApp)
}
}
protected fun appLayerBecomesVisible_warmStart() {
- testSpec.assertLayers {
- this.isInvisible(testApp.component)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isVisible(testApp.component)
+ flicker.assertLayers {
+ this.isInvisible(testApp)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isVisible(testApp)
}
}
/**
- * Checks that the app window doesn't exist at the start of the transition, that it is
+ * Checks that the [testApp] window doesn't exist at the start of the transition, that it is
* created (invisible - optional) and becomes visible during the transition
*
- * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging,
- * the window may be visible or not depending on what was processed until that moment.
+ * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, the
+ * window may be visible or not depending on what was processed until that moment.
*/
- @Presubmit
- @Test
- open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+ @Presubmit @Test open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
protected fun appWindowBecomesVisible_coldStart() {
- testSpec.assertWm {
- this.notContains(testApp.component)
- .then()
- .isAppWindowInvisible(testApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(testApp.component)
+ flicker.assertWm {
+ this.notContains(testApp)
+ .then()
+ .isAppWindowInvisible(testApp, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
}
}
protected fun appWindowBecomesVisible_warmStart() {
- testSpec.assertWm {
- this.isAppWindowInvisible(testApp.component)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowVisible(testApp.component)
+ flicker.assertWm {
+ this.isAppWindowInvisible(testApp)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
}
}
@@ -222,14 +115,22 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@Presubmit
@Test
open fun appWindowBecomesTopWindow() {
- testSpec.assertWm {
- this.isAppWindowNotOnTop(testApp.component)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(testApp.component)
+ flicker.assertWm {
+ this.isAppWindowNotOnTop(testApp)
+ .then()
+ .isAppWindowOnTop(
+ testApp.or(ComponentNameMatcher.SNAPSHOT).or(ComponentNameMatcher.SPLASH_SCREEN)
+ )
}
}
+
+ /**
+ * Checks that [testApp] window is not on top at the start of the transition, and then becomes
+ * the top visible window until the end of the transition.
+ */
+ @Presubmit
+ @Test
+ open fun appWindowIsTopWindowAtEnd() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
+ }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 97528c0471cc..3385830ee77f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,14 +16,14 @@
package com.android.server.wm.flicker.launch
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
@@ -37,88 +37,47 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:OpenAppWarmTest`
*
* Actions:
+ * ```
* Launch [testApp]
* Press home
* Relaunch an app [testApp] and wait animation to complete (only this action is traced)
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [OpenAppTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
+@FlickerServiceCompatible
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group1
-open class OpenAppWarmTest(testSpec: FlickerTestParameter)
- : OpenAppFromLauncherTransition(testSpec) {
- /**
- * Defines the transition used to run the test
- */
+open class OpenAppWarmTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+ /** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- eachRun {
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
+ tapl.setExpectedRotationCheckEnabled(false)
testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ this.setRotation(flicker.scenario.startRotation)
}
+ teardown { testApp.exit(wmHelper) }
+ transitions { testApp.launchViaIntent(wmHelper) }
}
/** {@inheritDoc} */
@FlakyTest(bugId = 206753786)
@Test
- override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appWindowReplacesLauncherAsTopWindow() =
- super.appWindowReplacesLauncherAsTopWindow()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
- /** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
-
- /** {@inheritDoc} */
- @Presubmit
- @Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Presubmit
@@ -130,22 +89,17 @@ open class OpenAppWarmTest(testSpec: FlickerTestParameter)
@Test
override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
- @FlakyTest(bugId = 229735718)
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests()
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
new file mode 100644
index 000000000000..d8b38b30cf13
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.annotation.FlickerServiceCompatible
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
new file mode 100644
index 000000000000..ae9ca8007dc8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
@@ -0,0 +1,167 @@
+/*
+ * 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.wm.flicker.launch
+
+import android.os.SystemClock
+import android.platform.test.annotations.Postsubmit
+import android.tools.device.apphelpers.CameraAppHelper
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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 org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test cold launching camera from launcher by double pressing power button
+ *
+ * To run this test: `atest FlickerTests:OpenCameraOnDoubleClickPowerButton`
+ *
+ * Actions:
+ * ```
+ * Make sure no apps are running on the device
+ * Launch an app [testApp] and wait animation to complete
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ * are inherited [OpenAppTransition]
+ * 2. Part of the test setup occurs automatically via
+ * [com.android.server.wm.flicker.TransitionRunnerWithRules],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
+ private val cameraApp = CameraAppHelper(instrumentation)
+ override val testApp: StandardAppHelper
+ get() = cameraApp
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ this.setRotation(flicker.scenario.startRotation)
+ }
+ transitions {
+ device.pressKeyCode(KeyEvent.KEYCODE_POWER)
+ SystemClock.sleep(100)
+ device.pressKeyCode(KeyEvent.KEYCODE_POWER)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(cameraApp).waitForAndVerify()
+ }
+ teardown { RemoveAllTasksButHomeRule.removeAllTasksButHome() }
+ }
+
+ @Postsubmit @Test override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+ @Postsubmit @Test override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+ @Postsubmit @Test override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+ @Postsubmit @Test override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ @Postsubmit @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ @Postsubmit @Test override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+ @Postsubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
+
+ @Postsubmit @Test override fun focusChanges() = super.focusChanges()
+
+ @Postsubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
+
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ @Ignore("Status bar visibility depends on whether the permission dialog is displayed or not")
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @Ignore("Not applicable to this CUJ. App is full screen at the end")
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() {
+ super.navBarWindowIsVisibleAtStartAndEnd()
+ }
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
new file mode 100644
index 000000000000..b48611edd738
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.os.Bundle
+import android.os.Handler
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.ConditionsFactory
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerBuilderProvider
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the [android.app.ActivityOptions.makeCustomTaskAnimation].
+ *
+ * To run this test: `atest FlickerTests:OverrideTaskTransitionTest`
+ *
+ * Actions:
+ * ```
+ * Launches SimpleActivity with a special animation.
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OverrideTaskTransitionTest(val flicker: FlickerTest) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ device.wakeUpAndGoToHomeScreen()
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ setRotation(flicker.scenario.startRotation)
+ }
+ transitions {
+ instrumentation.context.startActivity(
+ testApp.openAppIntent,
+ createCustomTaskAnimation()
+ )
+ wmHelper
+ .StateSyncBuilder()
+ .add(ConditionsFactory.isWMStateComplete())
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+ teardown { testApp.exit() }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun testSimpleActivityIsShownDirectly() {
+ flicker.assertLayers {
+ // Before the app launches, only the launcher is visible.
+ isVisible(ComponentNameMatcher.LAUNCHER)
+ .isInvisible(testApp)
+ .then()
+ // Animation starts, but the app may not be drawn yet which means the Splash
+ // may be visible.
+ .isSplashScreenVisibleFor(testApp, isOptional = true)
+ .then()
+ // App shows up with the custom animation starting at alpha=1.
+ .isVisible(testApp)
+ .then()
+ // App custom animation continues to alpha=0 (invisible).
+ .isInvisible(testApp)
+ .then()
+ // App custom animation ends with it being visible.
+ .isVisible(testApp)
+ }
+ }
+
+ private fun createCustomTaskAnimation(): Bundle {
+ return android.app.ActivityOptions.makeCustomTaskAnimation(
+ instrumentation.context,
+ R.anim.show_hide_show_3000ms,
+ 0,
+ Handler.getMain(),
+ null,
+ null
+ )
+ .toBundle()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 2f546b56f145..d0fd73207c42 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -18,29 +18,24 @@ package com.android.server.wm.flicker.launch
import android.app.Instrumentation
import android.app.WallpaperManager
-import android.platform.test.annotations.Postsubmit
+import android.content.res.Resources
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Presubmit
+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
+import android.tools.common.traces.component.ComponentSplashScreenMatcher
+import android.tools.common.traces.component.IComponentMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group4
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NewTasksAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.flicker.testapp.ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME
-import com.android.server.wm.flicker.testapp.ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.FlickerComponentName.Companion.SPLASH_SCREEN
-import com.android.server.wm.traces.common.FlickerComponentName.Companion.WALLPAPER_BBQ_WRAPPER
-import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -50,204 +45,177 @@ import org.junit.runners.Parameterized
/**
* Test the back and forward transition between 2 activities.
*
- * To run this test: `atest FlickerTests:ActivitiesTransitionTest`
+ * To run this test: `atest FlickerTests:TaskTransitionTest`
*
* Actions:
+ * ```
* Launch the NewTaskLauncherApp [mTestApp]
* Open a new task (SimpleActivity) from the NewTaskLauncherApp [mTestApp]
* Go back to the NewTaskLauncherApp [mTestApp]
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
-class TaskTransitionTest(val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val mTestApp: NewTasksAppHelper = NewTasksAppHelper(instrumentation)
- private val mWallpaper by lazy {
- getWallpaperPackage(InstrumentationRegistry.getInstrumentation())
- ?: error("Unable to obtain wallpaper")
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- eachRun {
- mTestApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(mTestApp.component)
- }
- }
- teardown {
- test {
- mTestApp.exit()
- }
- }
- transitions {
- mTestApp.openNewTask(device, wmHelper)
- device.pressBack()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(mTestApp.component)
- }
+class TaskTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
+ private val launchNewTaskApp = NewTasksAppHelper(instrumentation)
+ private val simpleApp = SimpleAppHelper(instrumentation)
+ private val wallpaper by lazy { getWallpaperPackage(instrumentation) }
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { launchNewTaskApp.launchViaIntent(wmHelper) }
+ teardown { launchNewTaskApp.exit(wmHelper) }
+ transitions {
+ launchNewTaskApp.openNewTask(device, wmHelper)
+ tapl.pressBack()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
}
}
/**
- * Checks that the wallpaper window is never visible when performing task transitions.
- * A solid color background should be shown instead.
+ * Checks that the [wallpaper] window is never visible when performing task transitions. A solid
+ * color background should be shown instead.
*/
- @Postsubmit
+ @FlakyTest(bugId = 253617416)
@Test
fun wallpaperWindowIsNeverVisible() {
- testSpec.assertWm {
- this.isNonAppWindowInvisible(mWallpaper)
- }
+ flicker.assertWm { this.isNonAppWindowInvisible(wallpaper) }
}
/**
- * Checks that the wallpaper layer is never visible when performing task transitions.
- * A solid color background should be shown instead.
+ * Checks that the [wallpaper] layer is never visible when performing task transitions. A solid
+ * color background should be shown instead.
*/
- @Postsubmit
+ @Presubmit
@Test
fun wallpaperLayerIsNeverVisible() {
- testSpec.assertLayers {
- this.isInvisible(mWallpaper)
+ flicker.assertLayers {
+ this.isInvisible(wallpaper)
this.isInvisible(WALLPAPER_BBQ_WRAPPER)
}
}
/**
- * Check that the launcher window is never visible when performing task transitions.
- * A solid color background should be shown above it.
+ * Check that the [ComponentNameMatcher.LAUNCHER] window is never visible when performing task
+ * transitions. A solid color background should be shown above it.
*/
- @Postsubmit
+ @Presubmit
@Test
fun launcherWindowIsNeverVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(LAUNCHER_COMPONENT)
- }
+ flicker.assertWm { this.isAppWindowInvisible(ComponentNameMatcher.LAUNCHER) }
}
/**
- * Checks that the launcher layer is never visible when performing task transitions.
- * A solid color background should be shown above it.
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is never visible when performing task
+ * transitions. A solid color background should be shown above it.
*/
- @Postsubmit
+ @Presubmit
@Test
fun launcherLayerIsNeverVisible() {
- testSpec.assertLayers {
- this.isInvisible(LAUNCHER_COMPONENT)
- }
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.LAUNCHER) }
}
- /**
- * Checks that a color background is visible while the task transition is occurring.
- */
- @Postsubmit
+ /** Checks that a color background is visible while the task transition is occurring. */
+ @FlakyTest(bugId = 265007895)
@Test
- fun colorLayerIsVisibleDuringTransition() {
- val bgColorLayer = FlickerComponentName("", "colorBackgroundLayer")
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
-
- testSpec.assertLayers {
+ fun transitionHasColorBackground() {
+ val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
+ flicker.assertLayers {
this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
- it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ it.visibleRegion(launchNewTaskApp.componentMatcher).coversExactly(displayBounds)
}
- .isInvisible(bgColorLayer)
+ .isInvisible(backgroundColorLayer)
+ .hasNoColor(backgroundColorLayer)
.then()
// Transitioning
- .isVisible(bgColorLayer)
+ .isVisible(backgroundColorLayer)
+ .hasColor(backgroundColorLayer)
.then()
// Fully transitioned to simple SIMPLE_ACTIVITY
+ .invoke(
+ "SIMPLE_ACTIVITY's splashscreen coversExactly displayBounds",
+ isOptional = true
+ ) {
+ it.visibleRegion(ComponentSplashScreenMatcher(simpleApp.componentMatcher))
+ .coversExactly(displayBounds)
+ }
.invoke("SIMPLE_ACTIVITY coversExactly displayBounds") {
- it.visibleRegion(SIMPLE_ACTIVITY).coversExactly(displayBounds)
+ it.visibleRegion(simpleApp.componentMatcher).coversExactly(displayBounds)
}
- .isInvisible(bgColorLayer)
+ .isInvisible(backgroundColorLayer)
+ .hasNoColor(backgroundColorLayer)
.then()
// Transitioning back
- .isVisible(bgColorLayer)
+ .isVisible(backgroundColorLayer)
+ .hasColor(backgroundColorLayer)
.then()
// Fully transitioned back to LAUNCH_NEW_TASK_ACTIVITY
+ .invoke(
+ "LAUNCH_NEW_TASK_ACTIVITY's splashscreen coversExactly displayBounds",
+ isOptional = true
+ ) {
+ it.visibleRegion(
+ ComponentSplashScreenMatcher(launchNewTaskApp.componentMatcher)
+ )
+ .coversExactly(displayBounds)
+ }
.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
- it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ it.visibleRegion(launchNewTaskApp.componentMatcher).coversExactly(displayBounds)
}
- .isInvisible(bgColorLayer)
+ .isInvisible(backgroundColorLayer)
+ .hasNoColor(backgroundColorLayer)
}
}
/**
- * Checks that we start with the LaunchNewTask activity on top and then open up
- * the SimpleActivity and then go back to the LaunchNewTask activity.
+ * Checks that we start with the LaunchNewTask activity on top and then open up the
+ * SimpleActivity and then go back to the LaunchNewTask activity.
*/
- @Postsubmit
+ @Presubmit
@Test
fun newTaskOpensOnTopAndThenCloses() {
- testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCH_NEW_TASK_ACTIVITY)
- .then()
- .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(SIMPLE_ACTIVITY)
- .then()
- .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
- .then()
- .isAppWindowOnTop(LAUNCH_NEW_TASK_ACTIVITY)
+ flicker.assertWm {
+ this.isAppWindowOnTop(launchNewTaskApp.componentMatcher)
+ .then()
+ .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(simpleApp.componentMatcher)
+ .then()
+ .isAppWindowOnTop(SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(launchNewTaskApp.componentMatcher)
}
}
- /**
- * Checks that all parts of the screen are covered at the start and end of the transition
- */
- @Postsubmit
- @Test
- fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- /**
- * Checks that the navbar window is visible throughout the transition
- */
- @Postsubmit
- @Test
- fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
-
- /**
- * Checks that the navbar layer is visible throughout the transition
- */
- @Postsubmit
- @Test
- fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
-
- /**
- * Checks that the status bar window is visible throughout the transition
- */
- @Postsubmit
- @Test
- fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
-
- /**
- * Checks that the status bar layer is visible throughout the transition
- */
- @Postsubmit
- @Test
- fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
-
companion object {
- private val LAUNCH_NEW_TASK_ACTIVITY =
- LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
- private val SIMPLE_ACTIVITY = SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
-
- private fun getWallpaperPackage(instrumentation: Instrumentation): FlickerComponentName? {
+ private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
return wallpaperManager.wallpaperInfo?.component?.toFlickerComponent()
+ ?: getStaticWallpaperPackage(instrumentation)
+ }
+
+ private fun getStaticWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
+ val resourceId =
+ Resources.getSystem()
+ .getIdentifier("image_wallpaper_component", "string", "android")
+ // frameworks/base/core/res/res/values/config.xml returns package plus class name,
+ // but wallpaper layer has only class name
+ val rawComponentMatcher =
+ ComponentNameMatcher.unflattenFromString(
+ instrumentation.targetContext.resources.getString(resourceId)
+ )
+
+ return ComponentNameMatcher(rawComponentMatcher.className)
}
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests()
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index c89e6a44ab6c..a8b80ad1d16c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -16,33 +16,21 @@
package com.android.server.wm.flicker.quickswitch
-import android.app.Instrumentation
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.tools.common.NavBar
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import org.junit.Assume
-import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -54,59 +42,43 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
*
* Actions:
+ * ```
* Launch an app [testApp1]
* Launch another app [testApp2]
* 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)
-@Group1
-open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val taplInstrumentation = LauncherInstrumentation()
-
+open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
-
- @Before
- open fun before() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- taplInstrumentation.setExpectedRotation(testSpec.startRotation)
- }
-
- eachRun {
- testApp1.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp1.component)
-
- testApp2.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp2.component)
- }
- }
- transitions {
- taplInstrumentation.launchedAppState.quickSwitchToPreviousApp()
- wmHelper.waitForFullScreenApp(testApp1.component)
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForNavBarStatusBarVisible()
- }
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ tapl.setIgnoreTaskbarVisibility(true)
+ testApp1.launchViaIntent(wmHelper)
+ testApp2.launchViaIntent(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(testApp1)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
- teardown {
- test {
- testApp1.exit()
- testApp2.exit()
- }
- }
+ teardown {
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
}
}
@@ -116,45 +88,35 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun startsWithApp2WindowsCoverFullScreen() {
- testSpec.assertWmStart {
- this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
- }
+ open fun startsWithApp2WindowsCoverFullScreen() {
+ flicker.assertWmStart { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
}
/**
* Checks that the transition starts with [testApp2]'s layers filling/covering exactly the
* entirety of the display.
*/
- @Presubmit
+ @FlakyTest(bugId = 250520840)
@Test
- fun startsWithApp2LayersCoverFullScreen() {
- testSpec.assertLayersStart {
- this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
- }
+ open fun startsWithApp2LayersCoverFullScreen() {
+ flicker.assertLayersStart { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
}
- /**
- * Checks that the transition starts with [testApp2] being the top window.
- */
+ /** Checks that the transition starts with [testApp2] being the top window. */
@Presubmit
@Test
- fun startsWithApp2WindowBeingOnTop() {
- testSpec.assertWmStart {
- this.isAppWindowOnTop(testApp2.component)
- }
+ open fun startsWithApp2WindowBeingOnTop() {
+ flicker.assertWmStart { this.isAppWindowOnTop(testApp2) }
}
/**
- * Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of the
- * transition once we have fully quick switched from [testApp2] back to the [testApp1].
+ * Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of
+ * the transition once we have fully quick switched from [testApp2] back to the [testApp1].
*/
@Presubmit
@Test
- fun endsWithApp1WindowsCoveringFullScreen() {
- testSpec.assertWmEnd {
- this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
- }
+ open fun endsWithApp1WindowsCoveringFullScreen() {
+ flicker.assertWmEnd { this.visibleRegion(testApp1).coversExactly(startDisplayBounds) }
}
/**
@@ -164,21 +126,17 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
@Presubmit
@Test
fun endsWithApp1LayersCoveringFullScreen() {
- testSpec.assertLayersEnd {
- this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
- }
+ flicker.assertLayersEnd { this.visibleRegion(testApp1).coversExactly(startDisplayBounds) }
}
/**
- * Checks that [testApp1] is the top window at the end of the transition once we have fully quick
- * switched from [testApp2] back to the [testApp1].
+ * Checks that [testApp1] is the top window at the end of the transition once we have fully
+ * quick switched from [testApp2] back to the [testApp1].
*/
@Presubmit
@Test
- fun endsWithApp1BeingOnTop() {
- testSpec.assertWmEnd {
- this.isAppWindowOnTop(testApp1.component)
- }
+ open fun endsWithApp1BeingOnTop() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp1) }
}
/**
@@ -187,13 +145,13 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app1WindowBecomesAndStaysVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(testApp1.component)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(testApp1.component)
+ open fun app1WindowBecomesAndStaysVisible() {
+ flicker.assertWm {
+ this.isAppWindowInvisible(testApp1)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp1)
}
}
@@ -203,12 +161,8 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app1LayerBecomesAndStaysVisible() {
- testSpec.assertLayers {
- this.isInvisible(testApp1.component)
- .then()
- .isVisible(testApp1.component)
- }
+ open fun app1LayerBecomesAndStaysVisible() {
+ flicker.assertLayers { this.isInvisible(testApp1).then().isVisible(testApp1) }
}
/**
@@ -217,12 +171,8 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app2WindowBecomesAndStaysInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(testApp2.component)
- .then()
- .isAppWindowInvisible(testApp2.component)
- }
+ open fun app2WindowBecomesAndStaysInvisible() {
+ flicker.assertWm { this.isAppWindowVisible(testApp2).then().isAppWindowInvisible(testApp2) }
}
/**
@@ -231,12 +181,8 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app2LayerBecomesAndStaysInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp2.component)
- .then()
- .isInvisible(testApp2.component)
- }
+ open fun app2LayerBecomesAndStaysInvisible() {
+ flicker.assertLayers { this.isVisible(testApp2).then().isInvisible(testApp2) }
}
/**
@@ -246,16 +192,16 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(testApp2.component)
- .then()
- // TODO: Do we actually want to test this? Seems too implementation specific...
- .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(testApp1.component)
+ open fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
+ flicker.assertWm {
+ this.isAppWindowVisible(testApp2)
+ .then()
+ // TODO: Do we actually want to test this? Seems too implementation specific...
+ .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp1)
}
}
@@ -266,67 +212,41 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa
*/
@Presubmit
@Test
- fun app1LayerIsVisibleOnceApp2LayerIsInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp2.component)
- .then()
- .isVisible(LAUNCHER_COMPONENT, isOptional = true)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(testApp1.component)
+ open fun app1LayerIsVisibleOnceApp2LayerIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(testApp2)
+ .then()
+ .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp1)
}
}
- /**
- * Checks that the navbar window is visible throughout the entire transition.
- */
- @Presubmit
+ /** {@inheritDoc} */
+ @Ignore("Nav bar window becomes invisible during quick switch")
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- /**
- * Checks that the navbar layer is visible throughout the entire transition.
- */
- @Presubmit
+ @FlakyTest(bugId = 246284708)
@Test
- fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
- /**
- * Checks that the navbar is always in the right position and covers the expected region.
- *
- * NOTE: This doesn't check that the navbar is visible or not.
- */
- @Presubmit
+ @FlakyTest(bugId = 250518877)
@Test
- fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
-
- /**
- * Checks that the status bar window is visible throughout the entire transition.
- */
- @Presubmit
- @Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
-
- /**
- * Checks that the status bar layer is visible throughout the entire transition.
- */
- @Presubmit
- @Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
+ private var startDisplayBounds = Rect.EMPTY
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- ),
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
- )
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
}
}
}
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
new file mode 100644
index 000000000000..f970a79abcb8
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) :
+ QuickSwitchBetweenTwoAppsBackTest(flicker) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
deleted file mode 100644
index b9fef085da29..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.quickswitch
-
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test quick switching back to previous app from last opened app
- *
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
- *
- * Actions:
- * Launch an app [testApp1]
- * Launch another app [testApp2]
- * 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)
-@Group1
-@FlakyTest(bugId = 228009808)
-open class QuickSwitchBetweenTwoAppsBackTest_ShellTransit(testSpec: FlickerTestParameter)
- : QuickSwitchBetweenTwoAppsBackTest(testSpec) {
- @Before
- override fun before() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 725d2c3d818c..96cd8ffa3eff 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -16,33 +16,21 @@
package com.android.server.wm.flicker.quickswitch
-import android.app.Instrumentation
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import android.tools.common.NavBar
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
-import com.android.server.wm.traces.common.Rect
-import org.junit.Assume
-import org.junit.Before
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -54,68 +42,51 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest`
*
* Actions:
+ * ```
* Launch an app [testApp1]
* Launch another app [testApp2]
* Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
* 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)
-@Group1
-open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val taplInstrumentation = LauncherInstrumentation()
-
+open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- @Before
- open fun before() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- }
-
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- taplInstrumentation.setExpectedRotation(testSpec.startRotation)
- }
-
- eachRun {
- testApp1.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp1.component)
-
- testApp2.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp2.component)
-
- startDisplayBounds = wmHelper.currentState.layerState
- .displays.firstOrNull { !it.isVirtual }
- ?.layerStackSpace
- ?: error("Display not found")
-
- taplInstrumentation.launchedAppState.quickSwitchToPreviousApp()
-
- wmHelper.waitForFullScreenApp(testApp1.component)
- wmHelper.waitForAppTransitionIdle()
- }
- }
- transitions {
- taplInstrumentation.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
-
- wmHelper.waitForFullScreenApp(testApp2.component)
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForNavBarStatusBarVisible()
- }
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+
+ testApp1.launchViaIntent(wmHelper)
+ testApp2.launchViaIntent(wmHelper)
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(testApp1)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(testApp2)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
- teardown {
- test {
- testApp1.exit(wmHelper)
- testApp2.exit(wmHelper)
- }
- }
+ teardown {
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
}
}
@@ -126,8 +97,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun startsWithApp1WindowsCoverFullScreen() {
- testSpec.assertWmStart {
- this.frameRegion(testApp1.component, FlickerComponentName.LETTERBOX)
+ flicker.assertWmStart {
+ this.visibleRegion(testApp1.or(ComponentNameMatcher.LETTERBOX))
.coversExactly(startDisplayBounds)
}
}
@@ -136,23 +107,17 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
* Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
* entirety of the display.
*/
- @Presubmit
+ @FlakyTest(bugId = 250522691)
@Test
open fun startsWithApp1LayersCoverFullScreen() {
- testSpec.assertLayersStart {
- this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
- }
+ flicker.assertLayersStart { this.visibleRegion(testApp1).coversExactly(startDisplayBounds) }
}
- /**
- * Checks that the transition starts with [testApp1] being the top window.
- */
+ /** Checks that the transition starts with [testApp1] being the top window. */
@Presubmit
@Test
open fun startsWithApp1WindowBeingOnTop() {
- testSpec.assertWmStart {
- this.isAppWindowOnTop(testApp1.component)
- }
+ flicker.assertWmStart { this.isAppWindowOnTop(testApp1) }
}
/**
@@ -162,9 +127,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun endsWithApp2WindowsCoveringFullScreen() {
- testSpec.assertWmEnd {
- this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
- }
+ flicker.assertWmEnd { this.visibleRegion(testApp2).coversExactly(startDisplayBounds) }
}
/**
@@ -174,8 +137,8 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun endsWithApp2LayersCoveringFullScreen() {
- testSpec.assertLayersEnd {
- this.visibleRegion(testApp2.component, FlickerComponentName.LETTERBOX)
+ flicker.assertLayersEnd {
+ this.visibleRegion(testApp2.or(ComponentNameMatcher.LETTERBOX))
.coversExactly(startDisplayBounds)
}
}
@@ -187,9 +150,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun endsWithApp2BeingOnTop() {
- testSpec.assertWmEnd {
- this.isAppWindowOnTop(testApp2.component)
- }
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp2) }
}
/**
@@ -199,12 +160,12 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app2WindowBecomesAndStaysVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(testApp2.component)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(testApp2.component)
+ flicker.assertWm {
+ this.isAppWindowInvisible(testApp2)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2)
}
}
@@ -215,11 +176,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app2LayerBecomesAndStaysVisible() {
- testSpec.assertLayers {
- this.isInvisible(testApp2.component)
- .then()
- .isVisible(testApp2.component)
- }
+ flicker.assertLayers { this.isInvisible(testApp2).then().isVisible(testApp2) }
}
/**
@@ -229,11 +186,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app1WindowBecomesAndStaysInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(testApp1.component)
- .then()
- .isAppWindowInvisible(testApp1.component)
- }
+ flicker.assertWm { this.isAppWindowVisible(testApp1).then().isAppWindowInvisible(testApp1) }
}
/**
@@ -243,11 +196,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app1LayerBecomesAndStaysInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp1.component)
- .then()
- .isInvisible(testApp1.component)
- }
+ flicker.assertLayers { this.isVisible(testApp1).then().isInvisible(testApp1) }
}
/**
@@ -258,14 +207,14 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
- testSpec.assertWm {
- this.isAppWindowVisible(testApp1.component)
- .then()
- .isAppWindowVisible(LAUNCHER_COMPONENT, isOptional = true)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(testApp2.component)
+ flicker.assertWm {
+ this.isAppWindowVisible(testApp1)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp2)
}
}
@@ -277,78 +226,45 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes
@Presubmit
@Test
open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
- testSpec.assertLayers {
- this.isVisible(testApp1.component)
- .then()
- .isVisible(LAUNCHER_COMPONENT, isOptional = true)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(testApp2.component)
+ flicker.assertLayers {
+ this.isVisible(testApp1)
+ .then()
+ .isVisible(ComponentNameMatcher.LAUNCHER, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp2)
}
}
- /**
- * Checks that the navbar window is visible throughout the entire transition.
- */
+ /** {@inheritDoc} */
@Presubmit
@Test
- open fun navBarWindowIsAlwaysVisible() {
- testSpec.navBarWindowIsVisible()
- }
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- /**
- * Checks that the navbar layer is visible throughout the entire transition.
- */
- @Presubmit
+ /** {@inheritDoc} */
+ @Ignore("Nav bar window becomes invisible during quick switch")
@Test
- open fun navBarLayerAlwaysIsVisible() {
- testSpec.navBarLayerIsVisible()
- }
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- /**
- * Checks that the navbar is always in the right position and covers the expected region.
- *
- * NOTE: This doesn't check that the navbar is visible or not.
- */
- @Presubmit
+ @FlakyTest(bugId = 246284708)
@Test
- open fun navbarIsAlwaysInRightPosition() {
- testSpec.navBarLayerRotatesAndScales()
- }
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
- /**
- * Checks that the status bar window is visible throughout the entire transition.
- */
- @Presubmit
- @Test
- open fun statusBarWindowIsAlwaysVisible() {
- testSpec.statusBarWindowIsVisible()
- }
-
- /**
- * Checks that the status bar layer is visible throughout the entire transition.
- */
- @Presubmit
+ @FlakyTest(bugId = 250518877)
@Test
- open fun statusBarLayerIsAlwaysVisible() {
- testSpec.statusBarLayerIsVisible()
- }
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
companion object {
private var startDisplayBounds = Rect.EMPTY
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- ),
- supportedRotations = listOf(Surface.ROTATION_0, Surface.ROTATION_90)
- )
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.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
new file mode 100644
index 000000000000..9f48cdae20f1
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) :
+ QuickSwitchBetweenTwoAppsForwardTest(flicker) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
deleted file mode 100644
index 4b8a8c80cd45..000000000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.quickswitch
-
-import android.platform.test.annotations.RequiresDevice
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume
-import org.junit.Before
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-/**
- * Test quick switching back to previous app from last opened app
- *
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest`
- *
- * Actions:
- * Launch an app [testApp1]
- * Launch another app [testApp2]
- * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1]
- * 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)
-@Group1
-@FlakyTest(bugId = 228009808)
-open class QuickSwitchBetweenTwoAppsForwardTest_ShellTransit(testSpec: FlickerTestParameter)
- : QuickSwitchBetweenTwoAppsForwardTest(testSpec) {
- @Before
- override fun before() {
- Assume.assumeTrue(isShellTransitionsEnabled)
- }
-}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index cc4a4b2d38aa..7e935f03aeb1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -16,30 +16,21 @@
package com.android.server.wm.flicker.quickswitch
-import android.app.Instrumentation
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
-import android.view.Surface
-import android.view.WindowManagerPolicyConstants
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.LAUNCHER_COMPONENT
-import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.entireScreenCovered
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -51,52 +42,47 @@ import org.junit.runners.Parameterized
* To run this test: `atest FlickerTests:QuickSwitchFromLauncherTest`
*
* Actions:
+ * ```
* Launch an app
* Navigate home to show launcher
* 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)
-@Group1
-class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
- private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val taplInstrumentation = LauncherInstrumentation()
-
+open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- setup {
- test {
- taplInstrumentation.setExpectedRotation(testSpec.startRotation)
- }
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
- eachRun {
- testApp.launchViaIntent(wmHelper)
- device.pressHome()
- wmHelper.waitForHomeActivityVisible()
- wmHelper.waitForWindowSurfaceDisappeared(testApp.component)
- }
- }
- transitions {
- taplInstrumentation.workspace.quickSwitchToPreviousApp()
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForNavBarStatusBarVisible()
- }
+ testApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(testApp)
+ .waitForAndVerify()
- teardown {
- eachRun {
- testApp.exit()
- }
- }
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(testApp)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
+ teardown { testApp.exit(wmHelper) }
}
/**
@@ -106,9 +92,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun endsWithAppWindowsCoveringFullScreen() {
- testSpec.assertWmEnd {
- this.frameRegion(testApp.component).coversExactly(startDisplayBounds)
- }
+ flicker.assertWmEnd { this.visibleRegion(testApp).coversExactly(startDisplayBounds) }
}
/**
@@ -118,9 +102,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun endsWithAppLayersCoveringFullScreen() {
- testSpec.assertLayersEnd {
- this.visibleRegion(testApp.component).coversExactly(startDisplayBounds)
- }
+ flicker.assertLayersEnd { this.visibleRegion(testApp).coversExactly(startDisplayBounds) }
}
/**
@@ -130,55 +112,48 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun endsWithAppBeingOnTop() {
- testSpec.assertWmEnd {
- this.isAppWindowOnTop(testApp.component)
- }
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
}
- /**
- * Checks that the transition starts with the home activity being tagged as visible.
- */
+ /** Checks that the transition starts with the home activity being tagged as visible. */
@Presubmit
@Test
fun startsWithHomeActivityFlaggedVisible() {
- testSpec.assertWmStart {
- this.isHomeActivityVisible()
- }
+ flicker.assertWmStart { this.isHomeActivityVisible() }
}
/**
- * Checks that the transition starts with the launcher windows filling/covering exactly the
- * entirety of the display.
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] windows
+ * filling/covering exactly display size
*/
@Presubmit
@Test
fun startsWithLauncherWindowsCoverFullScreen() {
- testSpec.assertWmStart {
- this.frameRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds)
+ flicker.assertWmStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
}
}
/**
- * Checks that the transition starts with the launcher layers filling/covering exactly the
- * entirety of the display.
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] layers
+ * filling/covering exactly the display size.
*/
@Presubmit
@Test
fun startsWithLauncherLayersCoverFullScreen() {
- testSpec.assertLayersStart {
- this.visibleRegion(LAUNCHER_COMPONENT).coversExactly(startDisplayBounds)
+ flicker.assertLayersStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
}
}
/**
- * Checks that the transition starts with the launcher being the top window.
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] being the top
+ * window.
*/
@Presubmit
@Test
fun startsWithLauncherBeingOnTop() {
- testSpec.assertWmStart {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
- }
+ flicker.assertWmStart { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) }
}
/**
@@ -188,9 +163,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun endsWithHomeActivityFlaggedInvisible() {
- testSpec.assertWmEnd {
- this.isHomeActivityInvisible()
- }
+ flicker.assertWmEnd { this.isHomeActivityInvisible() }
}
/**
@@ -200,11 +173,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun appWindowBecomesAndStaysVisible() {
- testSpec.assertWm {
- this.isAppWindowInvisible(testApp.component)
- .then()
- .isAppWindowVisible(testApp.component)
- }
+ flicker.assertWm { this.isAppWindowInvisible(testApp).then().isAppWindowVisible(testApp) }
}
/**
@@ -214,130 +183,101 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun appLayerBecomesAndStaysVisible() {
- testSpec.assertLayers {
- this.isInvisible(testApp.component)
- .then()
- .isVisible(testApp.component)
- }
+ flicker.assertLayers { this.isInvisible(testApp).then().isVisible(testApp) }
}
/**
- * Checks that the launcher 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.
+ * Checks that the [ComponentNameMatcher.LAUNCHER] 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
@Test
fun launcherWindowBecomesAndStaysInvisible() {
- testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
- .then()
- .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
}
}
/**
- * Checks that the launcher 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.
+ * Checks that the [ComponentNameMatcher.LAUNCHER] 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
fun launcherLayerBecomesAndStaysInvisible() {
- testSpec.assertLayers {
- this.isVisible(LAUNCHER_COMPONENT)
- .then()
- .isInvisible(LAUNCHER_COMPONENT)
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
}
}
/**
- * Checks that the launcher window is visible at least until the app window is visible. Ensures
- * that at any point, either the launcher or [testApp] windows are at least partially visible.
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is visible at least until the app
+ * window is visible. Ensures that at any point, either the launcher or [testApp] windows are at
+ * least partially visible.
*/
@Presubmit
@Test
fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
- testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
- .then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isAppWindowVisible(testApp.component)
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
}
}
/**
- * Checks that the launcher layer is visible at least until the app layer is visible. Ensures
- * that at any point, either the launcher or [testApp] layers are at least partially visible.
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is visible at least until the app layer
+ * is visible. Ensures that at any point, either the launcher or [testApp] layers are at least
+ * partially visible.
*/
@Presubmit
@Test
fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
- testSpec.assertLayers {
- this.isVisible(LAUNCHER_COMPONENT)
- .then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
- .then()
- .isVisible(testApp.component)
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(testApp)
}
}
- /**
- * Checks that the navbar window is visible throughout the entire transition.
- */
- @Presubmit
- @Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
-
- /**
- * Checks that the navbar layer is visible throughout the entire transition.
- */
- @Presubmit
- @Test
- fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
-
- /**
- * Checks that the navbar is always in the right position and covers the expected region.
- *
- * NOTE: This doesn't check that the navbar is visible or not.
- */
+ /** {@inheritDoc} */
@Presubmit
@Test
- fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
- /**
- * Checks that the status bar window is visible throughout the entire transition.
- */
- @Presubmit
+ /** {@inheritDoc} */
+ @Ignore("Nav bar window becomes invisible during quick switch")
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
- /**
- * Checks that the status bar layer is visible throughout the entire transition.
- */
- @Presubmit
+ @FlakyTest(bugId = 246285528)
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
-
- /**
- * Checks that the screen is always fully covered by visible layers throughout the transition.
- */
- @Presubmit
- @Test
- fun screenIsAlwaysFilled() = testSpec.entireScreenCovered()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(
- repetitions = 3,
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- ),
- // TODO: Test with 90 rotation
- supportedRotations = listOf(Surface.ROTATION_0)
- )
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.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/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
new file mode 100644
index 000000000000..af671df194ae
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
@@ -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 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) :
+ QuickSwitchFromLauncherTest(flicker) {
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.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/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index f0d16f3edb11..ef75c6105b53 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,19 +16,15 @@
package com.android.server.wm.flicker.rotation
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import androidx.test.filters.FlakyTest
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.statusBarLayerIsVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsVisible
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,54 +35,62 @@ import org.junit.runners.Parameterized
* Test opening an app and cycling through app rotations
*
* Currently runs:
+ * ```
* 0 -> 90 degrees
* 90 -> 0 degrees
+ * ```
*
* Actions:
+ * ```
* Launch an app (via intent)
* Set initial device orientation
* Start tracing
* Change device orientation
* Stop tracing
+ * ```
*
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
*
* To run only the presubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
*
* To run only the postsubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
*
* To run only the flaky assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [RotationTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
-class ChangeAppRotationTest(
- testSpec: FlickerTestParameter
-) : RotationTransition(testSpec) {
+open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) {
override val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
- setup {
- test {
- testApp.launchViaIntent(wmHelper)
- }
- }
+ setup { testApp.launchViaIntent(wmHelper) }
}
/**
@@ -96,29 +100,27 @@ class ChangeAppRotationTest(
@Presubmit
@Test
fun focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges(testApp.`package`)
- }
+ flicker.assertEventLog { this.focusChanges(testApp.`package`) }
}
/**
- * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
- * doesn't flicker, and disappears before the transition is complete
+ * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+ * flicker, and disappears before the transition is complete
*/
fun rotationLayerAppearsAndVanishesAssertion() {
- testSpec.assertLayers {
- this.isVisible(testApp.component)
+ flicker.assertLayers {
+ this.isVisible(testApp)
.then()
- .isVisible(FlickerComponentName.ROTATION)
+ .isVisible(ComponentNameMatcher.ROTATION)
.then()
- .isVisible(testApp.component)
- .isInvisible(FlickerComponentName.ROTATION)
+ .isVisible(testApp)
+ .isInvisible(ComponentNameMatcher.ROTATION)
}
}
/**
- * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
- * doesn't flicker, and disappears before the transition is complete
+ * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
+ * flicker, and disappears before the transition is complete
*/
@Presubmit
@Test
@@ -126,57 +128,25 @@ class ChangeAppRotationTest(
rotationLayerAppearsAndVanishesAssertion()
}
- /**
- * Checks that the status bar window is visible and above the app windows in all WM
- * trace entries
- */
- @Presubmit
- @Test
- fun statusBarWindowIsVisible() {
- testSpec.statusBarWindowIsVisible()
- }
-
- /**
- * Checks that the status bar layer is visible at the start and end of the transition
- */
- @Presubmit
- @Test
- fun statusBarLayerIsVisible() {
- testSpec.statusBarLayerIsVisible()
- }
-
- /**
- * Checks the position of the status bar at the start and end of the transition
- */
- @FlakyTest(bugId = 206753786)
- @Test
- fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
-
- /** {@inheritDoc} */
- @FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() {
- super.navBarLayerRotatesAndScales()
+ @PlatinumTest(focusArea = "framework")
+ override fun cujCompleted() {
+ super.cujCompleted()
+ focusChanges()
+ rotationLayerAppearsAndVanishes()
}
- /** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
-
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
+ * modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 3)
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests()
}
}
}
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
new file mode 100644
index 000000000000..0e6b20fc21c6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) : ChangeAppRotationTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
+ * modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 0becadf630e1..fe9da335a675 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -16,141 +16,67 @@
package com.android.server.wm.flicker.rotation
-import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.StandardAppHelper
+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.FlickerTest
+import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsVisible
-import com.android.server.wm.flicker.entireScreenCovered
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
-/**
- * Base class for app rotation tests
- */
-abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
+/** Base class for app rotation tests */
+abstract class RotationTransition(flicker: FlickerTest) : BaseTest(flicker) {
protected abstract val testApp: StandardAppHelper
- protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-
- protected open val transition: FlickerBuilder.() -> Unit = {
- setup {
- eachRun {
- this.setRotation(testSpec.startRotation)
- }
- }
- teardown {
- test {
- testApp.exit()
- }
- }
- transitions {
- this.setRotation(testSpec.endRotation)
- }
- }
-
- /**
- * Entry point for the test runner. It will use this method to initialize and cache
- * flicker executions
- */
- @FlickerBuilderProvider
- fun buildFlicker(): FlickerBuilder {
- return FlickerBuilder(instrumentation).apply {
- transition()
- }
- }
-
- /**
- * Checks that the navigation bar window is visible and above the app windows in all WM
- * trace entries
- */
- @Presubmit
- @Test
- open fun navBarWindowIsVisible() {
- testSpec.navBarWindowIsVisible()
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup { this.setRotation(flicker.scenario.startRotation) }
+ teardown { testApp.exit(wmHelper) }
+ transitions { this.setRotation(flicker.scenario.endRotation) }
}
- /**
- * Checks that the navigation bar layer is visible at the start and end of the transition
- */
+ /** {@inheritDoc} */
@Presubmit
@Test
- open fun navBarLayerIsVisible() {
- testSpec.navBarLayerIsVisible()
- }
-
- /**
- * Checks the position of the navigation bar at the start and end of the transition
- */
- @Presubmit
- @Test
- open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
-
- /**
- * Checks that all layers that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.assertLayers {
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
this.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers = listOf(FlickerComponentName.SPLASH_SCREEN,
- FlickerComponentName.SNAPSHOT,
- FlickerComponentName("", "SecondaryHomeHandle")
- )
+ ignoreLayers =
+ listOf(
+ ComponentNameMatcher.SPLASH_SCREEN,
+ ComponentNameMatcher.SNAPSHOT,
+ ComponentNameMatcher("", "SecondaryHomeHandle")
+ )
)
}
}
- /**
- * Checks that all windows that are visible on the trace, are visible for at least 2
- * consecutive entries.
- */
- @Presubmit
- @Test
- open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
- }
-
- /**
- * Checks that all parts of the screen are covered during the transition
- */
- @Presubmit
- @Test
- open fun entireScreenCovered() = testSpec.entireScreenCovered()
-
- /**
- * Checks that [testApp] layer covers the entire screen at the start of the transition
- */
+ /** Checks that [testApp] layer covers the entire screen at the start of the transition */
@Presubmit
@Test
open fun appLayerRotates_StartingPos() {
- testSpec.assertLayersStart {
+ flicker.assertLayersStart {
this.entry.displays.map { display ->
- this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ this.visibleRegion(testApp).coversExactly(display.layerStackSpace)
}
}
}
- /**
- * Checks that [testApp] layer covers the entire screen at the end of the transition
- */
+ /** Checks that [testApp] layer covers the entire screen at the end of the transition */
@Presubmit
@Test
open fun appLayerRotates_EndingPos() {
- testSpec.assertLayersEnd {
+ flicker.assertLayersEnd {
this.entry.displays.map { display ->
- this.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ 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/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index fac5baf7a2f9..f654bdd48e89 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,19 +16,20 @@
package com.android.server.wm.flicker.rotation
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
-import android.platform.test.annotations.RequiresDevice
+import android.tools.common.ScenarioBuilder
+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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
import android.view.WindowManager
-import androidx.test.filters.FlakyTest
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
-import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.annotation.Group3
-import com.android.server.wm.flicker.dsl.FlickerBuilder
+import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -37,205 +38,241 @@ import org.junit.runners.Parameterized
/**
* Test opening an app and cycling through app rotations using seamless rotations
*
- * Currently runs:
+ * Currently, runs:
+ * ```
* 0 -> 90 degrees
* 0 -> 90 degrees (with starved UI thread)
* 90 -> 0 degrees
* 90 -> 0 degrees (with starved UI thread)
+ * ```
*
* Actions:
+ * ```
* Launch an app in fullscreen and supporting seamless rotation (via intent)
* Set initial device orientation
* Start tracing
* Change device orientation
* Stop tracing
+ * ```
*
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
*
* To run only the presubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
+ * ```
*
* To run only the postsubmit assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
+ * ```
*
* To run only the flaky assertions add: `--
+ *
+ * ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
+ * ```
*
* Notes:
+ * ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
* are inherited [RotationTransition]
* 2. Part of the test setup occurs automatically via
* [com.android.server.wm.flicker.TransitionRunnerWithRules],
* including configuring navigation mode, initial orientation and ensuring no
* apps are running before setup
+ * ```
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group3
-open class SeamlessAppRotationTest(
- testSpec: FlickerTestParameter
-) : RotationTransition(testSpec) {
+open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) {
override val testApp = SeamlessRotationAppHelper(instrumentation)
+ /** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- test {
- testApp.launchViaIntent(wmHelper,
- stringExtras = mapOf(ActivityOptions.EXTRA_STARVE_UI_THREAD
- to testSpec.starveUiThread.toString())
- )
- }
+ testApp.launchViaIntent(
+ wmHelper,
+ stringExtras =
+ mapOf(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD to
+ flicker.starveUiThread.toString()
+ )
+ )
}
}
- /**
- * Checks that [testApp] window is always in full screen
- */
+ /** Checks that [testApp] window is always in full screen */
@Presubmit
@Test
fun appWindowFullScreen() {
- testSpec.assertWm {
+ flicker.assertWm {
this.invoke("isFullScreen") {
- val appWindow = it.windowState(testApp.`package`)
- val flags = appWindow.windowState?.attributes?.flags ?: 0
- appWindow.verify("isFullScreen")
+ val appWindow =
+ it.windowState(testApp.`package`)
+ ?: error("App window for package ${testApp.`package`} not found")
+ val flags = appWindow.windowState.attributes.flags
+ appWindow
+ .check { "isFullScreen" }
.that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN))
- .isGreaterThan(0)
+ .isGreater(0)
}
}
}
- /**
- * Checks that [testApp] window is always with seamless rotation
- */
+ /** Checks that [testApp] window is always with seamless rotation */
@Presubmit
@Test
fun appWindowSeamlessRotation() {
- testSpec.assertWm {
+ flicker.assertWm {
this.invoke("isRotationSeamless") {
- val appWindow = it.windowState(testApp.`package`)
- val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0
- appWindow.verify("isRotationSeamless")
- .that(rotationAnimation
- .and(WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS))
- .isGreaterThan(0)
+ val appWindow =
+ it.windowState(testApp.`package`)
+ ?: error("App window for package ${testApp.`package`} not found")
+ val rotationAnimation = appWindow.windowState.attributes.rotationAnimation
+ appWindow
+ .check { "isRotationSeamless" }
+ .that(
+ rotationAnimation.and(
+ WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
+ )
+ )
+ .isGreater(0)
}
}
}
- /**
- * Checks that [testApp] window is always visible
- */
+ /** Checks that [testApp] window is always visible */
@Presubmit
@Test
fun appLayerAlwaysVisible() {
- testSpec.assertLayers {
- isVisible(testApp.component)
- }
+ flicker.assertLayers { isVisible(testApp) }
}
- /**
- * Checks that [testApp] layer covers the entire screen during the whole transition
- */
+ /** Checks that [testApp] layer covers the entire screen during the whole transition */
@Presubmit
@Test
fun appLayerRotates() {
- testSpec.assertLayers {
+ flicker.assertLayers {
this.invoke("entireScreenCovered") { entry ->
entry.entry.displays.map { display ->
- entry.visibleRegion(testApp.component).coversExactly(display.layerStackSpace)
+ entry.visibleRegion(testApp).coversExactly(display.layerStackSpace)
}
}
}
}
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {}
+
+ /** {@inheritDoc} */
+ @Test
+ @Ignore("Not applicable to this CUJ. App is full screen")
+ override fun statusBarWindowIsAlwaysVisible() {}
+
/**
- * Checks that the [FlickerComponentName.STATUS_BAR] window is invisible during the whole
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is invisible during the whole
* transition
*/
@Presubmit
@Test
fun statusBarWindowIsAlwaysInvisible() {
- testSpec.assertWm {
- this.isAboveAppWindowInvisible(FlickerComponentName.STATUS_BAR)
- }
+ flicker.assertWm { this.isAboveAppWindowInvisible(ComponentNameMatcher.STATUS_BAR) }
}
/**
- * Checks that the [FlickerComponentName.STATUS_BAR] layer is invisible during the whole
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is invisible during the whole
* transition
*/
@Presubmit
@Test
fun statusBarLayerIsAlwaysInvisible() {
- testSpec.assertLayers {
- this.isInvisible(FlickerComponentName.STATUS_BAR)
- }
+ flicker.assertLayers { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
}
- /**
- * Checks that the focus doesn't change during animation
- */
+ /** Checks that the focus doesn't change during animation */
@Presubmit
@Test
fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
+ flicker.assertEventLog { this.focusDoesNotChange() }
}
- /** {@inheritDoc} */
- @FlakyTest
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ @PlatinumTest(focusArea = "framework")
+ override fun cujCompleted() {
+ appWindowFullScreen()
+ appWindowSeamlessRotation()
+ focusDoesNotChange()
+ statusBarLayerIsAlwaysInvisible()
+ statusBarWindowIsAlwaysInvisible()
+ appLayerRotates_StartingPos()
+ appLayerRotates_EndingPos()
+ entireScreenCovered()
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ runAndIgnoreAssumptionViolation { appLayerRotates() }
+ runAndIgnoreAssumptionViolation { appLayerAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { navBarWindowIsAlwaysVisible() }
+ runAndIgnoreAssumptionViolation { navBarLayerPositionAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarLayerIsVisibleAtStartAndEnd() }
+ runAndIgnoreAssumptionViolation { taskBarWindowIsAlwaysVisible() }
+ }
companion object {
- private val FlickerTestParameter.starveUiThread
- get() = config.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
+ private val FlickerTest.starveUiThread
+ get() =
+ getConfigValue<Boolean>(ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD)
+ ?: false
- private fun createConfig(
- sourceConfig: FlickerTestParameter,
+ @JvmStatic
+ protected fun createConfig(
+ sourceConfig: FlickerTest,
starveUiThread: Boolean
- ): FlickerTestParameter {
- val newConfig = sourceConfig.config.toMutableMap()
- .also { it[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread }
+ ): FlickerTest {
+ val originalScenario = sourceConfig.initialize("createConfig")
val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
- return FlickerTestParameter(newConfig, nameOverride = "$sourceConfig$nameExt")
- }
-
- /**
- * Creates the test configurations for seamless rotation based on the default rotation
- * tests from [FlickerTestParameterFactory.getConfigRotationTests], but adding an
- * additional flag ([ActivityOptions.EXTRA_STARVE_UI_THREAD]) to indicate if the app
- * should starve the UI thread of not
- */
- @JvmStatic
- private fun getConfigurations(): List<FlickerTestParameter> {
- return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 2)
- .flatMap { sourceConfig ->
- val defaultRun = createConfig(sourceConfig, starveUiThread = false)
- val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
- listOf(defaultRun, busyUiRun)
- }
+ val newConfig =
+ ScenarioBuilder()
+ .fromScenario(originalScenario)
+ .withExtraConfig(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD,
+ starveUiThread
+ )
+ .withDescriptionOverride("${originalScenario.description}$nameExt")
+ return FlickerTest(newConfig)
}
/**
- * Creates the test configurations.
- *
- * See [FlickerTestParameterFactory.getConfigRotationTests] for configuring
- * repetitions, screen orientation and navigation modes.
+ * Creates the test configurations for seamless rotation based on the default rotation tests
+ * from [FlickerTestFactory.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(): Collection<FlickerTestParameter> {
- return getConfigurations()
+ fun getParams(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests().flatMap { sourceConfig ->
+ val defaultRun = createConfig(sourceConfig, starveUiThread = false)
+ val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
+ }
}
}
}
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
new file mode 100644
index 000000000000..b236d87616ab
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+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: FlickerTest) : SeamlessAppRotationTest(flicker) {
+ companion object {
+ /**
+ * Creates the test configurations for seamless rotation based on the default rotation tests
+ * from [FlickerTestFactory.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(): Collection<FlickerTest> {
+ return FlickerTestFactory.rotationTests().flatMap { sourceConfig ->
+ val defaultRun = createConfig(sourceConfig, starveUiThread = false)
+ val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
+ }
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/Android.bp b/tests/FlickerTests/test-apps/Android.bp
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/tests/FlickerTests/test-apps/Android.bp
+++ /dev/null
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index 78660c04d8d4..75e35ee9c765 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -26,6 +26,24 @@ android_test {
srcs: ["**/*.java"],
sdk_version: "current",
test_suites: ["device-tests"],
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.appcompat_appcompat",
+ "androidx-constraintlayout_constraintlayout",
+ "androidx.core_core",
+ "androidx.fragment_fragment",
+ "androidx.recyclerview_recyclerview",
+ "androidx.test.ext.junit",
+ "androidx.navigation_navigation-common-ktx",
+ "androidx.navigation_navigation-fragment-ktx",
+ "androidx.navigation_navigation-runtime-ktx",
+ "androidx.navigation_navigation-ui-ktx",
+ "kotlin-stdlib",
+ "kotlinx-coroutines-android",
+ "wm-flicker-common-app-helpers",
+ "wm-flicker-common-assertions",
+ "wm-flicker-window-extensions",
+ ],
}
java_library {
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 43aa4b151548..1ec9ec9b0eda 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -15,38 +15,41 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.testapp">
+ package="com.android.server.wm.flicker.testapp">
<uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
+ android:targetSdkVersion="29"/>
<application android:allowBackup="false"
- android:supportsRtl="true">
+ android:supportsRtl="true">
+ <uses-library android:name="androidx.window.extensions" android:required="false"/>
+
<activity android:name=".SimpleActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SimpleApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SimpleActivity"
+ 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=".ImeActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="ImeActivity"
+ 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=".ImeActivityAutoFocus"
- android:theme="@style/CutoutShortEdges"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
- android:windowSoftInputMode="stateVisible"
- android:label="ImeAppAutoFocus"
- android:exported="true">
+ android:theme="@style/CutoutShortEdges"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
+ android:windowSoftInputMode="stateVisible"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:label="ImeAppAutoFocus"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -63,34 +66,46 @@
</intent-filter>
</activity>
<activity android:name=".SeamlessRotationActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="SeamlessApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="SeamlessActivity"
+ 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=".NonResizeableActivity"
- android:theme="@style/CutoutShortEdges"
- android:resizeableActivity="false"
- android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
- android:label="NonResizeableApp"
- android:exported="true"
- android:showOnLockScreen="true">
+ android:theme="@style/CutoutShortEdges"
+ android:resizeableActivity="false"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
+ android:label="NonResizeableActivity"
+ android:exported="true"
+ android:showOnLockScreen="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
- <activity android:name=".ButtonActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ButtonActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="ButtonActivity"
- android:exported="true">
+ <activity android:name=".NonResizeablePortraitActivity"
+ android:theme="@style/CutoutNever"
+ android:resizeableActivity="false"
+ android:screenOrientation="portrait"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeablePortraitActivity"
+ android:label="NonResizeablePortraitActivity"
+ 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=".LaunchNewActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="LaunchNewActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -108,59 +123,215 @@
</intent-filter>
</activity>
<activity android:name=".DialogThemedActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
- android:configChanges="orientation|screenSize"
- android:theme="@style/DialogTheme"
- android:label="DialogThemedActivity"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/DialogTheme"
+ android:label="DialogThemedActivity"
+ 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=".PortraitOnlyActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
- android:theme="@style/CutoutShortEdges"
- android:screenOrientation="portrait"
- android:configChanges="orientation|screenSize"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:screenOrientation="portrait"
+ android:configChanges="orientation|screenSize"
+ 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=".ImeEditorPopupDialogActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
- android:configChanges="orientation|screenSize"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeEditorPopupDialogActivity"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/CutoutShortEdges"
+ android:label="ImeEditorPopupDialogActivity"
+ 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=".ShowWhenLockedActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="ShowWhenLockedActivity"
- android:showWhenLocked="true"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="ShowWhenLockedActivity"
+ android:showWhenLocked="true"
+ 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=".NotificationActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="NotificationActivity"
+ 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=".ActivityEmbeddingMainActivity"
+ android:label="ActivityEmbedding Main"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
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=".ActivityEmbeddingSecondaryActivity"
+ android:label="ActivityEmbedding Secondary"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="false"/>
+ <activity
+ android:name=".ActivityEmbeddingPlaceholderPrimaryActivity"
+ android:label="ActivityEmbedding Placeholder Primary"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="false">
+ </activity>
+ <activity
+ android:name=".ActivityEmbeddingPlaceholderSecondaryActivity"
+ android:label="ActivityEmbedding Placeholder Secondary"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="false"/>
+ <activity android:name=".MailActivity"
+ android:exported="true"
+ android:label="MailActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
+ android:theme="@style/Theme.AppCompat.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".GameActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
+ android:immersive="true"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="GameActivity"
+ 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=".PipActivity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:launchMode="singleTop"
+ android:label="PipActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".SplitScreenActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SplitScreenPrimaryActivity"
+ 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=".SplitScreenSecondaryActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SplitScreenSecondaryActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <meta-data android:name="android.app.shortcuts"
+ android:resource="@xml/shortcuts" />
+ </activity>
+ <activity android:name=".SendNotificationActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SendNotificationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SendNotificationActivity"
+ 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=".LaunchBubbleActivity"
+ android:label="LaunchBubbleActivity"
+ android:exported="true"
+ android:theme="@style/CutoutShortEdges"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".BubbleActivity"
+ android:label="BubbleActivity"
+ android:exported="false"
+ android:theme="@style/CutoutShortEdges"
+ android:resizeableActivity="true"/>
+ <service
+ android:name=".AssistantInteractionSessionService"
+ android:exported="true"
+ android:permission="android.permission.BIND_VOICE_INTERACTION"/>
+ <service
+ android:name=".AssistantRecognitionService"
+ android:exported="true"
+ android:label="Test Voice Interaction Service">
+ <intent-filter>
+ <action android:name="android.speech.RecognitionService"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.speech"
+ android:resource="@xml/recognition_service"/>
+ </service>
+ <service
+ android:name=".AssistantInteractionService"
+ android:exported="true"
+ android:label="Test Voice Interaction Service"
+ android:permission="android.permission.BIND_VOICE_INTERACTION">
+ <intent-filter>
+ <action android:name="android.service.voice.VoiceInteractionService"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.voice_interaction"
+ android:resource="@xml/interaction_service"/>
+ </service>
</application>
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml b/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml
new file mode 100644
index 000000000000..c0165e5c1086
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/anim/slide_in_from_bottom.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/linear_interpolator"
+ android:shareInterpolator="false">
+ <translate
+ android:duration="500"
+ android:fromYDelta="100%p"
+ android:toYDelta="0%p" />
+</set>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png b/tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png
new file mode 100644
index 000000000000..d424a17b4157
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png
Binary files differ
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml
new file mode 100644
index 000000000000..4ea156de9f40
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M7.2,14.4m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.8,18m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.2,8.8m-4.8,0a4.8,4.8 0,1 1,9.6 0a4.8,4.8 0,1 1,-9.6 0"/>
+</vector>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml
new file mode 100644
index 000000000000..45ed98c6e503
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M12,4c-4.97,0 -9,3.58 -9,8c0,1.53 0.49,2.97 1.33,4.18c0.12,0.18 0.2,0.46 0.1,0.66c-0.33,0.68 -0.79,1.52 -1.38,2.39c-0.12,0.17 0.01,0.41 0.21,0.39c0.63,-0.05 1.86,-0.26 3.38,-0.91c0.17,-0.07 0.36,-0.06 0.52,0.03C8.55,19.54 10.21,20 12,20c4.97,0 9,-3.58 9,-8S16.97,4 12,4zM16.94,11.63l-3.29,3.29c-0.13,0.13 -0.34,0.04 -0.34,-0.14v-1.57c0,-0.11 -0.1,-0.21 -0.21,-0.2c-2.19,0.06 -3.65,0.65 -5.14,1.95c-0.15,0.13 -0.38,0 -0.33,-0.19c0.7,-2.57 2.9,-4.57 5.5,-4.75c0.1,-0.01 0.18,-0.09 0.18,-0.19V8.2c0,-0.18 0.22,-0.27 0.34,-0.14l3.29,3.29C17.02,11.43 17.02,11.55 16.94,11.63z"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
new file mode 100644
index 000000000000..7c7b2cacaefb
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
@@ -0,0 +1,48 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <Button
+ android:id="@+id/button_finish"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="8dp"
+ android:text="Finish" />
+ <Button
+ android:id="@+id/button_new_task"
+ android:layout_width="wrap_content"
+ android:layout_height="46dp"
+ android:layout_marginStart="8dp"
+ android:text="New Task" />
+ <Button
+ android:id="@+id/button_new_bubble"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:text="New Bubble" />
+
+ <Button
+ android:id="@+id/button_activity_for_result"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_marginStart="8dp"
+ android:text="Activity For Result" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml
new file mode 100644
index 000000000000..3a02cadc90dd
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/root_activity_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
new file mode 100644
index 000000000000..d78b9a836a37
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_orange_light">
+
+ <Button
+ android:id="@+id/launch_secondary_activity_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:onClick="launchSecondaryActivity"
+ android:text="Launch Secondary Activity" />
+
+ <Button
+ android:id="@+id/launch_placeholder_split_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:onClick="launchPlaceholderSplit"
+ android:text="Launch Placeholder Split" />
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
index baaf7073b3a6..fa73e2c63780 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -26,14 +26,27 @@
android:layout_width="match_parent"
android:imeOptions="flagNoExtractUi"
android:inputType="text"/>
- <Button
- android:id="@+id/finish_activity_btn"
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Finish activity" />
- <Button
- android:id="@+id/start_dialog_themed_activity_btn"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Start dialog themed activity" />
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/finish_activity_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Finish activity" />
+ <Button
+ android:id="@+id/start_dialog_themed_activity_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Dialog themed activity" />
+ <ToggleButton
+ android:id="@+id/toggle_fixed_portrait_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="Portrait (On)"
+ android:textOff="Portrait (Off)"
+ />
+ </LinearLayout>
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
index fe7bced690f9..fe7bced690f9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
new file mode 100644
index 000000000000..8a97433ede04
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_mail.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment
+ android:id="@+id/main_fragment"
+ android:name="androidx.navigation.fragment.NavHostFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:defaultNavHost="true"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:navGraph="@navigation/mail_navigation"></fragment>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml
new file mode 100644
index 000000000000..553c7fe08096
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml
@@ -0,0 +1,48 @@
+<?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.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/black">
+
+ <Button
+ android:id="@+id/button_create"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:text="Add Bubble" />
+
+ <Button
+ android:id="@+id/button_cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button_create"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:text="Cancel Bubble" />
+
+ <Button
+ android:id="@+id/button_cancel_all"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button_cancel"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:text="Cancel All Bubble" />
+</RelativeLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
new file mode 100644
index 000000000000..f7ba45b25d48
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -0,0 +1,141 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_blue_bright">
+
+ <!-- All the buttons (and other clickable elements) should be arranged in a way so that it is
+ possible to "cycle" over all them by clicking on the D-Pad DOWN button. The way we do it
+ here is by arranging them this vertical LL and by relying on the nextFocusDown attribute
+ where things are arranged differently and to circle back up to the top once we reach the
+ bottom. -->
+
+ <Button
+ android:id="@+id/enter_pip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Enter PIP"
+ android:onClick="enterPip"/>
+
+ <CheckBox
+ android:id="@+id/with_custom_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="With custom actions"/>
+
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:checkedButton="@id/enter_pip_on_leave_disabled">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Enter PiP on home press"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_disabled"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Disabled"
+ android:onClick="onAutoPipSelected"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_manual"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Via code behind"
+ android:onClick="onAutoPipSelected"/>
+
+ <RadioButton
+ android:id="@+id/enter_pip_on_leave_autoenter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Auto-enter PiP"
+ android:onClick="onAutoPipSelected"/>
+ </RadioGroup>
+
+ <RadioGroup
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:checkedButton="@id/ratio_default">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Ratio"/>
+
+ <RadioButton
+ android:id="@+id/ratio_default"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Default"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_square"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Square [1:1]"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_wide"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Wide [2:1]"
+ android:onClick="onRatioSelected"/>
+
+ <RadioButton
+ android:id="@+id/ratio_tall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Tall [1:2]"
+ android:onClick="onRatioSelected"/>
+ </RadioGroup>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Media Session"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/media_session_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:nextFocusDown="@id/media_session_stop"
+ android:text="Start"/>
+
+ <Button
+ android:id="@+id/media_session_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:nextFocusDown="@id/enter_pip"
+ android:text="Stop"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
new file mode 100644
index 000000000000..79e88e49b99e
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_green_light">
+
+ <TextView
+ android:id="@+id/SplitScreenTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:textIsSelectable="true"
+ android:text="PrimaryActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
new file mode 100644
index 000000000000..ed9feaf1eade
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
@@ -0,0 +1,32 @@
+<?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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/holo_blue_light">
+
+ <TextView
+ android:id="@+id/SplitScreenTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="SecondaryActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
new file mode 100644
index 000000000000..0b4693dec6e1
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_surfaceview.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml
new file mode 100644
index 000000000000..eb7f3074ebfb
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/vis_frame"
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="bottom"
+ android:background="#37474F"/>
+</FrameLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml
new file mode 100644
index 000000000000..fbbdc5b812ad
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_content.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml
new file mode 100644
index 000000000000..0e30745bdfc9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/fragment_mail_list.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/mail_recycle_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml
new file mode 100644
index 000000000000..c39bfb35e9b0
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/mail_row_item.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/list_item_height"
+ android:layout_marginLeft="@dimen/margin_medium"
+ android:layout_marginRight="@dimen/margin_medium">
+ <ImageView
+ android:id="@+id/mail_row_item_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/padding_small"
+ android:paddingEnd="@dimen/padding_small"
+ android:paddingStart="@dimen/padding_small"
+ android:paddingTop="@dimen/padding_small"/>
+ <TextView
+ android:id="@+id/mail_row_item_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingEnd="@dimen/padding_small"
+ android:textSize="@dimen/list_item_text_size" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml b/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml
new file mode 100644
index 000000000000..d12707a7a123
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/navigation/mail_navigation.xml
@@ -0,0 +1,36 @@
+<?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.
+ -->
+
+<navigation xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/mail_navigation"
+ app:startDestination="@id/fragment_mail_list">
+ <fragment
+ android:id="@+id/fragment_mail_list"
+ android:name=".MailListFragment"
+ android:label="Mail List">
+ <action
+ android:id="@+id/action_mail_list_to_mail_content"
+ app:destination="@id/fragment_mail_content"
+ app:enterAnim="@anim/slide_in_from_bottom" />
+ </fragment>
+ <fragment
+ android:id="@+id/fragment_mail_content"
+ android:name=".MailContentFragment"
+ android:label="Mail Content">
+ </fragment>
+</navigation>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml
new file mode 100644
index 000000000000..0b0763d7071d
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/dimens.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="padding_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="list_item_height">72dp</dimen>
+ <dimen name="list_item_text_size">24dp</dimen>
+ <dimen name="icon_size">64dp</dimen>
+ <dimen name="icon_text_size">36dp</dimen>
+</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/strings.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/strings.xml
new file mode 100644
index 000000000000..24830deac99f
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/strings.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ 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.
+ -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for split screen shortcut-->
+ <string name="split_screen_shortcut_label">Split Screen Secondary Activity</string>
+</resources> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml b/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml
new file mode 100644
index 000000000000..2e661fbd3122
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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.
+ -->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:recognitionService="com.android.server.wm.flicker.testapp.AssistantRecognitionService"
+ android:sessionService="com.android.server.wm.flicker.testapp.AssistantInteractionSessionService"
+ android:supportsAssist="true" />
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml b/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml
new file mode 100644
index 000000000000..2e124982084f
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ 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.
+ -->
+
+<recognition-service xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/xml/shortcuts.xml b/tests/FlickerTests/test-apps/flickerapp/res/xml/shortcuts.xml
new file mode 100644
index 000000000000..804ec998477f
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/xml/shortcuts.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
+ <shortcut
+ android:shortcutId="split_screen_shortcut"
+ android:shortcutShortLabel="@string/split_screen_shortcut_label">
+ <intent
+ android:action="android.intent.action.VIEW"
+ android:targetPackage="com.android.server.wm.flicker.testapp"
+ android:targetClass="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity" />
+ </shortcut>
+</shortcuts> \ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java
new file mode 100644
index 000000000000..cd23e9f76ebf
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingBaseActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/** Base activity of ActivityEmbedding split activities. */
+public abstract class ActivityEmbeddingBaseActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_embedding_base_layout);
+ findViewById(R.id.root_activity_layout).setBackgroundColor(getBackgroundColor());
+ }
+
+ /** Sets different colors to visually distinguish split pairs. */
+ abstract int getBackgroundColor();
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
new file mode 100644
index 000000000000..6a7a2ccd3378
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
@@ -0,0 +1,102 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.View;
+
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitPlaceholderRule;
+
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper;
+
+import java.util.Set;
+
+/** Main activity of the ActivityEmbedding test app to launch other embedding activities. */
+public class ActivityEmbeddingMainActivity extends Activity {
+ private static final String TAG = "ActivityEmbeddingMainActivity";
+ private static final float DEFAULT_SPLIT_RATIO = 0.5f;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_embedding_main_layout);
+ }
+
+ /** R.id.launch_secondary_activity_button onClick */
+ public void launchSecondaryActivity(View view) {
+ initializeSplitRules(createSplitPairRules());
+ startActivity(new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT));
+ }
+
+ /** R.id.launch_placeholder_split_button onClick */
+ public void launchPlaceholderSplit(View view) {
+ initializeSplitRules(createSplitPlaceholderRules());
+ startActivity(new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT));
+ }
+
+ private void initializeSplitRules(Set<EmbeddingRule> rules) {
+ ActivityEmbeddingComponent embeddingComponent =
+ ActivityEmbeddingAppHelper.getActivityEmbeddingComponent();
+ if (embeddingComponent == null) {
+ // Embedding not supported
+ Log.d(TAG, "ActivityEmbedding is not supported on this device");
+ finish();
+ return;
+ }
+ embeddingComponent.setEmbeddingRules(rules);
+ }
+
+ private Set<EmbeddingRule> createSplitPairRules() {
+ final Set<EmbeddingRule> rules = new ArraySet<>();
+ final SplitPairRule rule = new SplitPairRule.Builder(
+ activitiesPair -> activitiesPair.first instanceof ActivityEmbeddingMainActivity
+ && activitiesPair.second instanceof ActivityEmbeddingSecondaryActivity,
+ activityIntentPair ->
+ activityIntentPair.first instanceof ActivityEmbeddingMainActivity
+ && activityIntentPair.second.getComponent().equals(ActivityOptions
+ .ActivityEmbedding.SecondaryActivity.COMPONENT),
+ windowMetrics -> true)
+ .setSplitRatio(DEFAULT_SPLIT_RATIO)
+ .build();
+ rules.add(rule);
+ return rules;
+ }
+
+ private Set<EmbeddingRule> createSplitPlaceholderRules() {
+ final Set<EmbeddingRule> rules = new ArraySet<>();
+ final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder(
+ new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT),
+ activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity,
+ intent -> intent.getComponent().equals(
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT),
+ windowMetrics -> true)
+ .setSplitRatio(DEFAULT_SPLIT_RATIO)
+ .build();
+ rules.add(rule);
+ return rules;
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java
new file mode 100644
index 000000000000..05c7a0b34c33
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderPrimaryActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.graphics.Color;
+
+/**
+ * Primary activity that will launch {@link ActivityEmbeddingPlaceholderSecondaryActivity} to split
+ * as placeholder based on the placeholder rule.
+ */
+public class ActivityEmbeddingPlaceholderPrimaryActivity extends ActivityEmbeddingBaseActivity {
+ @Override
+ int getBackgroundColor() {
+ return Color.BLUE;
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java
new file mode 100644
index 000000000000..a9a51cd9ad8b
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingPlaceholderSecondaryActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.graphics.Color;
+
+/**
+ * Activity to be used as the secondary placeholder activity to split with
+ * {@link ActivityEmbeddingPlaceholderPrimaryActivity}.
+ */
+public class ActivityEmbeddingPlaceholderSecondaryActivity extends ActivityEmbeddingBaseActivity {
+ @Override
+ int getBackgroundColor() {
+ return Color.GREEN;
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
new file mode 100644
index 000000000000..00f4c2576eb1
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.graphics.Color;
+
+/**
+ * Activity to be used as the secondary activity to split with
+ * {@link ActivityEmbeddingMainActivity}.
+ */
+public class ActivityEmbeddingSecondaryActivity extends ActivityEmbeddingBaseActivity {
+ @Override
+ int getBackgroundColor() {
+ return Color.YELLOW;
+ }
+}
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 6cda482dd30a..9c3226b5292c 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
@@ -19,72 +19,195 @@ package com.android.server.wm.flicker.testapp;
import android.content.ComponentName;
public class ActivityOptions {
- public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
public static final String FLICKER_APP_PACKAGE = "com.android.server.wm.flicker.testapp";
- public static final String SEAMLESS_ACTIVITY_LAUNCHER_NAME = "SeamlessApp";
- public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
+ public static class SimpleActivity {
+ public static final String LABEL = "SimpleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SimpleActivity");
+ }
- public static final String IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME = "ImeAppAutoFocus";
- public static final ComponentName IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
+ public static class SeamlessRotation {
+ public static final String LABEL = "SeamlessRotationActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
+
+ public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
+ }
- public static final String IME_ACTIVITY_LAUNCHER_NAME = "ImeActivity";
- public static final ComponentName IME_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class Ime {
+ public static class Default {
+ public static final String LABEL = "ImeActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivity");
+ }
+
+ public static class AutoFocusActivity {
+ public static final String LABEL = "ImeAppAutoFocus";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
+ }
- public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
- public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class StateInitializeActivity {
+ public static final String LABEL = "ImeStateInitializeActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeStateInitializeActivity");
+ }
- public static final String SIMPLE_ACTIVITY_LAUNCHER_NAME = "SimpleApp";
- public static final ComponentName SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".SimpleActivity");
-
- public static final String NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME = "NonResizeableApp";
- public static final ComponentName NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".NonResizeableActivity");
-
- public static final String BUTTON_ACTIVITY_LAUNCHER_NAME = "ButtonApp";
- public static final ComponentName BUTTON_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ButtonActivity");
-
- public static final String LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME = "LaunchNewTaskApp";
- public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
-
- public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
- public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".DialogThemedActivity");
-
- public static final String PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME = "PortraitOnlyActivity";
- public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
-
- public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME =
- "ImeEditorPopupDialogActivity";
- public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class EditorPopupDialogActivity {
+ public static final String LABEL = "ImeEditorPopupDialogActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity");
-
- public static final String SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME = "ShowWhenLockedApp";
- public static final ComponentName SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
-
- public static final String NOTIFICATION_ACTIVITY_LAUNCHER_NAME = "NotificationApp";
- public static final ComponentName NOTIFICATION_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".NotificationActivity");
+ }
+ }
+
+ public static class NonResizeableActivity {
+ public static final String LABEL = "NonResizeableActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NonResizeableActivity");
+ }
+
+ public static class NonResizeablePortraitActivity {
+ public static final String LABEL = "NonResizeablePortraitActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
+ }
+
+ public static class DialogThemedActivity {
+ public static final String LABEL = "DialogThemedActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".DialogThemedActivity");
+ }
+
+ public static class PortraitOnlyActivity {
+ public static final String LABEL = "PortraitOnlyActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
+ public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+ }
+
+ public static class ActivityEmbedding {
+ public static class MainActivity {
+ public static final String LABEL = "ActivityEmbeddingMainActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingMainActivity");
+ }
+
+ public static class SecondaryActivity {
+ public static final String LABEL = "ActivityEmbeddingSecondaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingSecondaryActivity");
+ }
+
+ public static class PlaceholderPrimaryActivity {
+ public static final String LABEL = "ActivityEmbeddingPlaceholderPrimaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity");
+ }
+
+ public static class PlaceholderSecondaryActivity {
+ public static final String LABEL = "ActivityEmbeddingPlaceholderSecondaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
+ }
+ }
+
+ public static class Notification {
+ public static final String LABEL = "NotificationActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NotificationActivity");
+ }
+
+ public static class Mail {
+ public static final String LABEL = "MailActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".MailActivity");
+ }
+
+ public static class ShowWhenLockedActivity {
+ public static final String LABEL = "ShowWhenLockedActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
+ }
+
+ public static class LaunchNewTask {
+ public static final String LABEL = "LaunchNewTaskActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+ }
+
+ public static class Game {
+ public static final String LABEL = "GameActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".GameActivity");
+ }
+
+ public static class LaunchNewActivity {
+ public static final String LABEL = "LaunchNewActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchNewActivity");
+ }
+
+ public static class Pip {
+ // Test App > Pip Activity
+ public static final String LABEL = "PipActivity";
+ public static final String MENU_ACTION_NO_OP = "No-Op";
+ public static final String MENU_ACTION_ON = "On";
+ public static final String MENU_ACTION_OFF = "Off";
+ public static final String MENU_ACTION_CLEAR = "Clear";
+
+ // Intent action that this activity dynamically registers to enter picture-in-picture
+ public static final String ACTION_ENTER_PIP =
+ FLICKER_APP_PACKAGE + ".PipActivity.ENTER_PIP";
+ // Intent action that this activity dynamically registers to set requested orientation.
+ // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
+ public static final String ACTION_SET_REQUESTED_ORIENTATION =
+ FLICKER_APP_PACKAGE + ".PipActivity.SET_REQUESTED_ORIENTATION";
+
+ // Calls enterPictureInPicture() on creation
+ public static final String EXTRA_ENTER_PIP = "enter_pip";
+ // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
+ public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
+ // Adds a click listener to finish this activity when it is clicked
+ public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PipActivity");
+ }
+
+ public static class SplitScreen {
+ public static class Primary {
+ public static final String LABEL = "SplitScreenPrimaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SplitScreenActivity");
+ }
+
+ public static class Secondary {
+ public static final String LABEL = "SplitScreenSecondaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SplitScreenSecondaryActivity");
+ }
+ }
+
+ public static class Bubbles {
+ public static class LaunchBubble {
+ public static final String LABEL = "LaunchBubbleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchBubbleActivity");
+ }
+
+ public static class BubbleActivity {
+ public static final String LABEL = "BubbleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".BubbleActivity");
+ }
+ }
+
+ public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
+ public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
+ new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".GameActivity");
+
+ public static final ComponentName ASSISTANT_SERVICE_COMPONENT_NAME =
+ new ComponentName(
+ FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".AssistantInteractionService");
}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target00.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java
index 8fb4e91f790c..d60143cdf40a 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/b/Target00.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.componentalias.tests.b;
-import android.content.componentalias.tests.s.BaseService;
+package com.android.server.wm.flicker.testapp;
-public class Target00 extends BaseReceiver {
+import android.service.voice.VoiceInteractionService;
+
+public class AssistantInteractionService extends VoiceInteractionService {
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java
new file mode 100644
index 000000000000..d2c9b37704b8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java
@@ -0,0 +1,53 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.view.View;
+import android.view.Window;
+
+public class AssistantInteractionSession extends VoiceInteractionSession {
+
+ public AssistantInteractionSession(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onCreate() {
+ Window window = getWindow().getWindow();
+ if (window != null) {
+ window.getDecorView().setBackgroundColor(Color.TRANSPARENT);
+
+ }
+ View rootView = getLayoutInflater().inflate(R.layout.assistant_session, null);
+ setContentView(rootView);
+ setUiEnabled(false);
+ }
+
+ @Override
+ public void onShow(Bundle args, int showFlags) {
+ setUiEnabled(true);
+ }
+
+ @Override
+ public void onHide() {
+ setUiEnabled(false);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java
new file mode 100644
index 000000000000..4d6125c9a5d8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java
@@ -0,0 +1,28 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class AssistantInteractionSessionService extends VoiceInteractionSessionService {
+ @Override
+ public VoiceInteractionSession onNewSession(Bundle args) {
+ return new AssistantInteractionSession(this);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java
new file mode 100644
index 000000000000..68aae4520fe9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java
@@ -0,0 +1,37 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.content.Intent;
+import android.speech.RecognitionService;
+
+public class AssistantRecognitionService extends RecognitionService {
+ @Override
+ protected void onStartListening(Intent recognizerIntent, Callback listener) {
+
+ }
+
+ @Override
+ protected void onCancel(Callback listener) {
+
+ }
+
+ @Override
+ protected void onStopListening(Callback listener) {
+
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java
new file mode 100644
index 000000000000..58d7e670e908
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java
@@ -0,0 +1,77 @@
+/*
+ * 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.wm.flicker.testapp;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+public class BubbleActivity extends Activity {
+ private int mNotifId = 0;
+
+ public BubbleActivity() {
+ super();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ mNotifId = intent.getIntExtra(BubbleHelper.EXTRA_BUBBLE_NOTIF_ID, -1);
+ } else {
+ mNotifId = -1;
+ }
+
+ setContentView(R.layout.activity_bubble);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ String result = resultCode == Activity.RESULT_OK ? "OK" : "CANCELLED";
+ Toast.makeText(this, "Activity result: " + result, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
new file mode 100644
index 000000000000..c92b82b896f2
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -0,0 +1,173 @@
+/*
+ * 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.wm.flicker.testapp;
+
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.drawable.Icon;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.view.WindowManager;
+
+import java.util.HashMap;
+
+public class BubbleHelper {
+
+ static final String EXTRA_BUBBLE_NOTIF_ID = "EXTRA_BUBBLE_NOTIF_ID";
+ static final String CHANNEL_ID = "bubbles";
+ static final String CHANNEL_NAME = "Bubbles";
+ static final int DEFAULT_HEIGHT_DP = 300;
+
+ private static BubbleHelper sInstance;
+
+ private final Context mContext;
+ private NotificationManager mNotificationManager;
+ private float mDisplayHeight;
+
+ private HashMap<Integer, BubbleInfo> mBubbleMap = new HashMap<>();
+
+ private int mNextNotifyId = 0;
+ private int mColourIndex = 0;
+
+ public static class BubbleInfo {
+ public int id;
+ public int height;
+ public Icon icon;
+
+ public BubbleInfo(int id, int height, Icon icon) {
+ this.id = id;
+ this.height = height;
+ this.icon = icon;
+ }
+ }
+
+ public static BubbleHelper getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new BubbleHelper(context);
+ }
+ return sInstance;
+ }
+
+ private BubbleHelper(Context context) {
+ mContext = context;
+ mNotificationManager = context.getSystemService(NotificationManager.class);
+
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setDescription("Channel that posts bubbles");
+ channel.setAllowBubbles(true);
+ mNotificationManager.createNotificationChannel(channel);
+
+ Point p = new Point();
+ WindowManager wm = context.getSystemService(WindowManager.class);
+ wm.getDefaultDisplay().getRealSize(p);
+ mDisplayHeight = p.y;
+
+ }
+
+ private int getNextNotifyId() {
+ int id = mNextNotifyId;
+ mNextNotifyId++;
+ return id;
+ }
+
+ private Icon getIcon() {
+ return Icon.createWithResource(mContext, R.drawable.bg);
+ }
+
+ public int addNewBubble(boolean autoExpand, boolean suppressNotif) {
+ int id = getNextNotifyId();
+ BubbleInfo info = new BubbleInfo(id, DEFAULT_HEIGHT_DP, getIcon());
+ mBubbleMap.put(info.id, info);
+
+ Notification.BubbleMetadata data = getBubbleBuilder(info)
+ .setSuppressNotification(suppressNotif)
+ .setAutoExpandBubble(false)
+ .build();
+ Notification notification = getNotificationBuilder(info.id)
+ .setBubbleMetadata(data).build();
+
+ mNotificationManager.notify(info.id, notification);
+ return info.id;
+ }
+
+ private Notification.Builder getNotificationBuilder(int id) {
+ Person chatBot = new Person.Builder()
+ .setBot(true)
+ .setName("BubbleChat")
+ .setImportant(true)
+ .build();
+ String shortcutId = "BubbleChat";
+ return new Notification.Builder(mContext, CHANNEL_ID)
+ .setChannelId(CHANNEL_ID)
+ .setShortcutId(shortcutId)
+ .setContentTitle("BubbleChat")
+ .setContentIntent(PendingIntent.getActivity(mContext, 0,
+ new Intent(mContext, LaunchBubbleActivity.class),
+ PendingIntent.FLAG_UPDATE_CURRENT))
+ .setStyle(new Notification.MessagingStyle(chatBot)
+ .setConversationTitle("BubbleChat")
+ .addMessage("BubbleChat",
+ SystemClock.currentThreadTimeMillis() - 300000, chatBot)
+ .addMessage("Is it me, " + id + ", you're looking for?",
+ SystemClock.currentThreadTimeMillis(), chatBot)
+ )
+ .setSmallIcon(R.drawable.ic_bubble);
+ }
+
+ private Notification.BubbleMetadata.Builder getBubbleBuilder(BubbleInfo info) {
+ Intent target = new Intent(mContext, BubbleActivity.class);
+ target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
+ PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return new Notification.BubbleMetadata.Builder()
+ .setIntent(bubbleIntent)
+ .setIcon(info.icon)
+ .setDesiredHeight(info.height);
+ }
+
+ public void cancel(int id) {
+ mNotificationManager.cancel(id);
+ }
+
+ public void cancelAll() {
+ mNotificationManager.cancelAll();
+ }
+
+ public void cancelLast() {
+ StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
+ if (activeNotifications.length > 0) {
+ mNotificationManager.cancel(
+ activeNotifications[activeNotifications.length - 1].getId());
+ }
+ }
+
+ public void cancelFirst() {
+ StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
+ if (activeNotifications.length > 0) {
+ mNotificationManager.cancel(activeNotifications[0].getId());
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java
new file mode 100644
index 000000000000..722929fb81e5
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION;
+
+import android.os.Bundle;
+
+public class FixedActivity extends SimpleActivity {
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Set the fixed orientation if requested
+ if (getIntent().hasExtra(EXTRA_FIXED_ORIENTATION)) {
+ final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_FIXED_ORIENTATION));
+ setRequestedOrientation(ori);
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
new file mode 100644
index 000000000000..ef75d4ddcdcd
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/GameActivity.java
@@ -0,0 +1,77 @@
+/*
+ * 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.testapp;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowInsetsCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+public class GameActivity extends Activity implements SurfaceHolder.Callback {
+ private SurfaceHolder mSurfaceHolder;
+ private SurfaceView mSurfaceView;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_surfaceview);
+
+ mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
+ mSurfaceView.setZOrderOnTop(true);
+ mSurfaceHolder = mSurfaceView.getHolder();
+ mSurfaceHolder.setFormat(PixelFormat.TRANSPARENT);
+ mSurfaceHolder.addCallback(this);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ hideSystemBars();
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.BLUE);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ private void hideSystemBars() {
+ WindowInsetsControllerCompat windowInsetsController =
+ ViewCompat.getWindowInsetsController(getWindow().getDecorView());
+ if (windowInsetsController == null) {
+ return;
+ }
+ // Configure the behavior of the hidden system bars.
+ windowInsetsController.setSystemBarsBehavior(
+ WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ );
+ // Hide both the status bar and the navigation bar.
+ windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
index bb200f125507..7ee8debddcf1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java
@@ -16,21 +16,29 @@
package com.android.server.wm.flicker.testapp;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
import android.content.Intent;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ToggleButton;
public class ImeActivityAutoFocus extends ImeActivity {
-
@Override
protected void onStart() {
super.onStart();
- EditText editTextField = findViewById(R.id.plain_text_input);
- editTextField.requestFocus();
-
Button startThemedActivityButton = findViewById(R.id.start_dialog_themed_activity_btn);
startThemedActivityButton.setOnClickListener(
button -> startActivity(new Intent(this, DialogThemedActivity.class)));
+
+ ToggleButton toggleFixedPortraitButton = findViewById(R.id.toggle_fixed_portrait_btn);
+ toggleFixedPortraitButton.setOnCheckedChangeListener(
+ (button, isChecked) -> setRequestedOrientation(
+ isChecked ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_UNSPECIFIED));
+
+ EditText editTextField = findViewById(R.id.plain_text_input);
+ editTextField.requestFocus();
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
index a8613f531e1c..95f933f97bb2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
@@ -16,6 +16,8 @@
package com.android.server.wm.flicker.testapp;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
@@ -30,6 +32,7 @@ public class ImeEditorPopupDialogActivity extends Activity {
WindowManager.LayoutParams p = getWindow().getAttributes();
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ p.softInputMode = SOFT_INPUT_STATE_ALWAYS_HIDDEN;
getWindow().setAttributes(p);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
new file mode 100644
index 000000000000..dea34442464d
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.wm.flicker.testapp;
+
+
+import android.app.Activity;
+import android.app.Person;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.view.View;
+
+import java.util.Arrays;
+
+public class LaunchBubbleActivity extends Activity {
+
+ private BubbleHelper mBubbleHelper;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addInboxShortcut(getApplicationContext());
+ mBubbleHelper = BubbleHelper.getInstance(this);
+ setContentView(R.layout.activity_main);
+ findViewById(R.id.button_create).setOnClickListener(this::add);
+ findViewById(R.id.button_cancel).setOnClickListener(this::cancel);
+ findViewById(R.id.button_cancel_all).setOnClickListener(this::cancelAll);
+ }
+
+ private void add(View v) {
+ mBubbleHelper.addNewBubble(false /* autoExpand */, false /* suppressNotif */);
+ }
+
+ private void cancel(View v) {
+ mBubbleHelper.cancelLast();
+ }
+
+ private void cancelAll(View v) {
+ mBubbleHelper.cancelAll();
+ }
+
+ private void addInboxShortcut(Context context) {
+ Icon icon = Icon.createWithResource(this, R.drawable.bg);
+ Person[] persons = new Person[4];
+ for (int i = 0; i < persons.length; i++) {
+ persons[i] = new Person.Builder()
+ .setBot(false)
+ .setIcon(icon)
+ .setName("google" + i)
+ .setImportant(true)
+ .build();
+ }
+
+ ShortcutInfo shortcut = new ShortcutInfo.Builder(context, "BubbleChat")
+ .setShortLabel("BubbleChat")
+ .setLongLived(true)
+ .setIntent(new Intent(Intent.ACTION_VIEW))
+ .setIcon(Icon.createWithResource(context, R.drawable.ic_message))
+ .setPersons(persons)
+ .build();
+ ShortcutManager scmanager = context.getSystemService(ShortcutManager.class);
+ scmanager.addDynamicShortcuts(Arrays.asList(shortcut));
+ }
+
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java
index b42ac2a6fd97..e5710c850f07 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java
@@ -22,7 +22,7 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
-public class ButtonActivity extends Activity {
+public class LaunchNewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -30,11 +30,11 @@ public class ButtonActivity extends Activity {
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(p);
- setContentView(R.layout.activity_button);
+ setContentView(R.layout.activity_launch_new);
Button button = findViewById(R.id.launch_second_activity);
button.setOnClickListener(v -> {
- Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class);
+ Intent intent = new Intent(LaunchNewActivity.this, SimpleActivity.class);
startActivity(intent);
});
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java
new file mode 100644
index 000000000000..419d438baf69
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailActivity.java
@@ -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.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+public class MailActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_mail);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java
new file mode 100644
index 000000000000..984263acebac
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailAdapter.java
@@ -0,0 +1,134 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import static androidx.navigation.fragment.NavHostFragment.findNavController;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.graphics.drawable.shapes.Shape;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.navigation.NavController;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class MailAdapter extends RecyclerView.Adapter<MailAdapter.ViewHolder> {
+
+ private final Fragment mFragment;
+ private final int mSize;
+
+ static class BadgeShape extends OvalShape {
+ Context mContext;
+ String mLabel;
+
+ BadgeShape(Context context, String label) {
+ mContext = context;
+ mLabel = label;
+ }
+
+ @Override
+ public void draw(Canvas canvas, Paint paint) {
+ final Resources resources = mContext.getResources();
+ int textSize = resources.getDimensionPixelSize(R.dimen.icon_text_size);
+ paint.setColor(Color.BLACK);
+ super.draw(canvas, paint);
+ paint.setColor(Color.WHITE);
+ paint.setTextSize(textSize);
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
+ paint.setTextAlign(Paint.Align.CENTER);
+ Paint.FontMetrics fontMetrics = paint.getFontMetrics();
+ float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
+ canvas.drawText(
+ mLabel,
+ rect().centerX(),
+ rect().centerY() + distance,
+ paint);
+ }
+ }
+
+ static class ViewHolder extends RecyclerView.ViewHolder {
+
+ NavController mNavController;
+ ImageView mImageView;
+ TextView mTextView;
+ DisplayMetrics mDisplayMetrics;
+
+ ViewHolder(@NonNull View itemView, NavController navController) {
+ super(itemView);
+ mNavController = navController;
+ itemView.setOnClickListener(this::onClick);
+ mImageView = itemView.findViewById(R.id.mail_row_item_icon);
+ mTextView = itemView.findViewById(R.id.mail_row_item_text);
+ mDisplayMetrics = itemView.getContext().getResources().getDisplayMetrics();
+ }
+
+ void onClick(View v) {
+ mNavController.navigate(R.id.action_mail_list_to_mail_content);
+ }
+
+ public void setContent(int i) {
+ final Resources resources = mImageView.getContext().getResources();
+ final int badgeSize = resources.getDimensionPixelSize(R.dimen.icon_size);
+ final char c = (char) ('A' + i % 26);
+ final Shape badge = new BadgeShape(mImageView.getContext(), String.valueOf(c));
+ ShapeDrawable drawable = new ShapeDrawable();
+ drawable.setIntrinsicHeight(badgeSize);
+ drawable.setIntrinsicWidth(badgeSize);
+ drawable.setShape(badge);
+ mImageView.setImageDrawable(drawable);
+ mTextView.setText(String.format("%s-%04d", c, i));
+ }
+ }
+
+ public MailAdapter(Fragment fragment, int size) {
+ super();
+ mFragment = fragment;
+ mSize = size;
+ }
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
+ View v = LayoutInflater
+ .from(viewGroup.getContext())
+ .inflate(R.layout.mail_row_item, viewGroup, false);
+ return new ViewHolder(v, findNavController(mFragment));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
+ viewHolder.setContent(i);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSize;
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java
new file mode 100644
index 000000000000..278b92e4b353
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailContentFragment.java
@@ -0,0 +1,36 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+public class MailContentFragment extends Fragment {
+ public MailContentFragment() {
+ super(R.layout.fragment_mail_content);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ view.setBackgroundColor(Color.LTGRAY);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java
new file mode 100644
index 000000000000..551820c3322b
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/MailListFragment.java
@@ -0,0 +1,49 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class MailListFragment extends Fragment {
+
+ protected RecyclerView mRecyclerView;
+ protected RecyclerView.LayoutManager mLayoutManager;
+ protected MailAdapter mAdapter;
+
+ public MailListFragment() {
+ super(R.layout.fragment_mail_list);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View rootView = inflater.inflate(R.layout.fragment_mail_list, container, false);
+ mRecyclerView = rootView.findViewById(R.id.mail_recycle_view);
+ mAdapter = new MailAdapter(this, 1000);
+ mRecyclerView.setAdapter(mAdapter);
+ mLayoutManager = new LinearLayoutManager(getActivity());
+ mRecyclerView.setLayoutManager(mLayoutManager);
+ return rootView;
+ }
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target02.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
index df7579d8304d..4b420dcea54d 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/b/Target02.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NonResizeablePortraitActivity.java
@@ -13,7 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.componentalias.tests.b;
-public class Target02 extends BaseReceiver {
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class NonResizeablePortraitActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_non_resizeable);
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index b31af385d363..a4dd5753539d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -50,12 +50,12 @@ public class NotificationActivity extends Activity {
Intent resultIntent = new Intent(this, NotificationActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(0,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-
+ PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, NotificationActivity.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
+ .setWhen(System.currentTimeMillis())
.setContentTitle("Flicker Test Notification")
.setContentText("Flicker Test Notification")
// Set the intent that will fire when the user taps the notification
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
new file mode 100644
index 000000000000..cdb1d42bd4f2
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -0,0 +1,307 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import static android.media.MediaMetadata.METADATA_KEY_TITLE;
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+import static android.media.session.PlaybackState.ACTION_STOP;
+import static android.media.session.PlaybackState.STATE_PAUSED;
+import static android.media.session.PlaybackState.STATE_PLAYING;
+import static android.media.session.PlaybackState.STATE_STOPPED;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_SET_REQUESTED_ORIENTATION;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_ENTER_PIP;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_PIP_ORIENTATION;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.app.RemoteAction;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Rational;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.RadioButton;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class PipActivity extends Activity {
+ private static final String TAG = PipActivity.class.getSimpleName();
+ /**
+ * A media session title for when the session is in {@link STATE_PLAYING}.
+ * TvPipNotificationTests check whether the actual notification title matches this string.
+ */
+ private static final String TITLE_STATE_PLAYING = "TestApp media is playing";
+ /**
+ * A media session title for when the session is in {@link STATE_PAUSED}.
+ * TvPipNotificationTests check whether the actual notification title matches this string.
+ */
+ private static final String TITLE_STATE_PAUSED = "TestApp media is paused";
+
+ private static final Rational RATIO_DEFAULT = null;
+ private static final Rational RATIO_SQUARE = new Rational(1, 1);
+ private static final Rational RATIO_WIDE = new Rational(2, 1);
+ private static final Rational RATIO_TALL = new Rational(1, 2);
+
+ private static final String PIP_ACTION_NO_OP = "No-Op";
+ private static final String PIP_ACTION_OFF = "Off";
+ private static final String PIP_ACTION_ON = "On";
+ private static final String PIP_ACTION_CLEAR = "Clear";
+ private static final String ACTION_NO_OP = "com.android.wm.shell.flicker.testapp.NO_OP";
+ private static final String ACTION_SWITCH_OFF =
+ "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 final PictureInPictureParams.Builder mPipParamsBuilder =
+ new PictureInPictureParams.Builder()
+ .setAspectRatio(RATIO_DEFAULT);
+ private MediaSession mMediaSession;
+ private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder()
+ .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP)
+ .setState(STATE_STOPPED, 0, 1f);
+ private PlaybackState mPlaybackState = mPlaybackStateBuilder.build();
+ private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder();
+
+ private final List<RemoteAction> mSwitchOffActions = new ArrayList<>();
+ private final List<RemoteAction> mSwitchOnActions = new ArrayList<>();
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isInPictureInPictureMode()) {
+ switch (intent.getAction()) {
+ case ACTION_SWITCH_ON:
+ mPipParamsBuilder.setActions(mSwitchOnActions);
+ break;
+ case ACTION_SWITCH_OFF:
+ mPipParamsBuilder.setActions(mSwitchOffActions);
+ break;
+ case ACTION_CLEAR:
+ mPipParamsBuilder.setActions(Collections.emptyList());
+ break;
+ case ACTION_NO_OP:
+ return;
+ default:
+ Log.w(TAG, "Unhandled action=" + intent.getAction());
+ return;
+ }
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ } else {
+ switch (intent.getAction()) {
+ case ACTION_ENTER_PIP:
+ enterPip(null);
+ break;
+ case ACTION_SET_REQUESTED_ORIENTATION:
+ setRequestedOrientation(Integer.parseInt(intent.getStringExtra(
+ EXTRA_PIP_ORIENTATION)));
+ break;
+ default:
+ Log.w(TAG, "Unhandled action=" + intent.getAction());
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Window window = getWindow();
+ final WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ window.setAttributes(layoutParams);
+
+ setContentView(R.layout.activity_pip);
+
+ findViewById(R.id.media_session_start)
+ .setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING));
+ findViewById(R.id.media_session_stop)
+ .setOnClickListener(v -> updateMediaSessionState(STATE_STOPPED));
+
+ mMediaSession = new MediaSession(this, "WMShell_TestApp");
+ mMediaSession.setPlaybackState(mPlaybackStateBuilder.build());
+ mMediaSession.setCallback(new MediaSession.Callback() {
+ @Override
+ public void onPlay() {
+ updateMediaSessionState(STATE_PLAYING);
+ }
+
+ @Override
+ public void onPause() {
+ updateMediaSessionState(STATE_PAUSED);
+ }
+
+ @Override
+ public void onStop() {
+ updateMediaSessionState(STATE_STOPPED);
+ }
+ });
+
+ // Build two sets of the custom actions. We'll replace one with the other when 'On'/'Off'
+ // action is invoked.
+ // The first set consists of 3 actions: 1) Off; 2) No-Op; 3) Clear.
+ // The second set consists of 2 actions: 1) On; 2) Clear.
+ // Upon invocation 'Clear' action clear-off all the custom actions, including itself.
+ final Icon icon = Icon.createWithResource(this, android.R.drawable.ic_menu_help);
+ final RemoteAction noOpAction = buildRemoteAction(icon, PIP_ACTION_NO_OP, ACTION_NO_OP);
+ final RemoteAction switchOnAction =
+ buildRemoteAction(icon, PIP_ACTION_ON, ACTION_SWITCH_ON);
+ final RemoteAction switchOffAction =
+ buildRemoteAction(icon, PIP_ACTION_OFF, ACTION_SWITCH_OFF);
+ final RemoteAction clearAllAction = buildRemoteAction(icon, PIP_ACTION_CLEAR, ACTION_CLEAR);
+ mSwitchOffActions.addAll(Arrays.asList(switchOnAction, clearAllAction));
+ mSwitchOnActions.addAll(Arrays.asList(noOpAction, switchOffAction, clearAllAction));
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_NO_OP);
+ filter.addAction(ACTION_SWITCH_ON);
+ filter.addAction(ACTION_SWITCH_OFF);
+ filter.addAction(ACTION_CLEAR);
+ filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
+ filter.addAction(ACTION_ENTER_PIP);
+ registerReceiver(mBroadcastReceiver, filter);
+
+ handleIntentExtra(getIntent());
+ }
+
+ @Override
+ protected void onDestroy() {
+ unregisterReceiver(mBroadcastReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ protected void onUserLeaveHint() {
+ // Only used when auto PiP is disabled. This is to simulate the behavior that an app
+ // supports regular PiP but not auto PiP.
+ final boolean manuallyEnterPip =
+ ((RadioButton) findViewById(R.id.enter_pip_on_leave_manual)).isChecked();
+ if (manuallyEnterPip) {
+ enterPictureInPictureMode();
+ }
+ }
+
+ private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
+ final Intent intent = new Intent(action);
+ final PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ return new RemoteAction(icon, label, label, pendingIntent);
+ }
+
+ public void enterPip(View v) {
+ final boolean withCustomActions =
+ ((CheckBox) findViewById(R.id.with_custom_actions)).isChecked();
+ mPipParamsBuilder.setActions(
+ withCustomActions ? mSwitchOnActions : Collections.emptyList());
+ enterPictureInPictureMode(mPipParamsBuilder.build());
+ }
+
+ public void onAutoPipSelected(View v) {
+ switch (v.getId()) {
+ case R.id.enter_pip_on_leave_manual:
+ // disable auto enter PiP
+ case R.id.enter_pip_on_leave_disabled:
+ mPipParamsBuilder.setAutoEnterEnabled(false);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ break;
+ case R.id.enter_pip_on_leave_autoenter:
+ mPipParamsBuilder.setAutoEnterEnabled(true);
+ setPictureInPictureParams(mPipParamsBuilder.build());
+ break;
+ }
+ }
+
+ public void onRatioSelected(View v) {
+ switch (v.getId()) {
+ case R.id.ratio_default:
+ mPipParamsBuilder.setAspectRatio(RATIO_DEFAULT);
+ break;
+
+ case R.id.ratio_square:
+ mPipParamsBuilder.setAspectRatio(RATIO_SQUARE);
+ break;
+
+ case R.id.ratio_wide:
+ mPipParamsBuilder.setAspectRatio(RATIO_WIDE);
+ break;
+
+ case R.id.ratio_tall:
+ mPipParamsBuilder.setAspectRatio(RATIO_TALL);
+ break;
+ }
+ }
+
+ private void updateMediaSessionState(int newState) {
+ if (mPlaybackState.getState() == newState) {
+ return;
+ }
+ final String title;
+ switch (newState) {
+ case STATE_PLAYING:
+ title = TITLE_STATE_PLAYING;
+ break;
+ case STATE_PAUSED:
+ title = TITLE_STATE_PAUSED;
+ break;
+ case STATE_STOPPED:
+ title = "";
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown state " + newState);
+ }
+
+ mPlaybackStateBuilder.setState(newState, 0, 1f);
+ mPlaybackState = mPlaybackStateBuilder.build();
+
+ mMediaMetadataBuilder.putText(METADATA_KEY_TITLE, title);
+
+ mMediaSession.setPlaybackState(mPlaybackState);
+ mMediaSession.setMetadata(mMediaMetadataBuilder.build());
+ mMediaSession.setActive(newState != STATE_STOPPED);
+ }
+
+ private void handleIntentExtra(Intent intent) {
+ // Set the fixed orientation if requested
+ if (intent.hasExtra(EXTRA_PIP_ORIENTATION)) {
+ final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
+ setRequestedOrientation(ori);
+ }
+ // Enter picture in picture with the given aspect ratio if provided
+ if (intent.hasExtra(EXTRA_ENTER_PIP)) {
+ mPipParamsBuilder.setActions(mSwitchOnActions);
+ enterPip(null);
+ }
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
index 5cf81cb90fbc..ce7a0059fa2d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.testapp;
import static android.os.SystemClock.sleep;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD;
import android.app.Activity;
import android.os.Bundle;
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
new file mode 100644
index 000000000000..70196aee9ff1
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitScreenActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_splitscreen);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java
new file mode 100644
index 000000000000..a8ce8ff8f589
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitScreenSecondaryActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.activity_splitscreen_secondary);
+ }
+}
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index 07e775aeb838..9696fc31469a 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -3,6 +3,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworkperf">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL"/>
+ <uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
+ <uses-permission android:name="Manifest.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+
+
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-sdk android:minSdkVersion="5"/>
diff --git a/tests/HandwritingIme/OWNERS b/tests/HandwritingIme/OWNERS
new file mode 100644
index 000000000000..6bb4b17ed4eb
--- /dev/null
+++ b/tests/HandwritingIme/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java
new file mode 100644
index 000000000000..6b924f335ef7
--- /dev/null
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java
@@ -0,0 +1,116 @@
+/*
+ * 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.google.android.test.handwritingime;
+
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_EDITOR_BOUNDS;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_NONE;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorBoundsInfo;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+
+import java.util.List;
+
+public class BoundsInfoDrawHelper {
+ private static final Paint sPaint = new Paint();
+ private static final int EDITOR_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.DKGRAY, 128);
+ private static final int HANDWRITING_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.BLUE, 128);
+ private static final int VISIBLE_LINE_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.MAGENTA, 128);
+
+ public static void draw(Canvas canvas, View inkView, int boundsInfoMode,
+ CursorAnchorInfo cursorAnchorInfo) {
+ if (boundsInfoMode == BOUNDS_INFO_NONE || cursorAnchorInfo == null) {
+ return;
+ }
+
+ // The matrix in CursorAnchorInfo transforms the editor coordinates to on-screen
+ // coordinates. We then transform the matrix from the on-screen coordinates to the
+ // inkView's coordinates. So the result matrix transforms the editor coordinates
+ // to the inkView coordinates.
+ final Matrix matrix = cursorAnchorInfo.getMatrix();
+ inkView.transformMatrixToLocal(matrix);
+
+ if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+ drawEditorBoundsInfo(canvas, matrix, cursorAnchorInfo.getEditorBoundsInfo());
+ }
+
+ if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+ drawVisibleLineBounds(canvas, matrix, cursorAnchorInfo.getVisibleLineBounds());
+ }
+ }
+
+ private static void setPaintForEditorBoundsInfo() {
+ sPaint.reset();
+ sPaint.setStyle(Paint.Style.STROKE);
+ sPaint.setStrokeWidth(5f);
+ }
+
+ private static void drawEditorBoundsInfo(Canvas canvas, Matrix matrix,
+ @Nullable EditorBoundsInfo editorBoundsInfo) {
+ if (editorBoundsInfo == null) {
+ return;
+ }
+ final RectF editorBounds = editorBoundsInfo.getEditorBounds();
+ setPaintForEditorBoundsInfo();
+ if (editorBounds != null) {
+ final RectF localEditorBounds = new RectF(editorBounds);
+ matrix.mapRect(localEditorBounds);
+ sPaint.setColor(EDITOR_BOUNDS_COLOR);
+ canvas.drawRect(localEditorBounds, sPaint);
+ }
+
+ final RectF handwritingBounds = editorBoundsInfo.getHandwritingBounds();
+ if (handwritingBounds != null) {
+ final RectF localHandwritingBounds = new RectF(handwritingBounds);
+ matrix.mapRect(localHandwritingBounds);
+ sPaint.setColor(HANDWRITING_BOUNDS_COLOR);
+ canvas.drawRect(localHandwritingBounds, sPaint);
+ }
+ }
+
+ private static void setPaintForVisibleLineBounds() {
+ sPaint.reset();
+ sPaint.setStyle(Paint.Style.STROKE);
+ sPaint.setStrokeWidth(2f);
+ sPaint.setColor(VISIBLE_LINE_BOUNDS_COLOR);
+ }
+
+ private static void drawVisibleLineBounds(Canvas canvas, Matrix matrix,
+ List<RectF> visibleLineBounds) {
+ if (visibleLineBounds.isEmpty()) {
+ return;
+ }
+ setPaintForVisibleLineBounds();
+ for (RectF lineBound : visibleLineBounds) {
+ matrix.mapRect(lineBound);
+ canvas.drawRect(lineBound, sPaint);
+ }
+ }
+}
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 bf8bd14645a0..8380dcf4b4a4 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -15,28 +15,60 @@
*/
package com.google.android.test.handwritingime;
+import android.R;
import android.annotation.Nullable;
+import android.graphics.PointF;
+import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.util.Log;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.HandwritingGesture;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.JoinOrSplitGesture;
+import android.view.inputmethod.RemoveSpaceGesture;
+import android.view.inputmethod.SelectGesture;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
import android.widget.FrameLayout;
-import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
import android.widget.Toast;
import java.util.Random;
+import java.util.function.IntConsumer;
public class HandwritingIme extends InputMethodService {
+ private static final int OP_NONE = 0;
+ private static final int OP_SELECT = 1;
+ private static final int OP_DELETE = 2;
+ private static final int OP_INSERT = 3;
+ private static final int OP_REMOVE_SPACE = 4;
+ private static final int OP_JOIN_OR_SPLIT = 5;
- public static final int HEIGHT_DP = 100;
-
- private Window mInkWindow;
private InkView mInk;
static final String TAG = "HandwritingIme";
+ private int mRichGestureMode = OP_NONE;
+ private int mRichGestureGranularity = -1;
+ private Spinner mRichGestureModeSpinner;
+ private Spinner mRichGestureGranularitySpinner;
+ private PointF mRichGestureStartPoint;
+
+ static final int BOUNDS_INFO_NONE = 0;
+ static final int BOUNDS_INFO_VISIBLE_LINE_BOUNDS = 1;
+ static final int BOUNDS_INFO_EDITOR_BOUNDS = 2;
+ private int mBoundsInfoMode = BOUNDS_INFO_NONE;
+ private LinearLayout mBoundsInfoCheckBoxes;
+
+ private final IntConsumer mResultConsumer = value -> Log.d(TAG, "Gesture result: " + value);
interface HandwritingFinisher {
void finish();
@@ -66,8 +98,108 @@ public class HandwritingIme extends InputMethodService {
private void onStylusEvent(@Nullable MotionEvent event) {
// TODO Hookup recognizer here
- if (event.getAction() == MotionEvent.ACTION_UP) {
- sendKeyChar((char) (56 + new Random().nextInt(66)));
+ 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;
+ }
+ if (gesture == null) {
+ // This shouldn't happen
+ Log.e(TAG, "Unrecognized gesture mode: " + mRichGestureMode);
+ return;
+ }
+ performGesture(gesture);
+ } 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;
+ }
+ }
+ }
+
+ /**
+ * sanitize values to support rectangles in all cases.
+ */
+ private RectF getSanitizedRectF(float left, float top, float right, float bottom) {
+ // swap values when left > right OR top > bottom.
+ if (left > right) {
+ float temp = left;
+ left = right;
+ right = temp;
+ }
+ if (top > bottom) {
+ float temp = top;
+ top = bottom;
+ bottom = temp;
+ }
+ // increment by a pixel so that RectF.isEmpty() isn't true.
+ if (left == right) {
+ right++;
+ }
+ if (top == bottom) {
+ bottom++;
+ }
+
+ RectF rectF = new RectF(left, top, right, bottom);
+ Log.d(TAG, "Sending RichGesture " + rectF.toShortString());
+ return rectF;
+ }
+
+ private void performGesture(HandwritingGesture gesture) {
+ InputConnection ic = getCurrentInputConnection();
+ if (getCurrentInputStarted() && ic != null) {
+ ic.performHandwritingGesture(gesture, Runnable::run, mResultConsumer);
+ } else {
+ // This shouldn't happen
+ Log.e(TAG, "No active InputConnection");
}
}
@@ -75,29 +207,158 @@ public class HandwritingIme extends InputMethodService {
public View onCreateInputView() {
Log.d(TAG, "onCreateInputView");
final ViewGroup view = new FrameLayout(this);
- final View inner = new View(this);
- final float density = getResources().getDisplayMetrics().density;
- final int height = (int) (HEIGHT_DP * density);
view.setPadding(0, 0, 0, 0);
- view.addView(inner, new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, height));
- TextView text = new TextView(this);
- text.setText("Handwriting IME");
- text.setTextSize(13f);
- text.setTextColor(getColor(android.R.color.white));
- text.setGravity(Gravity.CENTER);
- text.setLayoutParams(new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, height));
- view.addView(text);
- inner.setBackgroundColor(0xff0110fe); // blue
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.addView(getRichGestureActionsSpinner());
+ layout.addView(getRichGestureGranularitySpinner());
+ layout.addView(getBoundsInfoCheckBoxes());
+ layout.setBackgroundColor(getColor(R.color.holo_green_light));
+ view.addView(layout);
return view;
}
+ private View getRichGestureActionsSpinner() {
+ if (mRichGestureModeSpinner != null) {
+ return mRichGestureModeSpinner;
+ }
+ mRichGestureModeSpinner = new Spinner(this);
+ mRichGestureModeSpinner.setPadding(100, 0, 100, 0);
+ mRichGestureModeSpinner.setTooltipText("Handwriting IME mode");
+ String[] items = new String[]{
+ "Handwriting IME - Rich gesture disabled",
+ "Rich gesture SELECT",
+ "Rich gesture DELETE",
+ "Rich gesture INSERT",
+ "Rich gesture REMOVE SPACE",
+ "Rich gesture JOIN OR SPLIT",
+ };
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_dropdown_item, items);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mRichGestureModeSpinner.setAdapter(adapter);
+ mRichGestureModeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mRichGestureMode = position;
+ mRichGestureGranularitySpinner.setEnabled(
+ mRichGestureMode == OP_SELECT || mRichGestureMode == OP_DELETE);
+ Log.d(TAG, "Setting RichGesture Mode " + mRichGestureMode);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ mRichGestureMode = OP_NONE;
+ mRichGestureGranularitySpinner.setEnabled(false);
+ }
+ });
+ mRichGestureModeSpinner.setSelection(0); // default disabled
+ return mRichGestureModeSpinner;
+ }
+
+ private void updateCursorAnchorInfo(int boundsInfoMode) {
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+
+ if (boundsInfoMode == BOUNDS_INFO_NONE) {
+ ic.requestCursorUpdates(0);
+ return;
+ }
+
+ final int cursorUpdateMode = InputConnection.CURSOR_UPDATE_MONITOR;
+ int cursorUpdateFilter = 0;
+ if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+ cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
+ }
+
+ if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+ cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS;
+ }
+ ic.requestCursorUpdates(cursorUpdateMode | cursorUpdateFilter);
+ }
+
+ private void updateBoundsInfoMode() {
+ if (mInk != null) {
+ mInk.setBoundsInfoMode(mBoundsInfoMode);
+ }
+ updateCursorAnchorInfo(mBoundsInfoMode);
+ }
+
+ private View getBoundsInfoCheckBoxes() {
+ if (mBoundsInfoCheckBoxes != null) {
+ return mBoundsInfoCheckBoxes;
+ }
+ mBoundsInfoCheckBoxes = new LinearLayout(this);
+ mBoundsInfoCheckBoxes.setPadding(100, 0, 100, 0);
+ mBoundsInfoCheckBoxes.setOrientation(LinearLayout.HORIZONTAL);
+
+ final CheckBox editorBoundsInfoCheckBox = new CheckBox(this);
+ editorBoundsInfoCheckBox.setText("EditorBoundsInfo");
+ editorBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mBoundsInfoMode |= BOUNDS_INFO_EDITOR_BOUNDS;
+ } else {
+ mBoundsInfoMode &= ~BOUNDS_INFO_EDITOR_BOUNDS;
+ }
+ updateBoundsInfoMode();
+ });
+
+ final CheckBox visibleLineBoundsInfoCheckBox = new CheckBox(this);
+ visibleLineBoundsInfoCheckBox.setText("VisibleLineBounds");
+ visibleLineBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mBoundsInfoMode |= BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+ } else {
+ mBoundsInfoMode &= ~BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+ }
+ updateBoundsInfoMode();
+ });
+
+ mBoundsInfoCheckBoxes.addView(editorBoundsInfoCheckBox);
+ mBoundsInfoCheckBoxes.addView(visibleLineBoundsInfoCheckBox);
+ return mBoundsInfoCheckBoxes;
+ }
+
+ private View getRichGestureGranularitySpinner() {
+ if (mRichGestureGranularitySpinner != null) {
+ return mRichGestureGranularitySpinner;
+ }
+ mRichGestureGranularitySpinner = new Spinner(this);
+ mRichGestureGranularitySpinner.setPadding(100, 0, 100, 0);
+ mRichGestureGranularitySpinner.setTooltipText(" Granularity");
+ String[] items =
+ new String[] { "Granularity - UNDEFINED",
+ "Granularity - WORD", "Granularity - CHARACTER"};
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_spinner_dropdown_item, items);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mRichGestureGranularitySpinner.setAdapter(adapter);
+ mRichGestureGranularitySpinner.setOnItemSelectedListener(
+ new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mRichGestureGranularity = position;
+ Log.d(TAG, "Setting RichGesture Granularity " + mRichGestureGranularity);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ mRichGestureGranularity = 0;
+ }
+ });
+ mRichGestureGranularitySpinner.setSelection(1);
+ return mRichGestureGranularitySpinner;
+ }
+
public void onPrepareStylusHandwriting() {
Log.d(TAG, "onPrepareStylusHandwriting ");
if (mInk == null) {
mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer());
+ mInk.setBoundsInfoMode(mBoundsInfoMode);
}
}
@@ -105,8 +366,8 @@ public class HandwritingIme extends InputMethodService {
public boolean onStartStylusHandwriting() {
Log.d(TAG, "onStartStylusHandwriting ");
Toast.makeText(this, "START HW", Toast.LENGTH_SHORT).show();
- mInkWindow = getStylusHandwritingWindow();
- mInkWindow.setContentView(mInk, mInk.getLayoutParams());
+ Window inkWindow = getStylusHandwritingWindow();
+ inkWindow.setContentView(mInk, mInk.getLayoutParams());
return true;
}
@@ -118,4 +379,25 @@ public class HandwritingIme extends InputMethodService {
((ViewGroup) mInk.getParent()).removeView(mInk);
mInk = null;
}
+
+ @Override
+ public boolean onEvaluateFullscreenMode() {
+ return false;
+ }
+
+ private boolean areRichGesturesEnabled() {
+ return mRichGestureMode != OP_NONE;
+ }
+
+ @Override
+ public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ if (mInk != null) {
+ mInk.setCursorAnchorInfo(cursorAnchorInfo);
+ }
+ }
+
+ @Override
+ public void onStartInput(EditorInfo attribute, boolean restarting) {
+ updateCursorAnchorInfo(mBoundsInfoMode);
+ }
}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index 87a5b900cc18..86b324cf08d9 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -19,27 +19,28 @@ package com.google.android.test.handwritingime;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import android.view.inputmethod.CursorAnchorInfo;
class InkView extends View {
- private static final long FINISH_TIMEOUT = 2500;
+ private static final long FINISH_TIMEOUT = 1500;
private final HandwritingIme.HandwritingFinisher mHwCanceller;
private final HandwritingIme.StylusConsumer mConsumer;
- private final int mTopInset;
- private Paint mPaint;
- private Path mPath;
+ private final Paint mPaint;
+ private final Path mPath;
private float mX, mY;
private static final float STYLUS_MOVE_TOLERANCE = 1;
private Runnable mFinishRunnable;
+ private CursorAnchorInfo mCursorAnchorInfo;
+ private int mBoundsInfoMode;
+
InkView(Context context, HandwritingIme.HandwritingFinisher hwController,
HandwritingIme.StylusConsumer consumer) {
super(context);
@@ -59,12 +60,8 @@ class InkView extends View {
WindowManager wm = context.getSystemService(WindowManager.class);
WindowMetrics metrics = wm.getCurrentWindowMetrics();
- Insets insets = metrics.getWindowInsets()
- .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
setLayoutParams(new ViewGroup.LayoutParams(
- metrics.getBounds().width() - insets.left - insets.right,
- metrics.getBounds().height() - insets.top - insets.bottom));
- mTopInset = insets.top;
+ metrics.getBounds().width(), metrics.getBounds().height()));
}
@Override
@@ -73,17 +70,16 @@ class InkView extends View {
canvas.drawPath(mPath, mPaint);
canvas.drawARGB(20, 255, 50, 50);
+ BoundsInfoDrawHelper.draw(canvas, this, mBoundsInfoMode, mCursorAnchorInfo);
}
private void stylusStart(float x, float y) {
- y = y - mTopInset;
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void stylusMove(float x, float y) {
- y = y - mTopInset;
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (mPath.isEmpty()) {
@@ -165,4 +161,15 @@ class InkView extends View {
return mFinishRunnable;
}
+ void setCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ mCursorAnchorInfo = cursorAnchorInfo;
+ invalidate();
+ }
+
+ void setBoundsInfoMode(int boundsInfoMode) {
+ if (boundsInfoMode != mBoundsInfoMode) {
+ invalidate();
+ }
+ mBoundsInfoMode = boundsInfoMode;
+ }
}
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
index 2ad0da98c409..8b9c02049351 100644
--- a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
@@ -58,7 +58,7 @@ public class ViewDumpParser {
Object hash = getProperty(props, "__hash__");
if (name instanceof String && hash instanceof Integer) {
- return String.format(Locale.US, "%s@%x", name, hash);
+ return String.format(Locale.US, "%s@%x", name, (Integer) hash);
} else {
return null;
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b0ccbd1cf22f..80c7a21dc11b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -120,6 +120,15 @@
</intent-filter>
</activity>
+ <activity android:name="ColorBitmapActivity"
+ android:label="Bitmaps/BitmapColors"
+ 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="PathOffsetActivity"
android:label="Path/Offset"
android:exported="true">
@@ -436,6 +445,15 @@
</intent-filter>
</activity>
+ <activity android:name="SurfaceViewAlphaActivity"
+ android:label="SurfaceView/SurfaceView with Alpha"
+ 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=".PenStylusActivity"
android:label="Pen/Draw"
android:exported="true">
@@ -1127,6 +1145,15 @@
</intent-filter>
</activity>
+ <activity android:name="HardwareBufferRendererActivity"
+ android:label="HardwareRenderer/HardwareBufferRenderer"
+ 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="MyLittleTextureView"
android:label="HardwareRenderer/MyLittleTextureView"
android:screenOrientation="fullSensor"
@@ -1137,5 +1164,21 @@
</intent-filter>
</activity>
+ <activity android:name="MeshActivity"
+ android:label="Mesh/SimpleMesh"
+ 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="MeshLargeActivity"
+ android:label="Mesh/LargeMesh"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
new file mode 100644
index 000000000000..e2d17cdbe9e6
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorBitmapActivity.java
@@ -0,0 +1,315 @@
+/*
+ * 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.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorSpace;
+import android.graphics.HardwareBufferRenderer;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.graphics.SurfaceTexture;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageWriter;
+import android.os.Bundle;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ColorBitmapActivity extends Activity implements SurfaceHolder.Callback,
+ TextureView.SurfaceTextureListener {
+
+ private static final int WIDTH = 512;
+ private static final int HEIGHT = 512;
+
+ private ImageView mImageView;
+ private SurfaceView mSurfaceView;
+ private TextureView mTextureView;
+ private HardwareBuffer mGradientBuffer;
+ private Map<View, ImageWriter> mImageWriters = new HashMap<>();
+ private ColorSpace mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+ private String[] mColorNames = {"sRGB", "BT2020_HLG", "BT2020_PQ"};
+
+ private int mGradientEndColor = 0xFFFFFFFF;
+
+ private int[] mGradientEndColors = {0xFFFFFFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF};
+ private String[] mGradientColorNames = {"Grayscale", "Red", "Green", "Blue"};
+
+ private final ExecutorService mBufferFenceExecutor = Executors.newFixedThreadPool(1);
+ private final ExecutorService mBufferExecutor = Executors.newFixedThreadPool(1);
+
+ private FutureTask<HardwareBuffer> authorGradientBuffer(
+ HardwareBuffer buffer, int gradentEndColor) {
+ HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer);
+ RenderNode node = new RenderNode("content");
+ node.setPosition(0, 0, buffer.getWidth(), buffer.getHeight());
+
+ Canvas canvas = node.beginRecording();
+ LinearGradient gradient = new LinearGradient(
+ 0, 0, buffer.getWidth(), buffer.getHeight(), 0xFF000000,
+ gradentEndColor, Shader.TileMode.CLAMP);
+ Paint paint = new Paint();
+ paint.setShader(gradient);
+ paint.setDither(true);
+ canvas.drawRect(0f, 0f, buffer.getWidth(), buffer.getHeight(), paint);
+ node.endRecording();
+
+ renderer.setContentRoot(node);
+
+ ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+ FutureTask<HardwareBuffer> resolvedBuffer = new FutureTask<>(() -> buffer);
+ renderer.obtainRenderRequest()
+ .setColorSpace(colorSpace)
+ .draw(mBufferFenceExecutor, result -> {
+ result.getFence().await(Duration.ofSeconds(3));
+ resolvedBuffer.run();
+ });
+ return resolvedBuffer;
+ }
+
+ private FutureTask<HardwareBuffer> getGradientBuffer() {
+ HardwareBuffer buffer = HardwareBuffer.create(
+ WIDTH, HEIGHT, PixelFormat.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
+ return authorGradientBuffer(buffer, mGradientEndColor);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ try {
+
+ mColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
+
+ ArrayAdapter<String> colorSpaceAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_item, mColorNames);
+
+ colorSpaceAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Spinner colorSpaceSpinner = new Spinner(this);
+ colorSpaceSpinner.setAdapter(colorSpaceAdapter);
+ colorSpaceSpinner.setOnItemSelectedListener(new ColorSpaceOnItemSelectedListener());
+
+ ArrayAdapter<String> gradientColorAdapter = new ArrayAdapter<>(
+ this, android.R.layout.simple_spinner_item, mGradientColorNames);
+
+ gradientColorAdapter
+ .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ Spinner gradientColorSpinner = new Spinner(this);
+ gradientColorSpinner.setAdapter(gradientColorAdapter);
+ gradientColorSpinner
+ .setOnItemSelectedListener(new GradientColorOnItemSelectedListener());
+
+ mGradientBuffer = getGradientBuffer().get();
+
+ LinearLayout linearLayout = new LinearLayout(this);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+
+ TextView imageViewText = new TextView(this);
+ imageViewText.setText("ImageView");
+ mImageView = new ImageView(this);
+
+ TextView textureViewText = new TextView(this);
+ textureViewText.setText("TextureView");
+ mTextureView = new TextureView(this);
+ mTextureView.setSurfaceTextureListener(this);
+
+ TextView surfaceViewText = new TextView(this);
+ surfaceViewText.setText("SurfaceView");
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+
+ LinearLayout spinnerLayout = new LinearLayout(this);
+ spinnerLayout.setOrientation(LinearLayout.HORIZONTAL);
+
+ spinnerLayout.addView(colorSpaceSpinner, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ spinnerLayout.addView(gradientColorSpinner, 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));
+
+ linearLayout.addView(imageViewText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ linearLayout.addView(mImageView, new LinearLayout.LayoutParams(WIDTH, HEIGHT));
+ linearLayout.addView(textureViewText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ linearLayout.addView(mTextureView, new LinearLayout.LayoutParams(WIDTH, HEIGHT));
+ linearLayout.addView(surfaceViewText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+ linearLayout.addView(mSurfaceView, new LinearLayout.LayoutParams(WIDTH, HEIGHT));
+
+ setContentView(linearLayout);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private ColorSpace getFromName(String name) {
+ if (name.equals("sRGB")) {
+ return ColorSpace.get(ColorSpace.Named.SRGB);
+ } else if (name.equals("BT2020_HLG")) {
+ return ColorSpace.get(ColorSpace.Named.BT2020_HLG);
+ } else if (name.equals("BT2020_PQ")) {
+ return ColorSpace.get(ColorSpace.Named.BT2020_PQ);
+ }
+
+ throw new RuntimeException("Unrecognized Colorspace!");
+ }
+
+ private void populateBuffers() {
+ try {
+ Bitmap bitmap = Bitmap.wrapHardwareBuffer(
+ getGradientBuffer().get(), ColorSpace.get(ColorSpace.Named.SRGB));
+ Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ copy.setColorSpace(mColorSpace);
+ mImageView.setImageBitmap(copy);
+
+ for (ImageWriter writer : mImageWriters.values()) {
+ mBufferExecutor.execute(() -> {
+ try (Image image = writer.dequeueInputImage()) {
+ authorGradientBuffer(image.getHardwareBuffer(), mGradientEndColor).get();
+ image.setDataSpace(mColorSpace.getDataSpace());
+ writer.queueInputImage(image);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ mImageWriters.put(mSurfaceView, new ImageWriter.Builder(holder.getSurface())
+ .setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
+ | HardwareBuffer.USAGE_COMPOSER_OVERLAY)
+ .build());
+ populateBuffers();
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mImageWriters.containsKey(mSurfaceView)) {
+ mImageWriters.remove(mSurfaceView);
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(
+ @NonNull SurfaceTexture surface, int width, int height) {
+ mImageWriters.put(mTextureView, new ImageWriter.Builder(new Surface(surface))
+ .setUsage(HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE
+ | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
+ .build());
+ populateBuffers();
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(
+ @NonNull SurfaceTexture surface, int width, int height) {
+
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
+ if (mImageWriters.containsKey(mTextureView)) {
+ mImageWriters.remove(mTextureView);
+ }
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
+
+ }
+
+ private final class ColorSpaceOnItemSelectedListener
+ implements AdapterView.OnItemSelectedListener {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ ColorBitmapActivity.this.mColorSpace =
+ getFromName(ColorBitmapActivity.this.mColorNames[position]);
+ ColorBitmapActivity.this.getMainExecutor()
+ .execute(ColorBitmapActivity.this::populateBuffers);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ }
+
+ private final class GradientColorOnItemSelectedListener
+ implements AdapterView.OnItemSelectedListener {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ ColorBitmapActivity.this.mGradientEndColor =
+ ColorBitmapActivity.this.mGradientEndColors[position];
+ ColorBitmapActivity.this.getMainExecutor()
+ .execute(ColorBitmapActivity.this::populateBuffers);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java
new file mode 100644
index 000000000000..e4de434f1ed2
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java
@@ -0,0 +1,86 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorSpace;
+import android.graphics.ColorSpace.Named;
+import android.graphics.HardwareBufferRenderer;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RenderNode;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.time.Duration;
+import java.util.concurrent.Executors;
+
+public class HardwareBufferRendererActivity extends Activity {
+
+ private ImageView mImageView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mImageView = new ImageView(this);
+ mImageView.setBackgroundColor(Color.MAGENTA);
+ FrameLayout layout = new FrameLayout(this);
+ layout.setBackgroundColor(Color.CYAN);
+ layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ layout.addView(mImageView, new FrameLayout.LayoutParams(100, 100));
+ setContentView(layout);
+
+ HardwareBuffer buffer = HardwareBuffer.create(100, 100, PixelFormat.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT);
+ HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer);
+ RenderNode node = new RenderNode("content");
+ node.setPosition(0, 0, 100, 100);
+
+ Canvas canvas = node.beginRecording();
+ canvas.drawColor(Color.BLUE);
+
+ Paint paint = new Paint();
+ paint.setColor(Color.RED);
+ canvas.drawRect(0f, 0f, 50f, 50f, paint);
+ node.endRecording();
+
+ renderer.setContentRoot(node);
+
+ ColorSpace colorSpace = ColorSpace.get(Named.SRGB);
+ Handler handler = new Handler(Looper.getMainLooper());
+ renderer.obtainRenderRequest()
+ .setColorSpace(colorSpace)
+ .draw(Executors.newSingleThreadExecutor(), result -> {
+ result.getFence().await(Duration.ofMillis(3000));
+ handler.post(() -> {
+ Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, colorSpace);
+ Bitmap copy = bitmap.copy(Config.ARGB_8888, false);
+ mImageView.setImageBitmap(copy);
+ });
+ });
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
new file mode 100644
index 000000000000..ae3dcb834687
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
@@ -0,0 +1,125 @@
+/*
+ * 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.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Mesh;
+import android.graphics.MeshSpecification;
+import android.graphics.MeshSpecification.Attribute;
+import android.graphics.MeshSpecification.Varying;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+public class MeshActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new MeshView(this));
+ }
+
+ static class MeshView extends View {
+ MeshView(Context c) {
+ super(c);
+ this.setOnTouchListener((v, event) -> {
+ invalidate();
+ return true;
+ });
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ MeshSpecification meshSpec = createMeshSpecification();
+ FloatBuffer vertexBuffer = FloatBuffer.allocate(6);
+ vertexBuffer.put(0, 100.0f);
+ vertexBuffer.put(1, 100.0f);
+ vertexBuffer.put(2, 400.0f);
+ vertexBuffer.put(3, 0.0f);
+ vertexBuffer.put(4, 0.0f);
+ vertexBuffer.put(5, 400.0f);
+ vertexBuffer.rewind();
+ Mesh mesh = new Mesh(
+ meshSpec, Mesh.TRIANGLES, vertexBuffer, 3, new RectF(0, 0, 1000, 1000));
+
+ canvas.drawMesh(mesh, BlendMode.COLOR, new Paint());
+
+ int numTriangles = 100;
+ // number of triangles plus first 2 vertices
+ FloatBuffer iVertexBuffer = FloatBuffer.allocate(numTriangles * 2 + 4);
+ ShortBuffer indexBuffer = ShortBuffer.allocate(300);
+
+ int radius = 200;
+ // origin
+ iVertexBuffer.put(0, 500.0f);
+ iVertexBuffer.put(1, 500.0f);
+
+ // first point
+ iVertexBuffer.put(2, 500.0f + radius);
+ iVertexBuffer.put(3, 500.0f);
+ int nVert = 2;
+ int nInd = 0;
+ for (int i = 1; i <= numTriangles; i++) {
+ double angle = (Math.PI * i) / numTriangles;
+ double x = radius * Math.cos(angle);
+ double y = radius * Math.sin(angle);
+ iVertexBuffer.put((i + 1) * 2, 500 + (float) x);
+ iVertexBuffer.put((i + 1) * 2 + 1, 500 + (float) y);
+
+ indexBuffer.put(nInd++, (short) 0);
+ indexBuffer.put(nInd++, (short) (nVert - 1));
+ indexBuffer.put(nInd++, (short) nVert);
+ nVert++;
+ }
+ iVertexBuffer.rewind();
+ indexBuffer.rewind();
+ Mesh mesh2 = new Mesh(meshSpec, Mesh.TRIANGLES, iVertexBuffer, 102, indexBuffer,
+ new RectF(0, 0, 1000, 1000));
+ Paint paint = new Paint();
+ paint.setColor(Color.RED);
+ canvas.drawMesh(mesh2, BlendMode.COLOR, paint);
+ }
+
+ private MeshSpecification createMeshSpecification() {
+ String vs = "Varyings main(const Attributes attributes) { "
+ + " Varyings varyings;"
+ + " varyings.position = attributes.position;"
+ + " return varyings;"
+ + "}";
+ String fs = "float2 main(const Varyings varyings, out float4 color) {\n"
+ + " color = vec4(1.0, 0.0, 0.0, 1.0);"
+ + " return varyings.position;\n"
+ + "}";
+ Attribute[] attList = new Attribute[]{
+ new Attribute(MeshSpecification.TYPE_FLOAT2, 0, "position"),
+
+ };
+ Varying[] varyList = new Varying[0];
+ return MeshSpecification.make(attList, 8, varyList, vs, fs);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
new file mode 100644
index 000000000000..01ca2fcdbb86
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
@@ -0,0 +1,167 @@
+/*
+ * 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.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Mesh;
+import android.graphics.MeshSpecification;
+import android.graphics.MeshSpecification.Attribute;
+import android.graphics.MeshSpecification.Varying;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.view.View;
+
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+public class MeshLargeActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(new MeshView(this));
+ }
+
+ static class MeshView extends View {
+ MeshView(Context c) {
+ super(c);
+ this.setOnTouchListener((v, event) -> {
+ invalidate();
+ return true;
+ });
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ MeshSpecification meshSpec = createMeshSpecification();
+ int numTriangles = 10000;
+ // number of triangles plus first 2 vertices
+ FloatBuffer vertexBuffer = FloatBuffer.allocate((numTriangles + 2) * 30);
+ ShortBuffer indexBuffer = ShortBuffer.allocate(numTriangles * 3);
+
+ float origin = 500.0f;
+ int radius = 200;
+
+ // origin
+ vertexBuffer.put(0, origin);
+ vertexBuffer.put(1, origin);
+ for (int i = 0; i < 7; i++) {
+ vertexBuffer.put(2 + (i * 4), 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 1, 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 2, 1.0f);
+ vertexBuffer.put(2 + (i * 4) + 3, 1.0f);
+ }
+
+ // first point
+ vertexBuffer.put(30, origin + radius);
+ vertexBuffer.put(31, origin);
+ for (int i = 0; i < 7; i++) {
+ vertexBuffer.put(32 + (i * 4), 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 1, 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 2, 1.0f);
+ vertexBuffer.put(32 + (i * 4) + 3, 1.0f);
+ }
+
+ int nVert = 2;
+ int nInd = 0;
+ for (int i = 2; i <= numTriangles + 1; i++) {
+ double angle = 2 * Math.PI * i / numTriangles;
+ double x = radius * Math.cos(angle);
+ double y = radius * Math.sin(angle);
+ // position
+ vertexBuffer.put(i * 30, origin + (float) x);
+ vertexBuffer.put(i * 30 + 1, origin + (float) y);
+
+ // test through test7
+ for (int j = 0; j < 7; j++) {
+ vertexBuffer.put((i * 30 + 2) + (j * 4), 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 1, 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 2, 1.0f);
+ vertexBuffer.put((i * 30 + 2) + (j * 4) + 3, 1.0f);
+ }
+
+ indexBuffer.put(nInd++, (short) 0);
+ indexBuffer.put(nInd++, (short) (nVert - 1));
+ indexBuffer.put(nInd++, (short) nVert);
+ nVert++;
+ }
+ vertexBuffer.rewind();
+ indexBuffer.rewind();
+ Mesh mesh = new Mesh(
+ meshSpec, Mesh.TRIANGLES, vertexBuffer, numTriangles + 2, indexBuffer,
+ new RectF(0, 0, 1000, 1000)
+ );
+ mesh.setFloatUniform("test", 1.0f, 2.0f);
+ Paint paint = new Paint();
+ paint.setColor(Color.BLUE);
+
+ canvas.drawMesh(mesh, BlendMode.SRC, paint);
+ }
+
+ private MeshSpecification createMeshSpecification() {
+ String vs = "Varyings main(const Attributes attributes) { "
+ + " Varyings varyings;"
+ + " varyings.position = attributes.position;"
+ + " return varyings;"
+ + "}";
+ String fs = "uniform float2 test;"
+ + "float2 main(const Varyings varyings, out float4 color) {\n"
+ + " color = vec4(1.0, 0.0, 0.0, 1.0);"
+ + " return varyings.position;\n"
+ + "}";
+ Attribute[] attList = new Attribute[]{
+ new Attribute(MeshSpecification.TYPE_FLOAT2, 0, "position"),
+ new Attribute(MeshSpecification.TYPE_FLOAT4, 8, "test"),
+ new Attribute(MeshSpecification.TYPE_FLOAT4, 24, "test2"),
+ new Attribute(
+ MeshSpecification.TYPE_FLOAT4,
+ 40,
+ "test3"
+ ),
+ new Attribute(
+ MeshSpecification.TYPE_FLOAT4,
+ 56,
+ "test4"
+ ),
+ new Attribute(
+ MeshSpecification.TYPE_FLOAT4,
+ 72,
+ "test5"
+ ),
+ new Attribute(
+ MeshSpecification.TYPE_FLOAT4,
+ 88,
+ "test6"
+ ),
+ new Attribute(
+ MeshSpecification.TYPE_FLOAT4,
+ 104,
+ "test7"
+ )
+ };
+ Varying[] varyList = new Varying[0];
+ return MeshSpecification.make(attList, 120, varyList, vs, fs);
+ }
+ }
+}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
new file mode 100644
index 000000000000..01fe6ae0518b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 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.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class SurfaceViewAlphaActivity extends Activity implements Callback {
+ SurfaceView mSurfaceView;
+
+ private enum ZOrder {
+ ABOVE,
+ BELOW
+ }
+
+ private float mAlpha = 127f / 255f;
+ private ZOrder mZOrder = ZOrder.BELOW;
+
+
+ private String getAlphaText() {
+ return "Alpha: " + mAlpha;
+ }
+
+ private void toggleZOrder() {
+ if (ZOrder.ABOVE.equals(mZOrder)) {
+ mZOrder = ZOrder.BELOW;
+ } else {
+ mZOrder = ZOrder.ABOVE;
+ }
+ }
+
+ // Overlaps a blue view on the left, then the SurfaceView in the center, then a blue view on the
+ // right.
+ private void overlapViews(SurfaceView view, LinearLayout parent) {
+ float density = getResources().getDisplayMetrics().density;
+ int surfaceViewSize = (int) (200 * density);
+ int blueViewSize = (int) (surfaceViewSize * 2 / 3f);
+ int totalSize = (int) (surfaceViewSize * 5 / 3f);
+
+ RelativeLayout overlapLayout = new RelativeLayout(this);
+
+ RelativeLayout.LayoutParams leftViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ leftViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+
+ View leftBlueView = new View(this);
+ leftBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(leftBlueView, leftViewLayoutParams);
+
+ RelativeLayout.LayoutParams sVLayoutParams = new RelativeLayout.LayoutParams(
+ surfaceViewSize, surfaceViewSize);
+ sVLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+ overlapLayout.addView(view, sVLayoutParams);
+
+ RelativeLayout.LayoutParams rightViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ rightViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+
+ View rightBlueView = new View(this);
+ rightBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(rightBlueView, rightViewLayoutParams);
+
+ parent.addView(overlapLayout, new LinearLayout.LayoutParams(
+ totalSize, surfaceViewSize));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+ mSurfaceView.setAlpha(mAlpha);
+
+ LinearLayout content = new LinearLayout(this);
+ content.setOrientation(LinearLayout.VERTICAL);
+
+ TextView alphaText = new TextView(this);
+ alphaText.setText(getAlphaText());
+
+ SeekBar alphaToggle = new SeekBar(this);
+ alphaToggle.setMin(0);
+ alphaToggle.setMax(255);
+ alphaToggle.setProgress(Math.round(mAlpha * 255));
+ alphaToggle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAlpha = progress / 255f;
+ alphaText.setText(getAlphaText());
+ mSurfaceView.setAlpha(mAlpha);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ content.addView(alphaText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ content.addView(alphaToggle, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ Button button = new Button(this);
+ button.setText("Z " + mZOrder.toString());
+ button.setOnClickListener(v -> {
+ toggleZOrder();
+ mSurfaceView.setZOrderOnTop(ZOrder.ABOVE.equals(mZOrder));
+ button.setText("Z " + mZOrder.toString());
+ });
+
+ content.addView(button, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ overlapViews(mSurfaceView, content);
+
+ setContentView(content);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.RED);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index de9bbb6ef9fa..4fa6fbe1d4e1 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -13,15 +13,19 @@ android_test {
"src/**/*.java",
"src/**/*.kt",
],
+ kotlincflags: [
+ "-Werror",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "mockito-target-minus-junit4",
"services.core.unboosted",
"testables",
"truth-prebuilt",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
test_suites: ["device-tests"],
}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 1d65cc35c3bc..d185ee6ae116 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -27,14 +27,15 @@ import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLI
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
-import android.support.test.uiautomator.By
-import android.support.test.uiautomator.UiDevice
-import android.support.test.uiautomator.UiObject2
-import android.support.test.uiautomator.Until
import android.testing.PollingCheck
import android.view.InputDevice
import android.view.MotionEvent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
@@ -73,7 +74,7 @@ class AnrTest {
val contentResolver = instrumentation.targetContext.contentResolver
hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
- PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName()
+ PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName()
}
@After
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 63500774816a..d075b5f01ef9 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -17,8 +17,8 @@
package android.view;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import android.hardware.input.HostUsiVersion;
import android.os.Parcel;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -55,11 +55,15 @@ public class InputDeviceTest {
assertEquals(device.isExternal(), outDevice.isExternal());
assertEquals(device.getSources(), outDevice.getSources());
assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
+ assertEquals(device.getKeyboardLanguageTag(), outDevice.getKeyboardLanguageTag());
+ assertEquals(device.getKeyboardLayoutType(), outDevice.getKeyboardLayoutType());
assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
+ assertEquals(device.getHostUsiVersion(), outDevice.getHostUsiVersion());
+ assertEquals(device.getAssociatedDisplayId(), outDevice.getAssociatedDisplayId());
KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
- assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap));
+ assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);
for (int j = 0; j < device.getMotionRanges().size(); j++) {
assertMotionRangeEquals(device.getMotionRanges().get(j),
@@ -68,12 +72,39 @@ public class InputDeviceTest {
}
private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
- final InputDevice device =
- new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
- 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
- 0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
- false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
- true /* hasSensor */, false /* hasBattery */);
+ final InputDevice.Builder deviceBuilder = new InputDevice.Builder()
+ .setId(DEVICE_ID)
+ .setGeneration(42)
+ .setControllerNumber(43)
+ .setName("Test Device " + DEVICE_ID)
+ .setVendorId(44)
+ .setProductId(45)
+ .setDescriptor("descriptor")
+ .setExternal(true)
+ .setSources(InputDevice.SOURCE_HDMI)
+ .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC)
+ .setKeyCharacterMap(keyCharacterMap)
+ .setHasVibrator(true)
+ .setHasMicrophone(true)
+ .setHasButtonUnderPad(true)
+ .setHasSensor(true)
+ .setHasBattery(true)
+ .setKeyboardLanguageTag("en-US")
+ .setKeyboardLayoutType("qwerty")
+ .setUsiVersion(new HostUsiVersion(2, 0));
+
+ for (int i = 0; i < 30; i++) {
+ deviceBuilder.addMotionRange(
+ MotionEvent.AXIS_GENERIC_1,
+ InputDevice.SOURCE_UNKNOWN,
+ i,
+ i + 1,
+ i + 2,
+ i + 3,
+ i + 4);
+ }
+
+ final InputDevice device = deviceBuilder.build();
Parcel parcel = Parcel.obtain();
device.writeToParcel(parcel, 0);
diff --git a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
new file mode 100644
index 000000000000..24a567130ff0
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.input
+
+import android.content.Context
+import android.content.res.Resources
+import android.os.SystemProperties
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_MOVE
+import android.view.MotionEvent.PointerCoords
+import android.view.MotionEvent.PointerProperties
+import android.view.MotionPredictor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
+
+import java.time.Duration
+
+private fun getStylusMotionEvent(
+ eventTime: Duration,
+ action: Int,
+ x: Float,
+ y: Float,
+ ): MotionEvent{
+ val pointerCount = 1
+ val properties = arrayOfNulls<MotionEvent.PointerProperties>(pointerCount)
+ val coords = arrayOfNulls<MotionEvent.PointerCoords>(pointerCount)
+
+ for (i in 0 until pointerCount) {
+ properties[i] = PointerProperties()
+ properties[i]!!.id = i
+ properties[i]!!.toolType = MotionEvent.TOOL_TYPE_STYLUS
+ coords[i] = PointerCoords()
+ coords[i]!!.x = x
+ coords[i]!!.y = y
+ }
+
+ return MotionEvent.obtain(/*downTime=*/0, eventTime.toMillis(), action, properties.size,
+ properties, coords, /*metaState=*/0, /*buttonState=*/0,
+ /*xPrecision=*/0f, /*yPrecision=*/0f, /*deviceId=*/0, /*edgeFlags=*/0,
+ InputDevice.SOURCE_STYLUS, /*flags=*/0)
+}
+
+private fun getPredictionContext(offset: Duration, enablePrediction: Boolean): Context {
+ val context = mock(Context::class.java)
+ val resources: Resources = mock(Resources::class.java)
+ `when`(context.getResources()).thenReturn(resources)
+ `when`(resources.getInteger(
+ com.android.internal.R.integer.config_motionPredictionOffsetNanos)).thenReturn(
+ offset.toNanos().toInt())
+ `when`(resources.getBoolean(
+ com.android.internal.R.bool.config_enableMotionPrediction)).thenReturn(enablePrediction)
+ return context
+}
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MotionPredictorTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ val initialPropertyValue = SystemProperties.get("persist.input.enable_motion_prediction")
+
+ @Before
+ fun setUp() {
+ instrumentation.uiAutomation.executeShellCommand(
+ "setprop persist.input.enable_motion_prediction true")
+ }
+
+ @After
+ fun tearDown() {
+ instrumentation.uiAutomation.executeShellCommand(
+ "setprop persist.input.enable_motion_prediction $initialPropertyValue")
+ }
+
+ /**
+ * In a typical usage, app will send the event to the predictor and then call .predict to draw
+ * a prediction. Here, we send 2 events to the predictor and check the returned event.
+ * Input:
+ * t = 0 x = 0 y = 0
+ * t = 4 x = 10 y = 20
+ * Output (expected):
+ * t = 12 x = 30 y = 60 ± error
+ *
+ * Historical data is ignored for simplicity.
+ */
+ @Test
+ fun testPredictedCoordinatesAndTime() {
+ val context = getPredictionContext(
+ /*offset=*/Duration.ofMillis(1), /*enablePrediction=*/true)
+ val predictor = MotionPredictor(context)
+ var eventTime = Duration.ofMillis(0)
+ val downEvent = getStylusMotionEvent(eventTime, ACTION_DOWN, /*x=*/0f, /*y=*/0f)
+ // ACTION_DOWN t=0 x=0 y=0
+ predictor.record(downEvent)
+
+ eventTime += Duration.ofMillis(4)
+ val moveEvent = getStylusMotionEvent(eventTime, ACTION_MOVE, /*x=*/10f, /*y=*/20f)
+ // ACTION_MOVE t=1 x=1 y=2
+ predictor.record(moveEvent)
+
+ val predicted = predictor.predict(Duration.ofMillis(8).toNanos())
+ assertNotNull(predicted)
+
+ // Prediction will happen for t=12 (since it is the next input interval after the requested
+ // time, 8, plus the model offset, 1).
+ assertEquals(12, predicted!!.eventTime)
+ assertEquals(30f, predicted.x, /*delta=*/5f)
+ assertEquals(60f, predicted.y, /*delta=*/15f)
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
index d83a4570fedc..3a24406e2b73 100644
--- a/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
+++ b/tests/Input/src/com/android/test/input/UnresponsiveGestureMonitorActivity.kt
@@ -45,7 +45,8 @@ class UnresponsiveGestureMonitorActivity : Activity() {
private lateinit var mInputMonitor: InputMonitor
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- mInputMonitor = InputManager.getInstance().monitorGestureInput(MONITOR_NAME, displayId)
+ val inputManager = getSystemService(InputManager::class.java)
+ mInputMonitor = inputManager.monitorGestureInput(MONITOR_NAME, displayId)
mInputEventReceiver = UnresponsiveReceiver(
mInputMonitor.getInputChannel(), Looper.myLooper())
}
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
index 0ad38768238a..84845c69fb27 100644
--- a/tests/InputMethodStressTest/Android.bp
+++ b/tests/InputMethodStressTest/Android.bp
@@ -30,7 +30,9 @@ android_test {
],
test_suites: [
"general-tests",
- "vts",
],
- sdk_version: "31",
+ data: [
+ ":SimpleTestIme",
+ ],
+ sdk_version: "current",
}
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
index f5fe8f2e8e76..e890c9974882 100644
--- a/tests/InputMethodStressTest/AndroidManifest.xml
+++ b/tests/InputMethodStressTest/AndroidManifest.xml
@@ -16,11 +16,10 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.inputmethod.stresstest">
-
+ package="com.android.inputmethod.stresstest">
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application>
- <activity android:name=".AutoShowTest$TestActivity"/>
- <activity android:name=".ImeOpenCloseStressTest$TestActivity"/>
+ <activity android:name=".ImeStressTestUtil$TestActivity"/>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml
index 9ac41351f684..bfebb42ad244 100644
--- a/tests/InputMethodStressTest/AndroidTest.xml
+++ b/tests/InputMethodStressTest/AndroidTest.xml
@@ -19,12 +19,14 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<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="test-file-name" value="SimpleTestIme.apk" />
<option name="test-file-name" value="InputMethodStressTest.apk" />
</target_preparer>
diff --git a/tests/InputMethodStressTest/TEST_MAPPING b/tests/InputMethodStressTest/TEST_MAPPING
index ad07205ab02d..06e2ce84eb0f 100644
--- a/tests/InputMethodStressTest/TEST_MAPPING
+++ b/tests/InputMethodStressTest/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "presubmit": [
+ "presubmit-large": [
{
"name": "InputMethodStressTest"
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
index c84c2bcf19c6..807f0c63668c 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -16,77 +16,460 @@
package com.android.inputmethod.stresstest;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
-
-import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.UNFOCUSABLE_VIEW;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
-import android.app.Activity;
+import static com.google.common.truth.Truth.assertThat;
+
import android.app.Instrumentation;
import android.content.Intent;
-import android.os.Bundle;
+import android.content.res.Configuration;
+import android.os.SystemClock;
import android.platform.test.annotations.RootPermissionTest;
import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.view.WindowManager;
import android.widget.EditText;
-import android.widget.LinearLayout;
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Collections;
+import java.util.List;
+/**
+ * Tests to verify the "auto-show" behavior in {@code InputMethodManagerService} when the window
+ * gaining the focus to start the input.
+ */
@RootPermissionTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public final class AutoShowTest {
- @Rule
- public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
-
- @Rule
- public ScreenCaptureRule mScreenCaptureRule =
+ @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+ @Rule(order = 1) public ImeStressTestRule mImeStressTestRule =
+ new ImeStressTestRule(true /* useSimpleTestIme */);
+ @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule =
new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+ @Parameterized.Parameters(
+ name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}")
+ public static List<Object[]> windowAndSoftInputFlagParameters() {
+ return getWindowAndSoftInputFlagParameters();
+ }
+
+ private final int mSoftInputFlags;
+ private final int mWindowFocusFlags;
+ private final Instrumentation mInstrumentation;
+ private final boolean mIsLargeScreen;
+
+ public AutoShowTest(int windowFocusFlags, int softInputVisibility, int softInputAdjustment) {
+ mSoftInputFlags = softInputVisibility | softInputAdjustment;
+ mWindowFocusFlags = windowFocusFlags;
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mIsLargeScreen = mInstrumentation.getContext().getResources()
+ .getConfiguration().isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE);
+ }
+
+ /**
+ * Test auto-show IME behavior when the {@link EditText} is focusable ({@link
+ * EditText#isFocusableInTouchMode} is {@code true}) and has called {@link
+ * EditText#requestFocus}.
+ */
+ @Test
+ public void autoShow_hasFocusedView_requestFocus() {
+ // request focus at onCreate()
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
+
+ verifyAutoShowBehavior_forwardWithKeyboardOff(activity);
+ }
+
+ /**
+ * Test auto-show IME behavior when the {@link EditText} is focusable ({@link
+ * EditText#isFocusableInTouchMode} is {@code true}) and {@link EditText#requestFocus} is not
+ * called. The IME should never be shown because there is no focused editor in the window.
+ */
+ @Test
+ public void autoShow_hasFocusedView_notRequestFocus() {
+ // request focus not set
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ EditText editText = activity.getEditText();
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus.
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+ } else {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+ }
+ // IME is always hidden because there is no view focus.
+ verifyImeIsAlwaysHidden(editText);
+ }
+
+ /**
+ * Test auto-show IME behavior when the {@link EditText} is not focusable ({@link
+ * EditText#isFocusableInTouchMode} is {@code false}) and {@link EditText#requestFocus} is not
+ * called. The IME should never be shown because there is no focusable editor in the window.
+ */
@Test
- public void autoShow() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent()
- .setAction(Intent.ACTION_MAIN)
- .setClass(instrumentation.getContext(), TestActivity.class)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- TestActivity activity = (TestActivity) instrumentation.startActivitySync(intent);
+ public void autoShow_notFocusedView_notRequestFocus() {
+ // Unfocusable view, request focus not set
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(UNFOCUSABLE_VIEW));
+ TestActivity activity = TestActivity.start(intent);
EditText editText = activity.getEditText();
- waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
- waitOnMainUntilImeIsShown(editText);
- }
-
- public static class TestActivity extends Activity {
- private EditText mEditText;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // IME will be auto-shown if the following conditions are met:
- // 1. SoftInputMode state is SOFT_INPUT_STATE_UNSPECIFIED.
- // 2. SoftInputMode adjust is SOFT_INPUT_ADJUST_RESIZE.
- getWindow().setSoftInputMode(SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE);
- LinearLayout rootView = new LinearLayout(this);
- rootView.setOrientation(LinearLayout.VERTICAL);
- mEditText = new EditText(this);
- rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
- setContentView(rootView);
- // 3. The focused view is a text editor (View#onCheckIsTextEditor() returns true).
- mEditText.requestFocus();
+
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus.
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+ } else {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
}
+ // IME is always hidden because there is no focused view.
+ verifyImeIsAlwaysHidden(editText);
+ }
+
+ /**
+ * Test auto-show IME behavior when the activity is navigated forward from another activity with
+ * keyboard off.
+ */
+ @Test
+ public void autoShow_forwardWithKeyboardOff() {
+ // Create first activity with keyboard off
+ Intent intent1 =
+ createIntent(
+ 0x0 /* No window focus flags */,
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+ Collections.emptyList());
+ TestActivity firstActivity = TestActivity.start(intent1);
+
+ // Create second activity with parameterized flags:
+ Intent intent2 =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+
+ // The auto-show behavior should be the same as opening the app
+ verifyAutoShowBehavior_forwardWithKeyboardOff(secondActivity);
+ }
+
+ /**
+ * Test auto-show IME behavior when the activity is navigated forward from another activity with
+ * keyboard on.
+ */
+ @Test
+ public void autoShow_forwardWithKeyboardOn() {
+ // Create first activity with keyboard on
+ Intent intent1 =
+ createIntent(
+ 0x0 /* No window focus flags */,
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity firstActivity = TestActivity.start(intent1);
+ // Show Ime with InputMethodManager to ensure the keyboard is on.
+ callOnMainSync(firstActivity::showImeWithInputMethodManager);
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+
+ // Create second activity with parameterized flags:
+ Intent intent2 =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+
+ // The auto-show behavior should be the same as open app
+ verifyAutoShowBehavior_forwardWithKeyboardOn(secondActivity);
+ }
+
+ /**
+ * Test auto-show IME behavior when the activity is navigated back from another activity with
+ * keyboard off.
+ */
+ @Test
+ public void autoShow_backwardWithKeyboardOff() {
+ // Not request focus at onCreate() to avoid triggering auto-show behavior
+ Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity firstActivity = TestActivity.start(intent1);
+ // Request view focus after app starts
+ mInstrumentation.runOnMainSync(firstActivity::requestFocus);
+
+ Intent intent2 =
+ createIntent(
+ 0x0 /* No window focus flags */,
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+ Collections.emptyList());
+ TestActivity secondActivity = firstActivity.startSecondTestActivity(intent2);
+ secondActivity.finish();
+ mInstrumentation.waitForIdleSync();
+
+ // When activity is navigated back from another activity with keyboard off, the keyboard
+ // will not show except when soft input visibility flag is SOFT_INPUT_STATE_ALWAYS_VISIBLE.
+ verifyAutoShowBehavior_backwardWithKeyboardOff(firstActivity);
+ }
+
+ /**
+ * Test auto-show IME behavior when the activity is navigated back from another activity with
+ * keyboard on.
+ */
+ @Test
+ public void autoShow_backwardWithKeyboardOn() {
+ // Not request focus at onCreate() to avoid triggering auto-show behavior
+ Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent1);
+ // Request view focus after app starts
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ // Create second TestActivity
+ Intent intent2 =
+ createIntent(
+ 0x0 /* No window focus flags */,
+ WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED
+ | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ ImeStressTestUtil.TestActivity secondActivity = activity.startSecondTestActivity(intent2);
+ // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+ callOnMainSync(secondActivity::showImeWithInputMethodManager);
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ // Close the second activity
+ secondActivity.finish();
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ // When activity is navigated back from another activity with keyboard on, the keyboard
+ // will not hide except when soft input visibility flag is SOFT_INPUT_STATE_ALWAYS_HIDDEN.
+ verifyAutoShowBehavior_backwardWithKeyboardOn(activity);
+ }
+
+ @Test
+ public void clickFocusableView_requestFocus() {
+ if ((mWindowFocusFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // UiAutomator cannot get UiObject if FLAG_NOT_FOCUSABLE is set
+ return;
+ }
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request view focus after app starts
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ // Find the editText and click it
+ UiObject2 editTextUiObject =
+ UiDevice.getInstance(mInstrumentation)
+ .wait(Until.findObject(By.clazz(EditText.class)), 5000);
+ assertThat(editTextUiObject).isNotNull();
+ editTextUiObject.click();
+
+ // Ime will show unless window flag is set
+ verifyClickBehavior(activity);
+ }
+
+ @Test
+ public void clickFocusableView_notRequestFocus() {
+ if ((mWindowFocusFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // UiAutomator cannot get UiObject if FLAG_NOT_FOCUSABLE is set
+ return;
+ }
+ // Not request focus
+ Intent intent1 = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent1);
+
+ // Find the editText and click it
+ UiObject2 editTextUiObject =
+ UiDevice.getInstance(mInstrumentation)
+ .wait(Until.findObject(By.clazz(EditText.class)), 5000);
+ assertThat(editTextUiObject).isNotNull();
+ editTextUiObject.click();
+
+ // Ime will show unless window flag is set
+ verifyClickBehavior(activity);
+ }
+
+ private void verifyAutoShowBehavior_forwardWithKeyboardOff(TestActivity activity) {
+ if (hasUnfocusableWindowFlags(activity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+ return;
+ }
+
+ int softInputMode = activity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ int softInputAdjustment = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+ EditText editText = activity.getEditText();
+
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ switch (softInputVisibility) {
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: {
+ // IME will be auto-shown if softInputMode is set with flag:
+ // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
+ waitOnMainUntilImeIsShown(editText);
+ break;
+ }
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: {
+ // IME will be not be auto-shown if softInputMode is set with flag:
+ // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN,
+ // or stay unchanged if set SOFT_INPUT_STATE_UNCHANGED
+ verifyImeIsAlwaysHidden(editText);
+ break;
+ }
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
+ if ((softInputAdjustment
+ == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen) {
+ // The current system behavior will choose to show IME automatically when
+ // navigating forward to an app that has no visibility state specified
+ // (i.e. SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE
+ // flag or running on a large screen device.
+ waitOnMainUntilImeIsShown(editText);
+ } else {
+ verifyImeIsAlwaysHidden(editText);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ private void verifyAutoShowBehavior_forwardWithKeyboardOn(TestActivity activity) {
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ int softInputMode = activity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ int softInputAdjustment = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+ EditText editText = activity.getEditText();
+
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus. The IME
+ // will always be hidden even though the view can get focus itself.
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ true);
+ // TODO(b/252192121): Ime should be hidden but is shown.
+ // waitOnMainUntilImeIsHidden(editText);
+ return;
+ } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+ || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+ // When FLAG_ALT_FOCUSABLE_IM or FLAG_LOCAL_FOCUS_MODE is set, the view can gain both
+ // window focus and view focus but not IME focus. The IME will always be hidden.
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ // TODO(b/252192121): Ime should be hidden but is shown.
+ // waitOnMainUntilImeIsHidden(editText);
+ return;
+ }
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ switch (softInputVisibility) {
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: {
+ // IME will be auto-shown if softInputMode is set with flag:
+ // SOFT_INPUT_STATE_VISIBLE or SOFT_INPUT_STATE_ALWAYS_VISIBLE
+ waitOnMainUntilImeIsShown(editText);
+ break;
+ }
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN:
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: {
+ // IME will be not be auto-shown if softInputMode is set with flag:
+ // SOFT_INPUT_STATE_HIDDEN or SOFT_INPUT_STATE_ALWAYS_HIDDEN
+ // or stay unchanged if set SOFT_INPUT_STATE_UNCHANGED
+ verifyImeIsAlwaysHidden(editText);
+ break;
+ }
+ case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: {
+ if ((softInputAdjustment
+ == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) || mIsLargeScreen) {
+ // The current system behavior will choose to show IME automatically when
+ // navigating forward to an app that has no visibility state specified (i.e.
+ // SOFT_INPUT_STATE_UNSPECIFIED) with set SOFT_INPUT_ADJUST_RESIZE flag or
+ // running on a large screen device.
+ waitOnMainUntilImeIsShown(editText);
+ } else {
+ verifyImeIsAlwaysHidden(editText);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ private static void verifyAutoShowBehavior_backwardWithKeyboardOff(TestActivity activity) {
+ if (hasUnfocusableWindowFlags(activity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+ return;
+ }
+ int softInputMode = activity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ EditText editText = activity.getEditText();
+
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE) {
+ waitOnMainUntilImeIsShown(editText);
+ } else {
+ verifyImeIsAlwaysHidden(editText);
+ }
+ }
+
+ private static void verifyAutoShowBehavior_backwardWithKeyboardOn(TestActivity activity) {
+ if (hasUnfocusableWindowFlags(activity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+ return;
+ }
+ int softInputMode = activity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+ EditText editText = activity.getEditText();
+
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+ verifyImeIsAlwaysHidden(editText);
+ } else {
+ waitOnMainUntilImeIsShown(editText);
+ }
+ }
+
+ private static void verifyClickBehavior(TestActivity activity) {
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ EditText editText = activity.getEditText();
- public EditText getEditText() {
- return mEditText;
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+ || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+ verifyImeIsAlwaysHidden(editText);
+ } else {
+ waitOnMainUntilImeIsShown(editText);
}
}
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
new file mode 100644
index 000000000000..0c267b27490b
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.inputmethod.stresstest;
+
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
+
+import android.content.Intent;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.rule.UnlockScreenRule;
+import android.widget.EditText;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Test IME visibility by using system default IME to ensure the behavior is consistent
+ * across Android platform versions.
+ */
+@RootPermissionTest
+@RunWith(Parameterized.class)
+public final class DefaultImeVisibilityTest {
+
+ @Rule(order = 0)
+ public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+ // Use system default IME for test.
+ @Rule(order = 1)
+ public ImeStressTestRule mImeStressTestRule =
+ new ImeStressTestRule(false /* useSimpleTestIme */);
+
+ @Rule(order = 2)
+ public ScreenCaptureRule mScreenCaptureRule =
+ new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+
+ private static final int NUM_TEST_ITERATIONS = 10;
+
+ @Parameterized.Parameters(name = "isPortrait={0}")
+ public static List<Boolean> isPortraitCases() {
+ // Test in both portrait and landscape mode.
+ return Arrays.asList(true, false);
+ }
+
+ public DefaultImeVisibilityTest(boolean isPortrait) {
+ mImeStressTestRule.setIsPortrait(isPortrait);
+ }
+
+ @Test
+ public void showHideDefaultIme() {
+ Intent intent =
+ createIntent(
+ 0x0, /* No window focus flags */
+ SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ ImeStressTestUtil.TestActivity activity = ImeStressTestUtil.TestActivity.start(intent);
+ EditText editText = activity.getEditText();
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ callOnMainSync(activity::showImeWithInputMethodManager);
+ verifyWindowAndViewFocus(editText, true, true);
+ waitOnMainUntilImeIsShown(editText);
+
+ callOnMainSync(activity::hideImeWithInputMethodManager);
+ waitOnMainUntilImeIsHidden(editText);
+ }
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
index 14c5cb4bb1a9..5c0212400ff1 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -16,226 +16,579 @@
package com.android.inputmethod.stresstest;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_HIDE_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.INPUT_METHOD_MANAGER_SHOW_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.getWindowAndSoftInputFlagParameters;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.hasUnfocusableWindowFlags;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeAlwaysHiddenWithWindowFlagSet;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyImeIsAlwaysHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.verifyWindowAndViewFocus;
import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
+
+import static com.google.common.truth.Truth.assertThat;
-import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
-import android.os.Bundle;
+import android.os.Build;
import android.os.SystemClock;
import android.platform.test.annotations.RootPermissionTest;
import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.UiDevice;
import android.util.Log;
-import android.view.WindowInsets;
-import android.view.WindowInsetsAnimation;
-import android.view.inputmethod.InputMethodManager;
+import android.view.WindowManager;
import android.widget.EditText;
-import android.widget.LinearLayout;
-import androidx.annotation.Nullable;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
-import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
@RootPermissionTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public final class ImeOpenCloseStressTest {
private static final String TAG = "ImeOpenCloseStressTest";
private static final int NUM_TEST_ITERATIONS = 10;
- @Rule
- public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
-
- @Rule
- public ScreenCaptureRule mScreenCaptureRule =
+ @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+ @Rule(order = 1) public ImeStressTestRule mImeStressTestRule =
+ new ImeStressTestRule(true /* useSimpleTestIme */);
+ @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule =
new ScreenCaptureRule("/sdcard/InputMethodStressTest");
- private Instrumentation mInstrumentation;
- @Before
- public void setUp() {
+ private final Instrumentation mInstrumentation;
+ private final int mSoftInputFlags;
+ private final int mWindowFocusFlags;
+
+ @Parameterized.Parameters(
+ name = "windowFocusFlags={0}, softInputVisibility={1}, softInputAdjustment={2}")
+ public static List<Object[]> windowAndSoftInputFlagParameters() {
+ return getWindowAndSoftInputFlagParameters();
+ }
+
+ public ImeOpenCloseStressTest(
+ int windowFocusFlags, int softInputVisibility, int softInputAdjustment) {
+ mSoftInputFlags = softInputVisibility | softInputAdjustment;
+ mWindowFocusFlags = windowFocusFlags;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
@Test
- public void testShowHide_waitingVisibilityChange() {
- TestActivity activity = TestActivity.start();
- EditText editText = activity.getEditText();
- waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
- for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ public void testShowHideWithInputMethodManager_waitingVisibilityChange() {
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+ // Test only once if window flags set to save time.
+ int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
+ for (int i = 0; i < iterNum; i++) {
String msgPrefix = "Iteration #" + i + " ";
Log.i(TAG, msgPrefix + "start");
- mInstrumentation.runOnMainSync(activity::showIme);
- waitOnMainUntil(msgPrefix + "IME should be visible", () -> isImeShown(editText));
- mInstrumentation.runOnMainSync(activity::hideIme);
- waitOnMainUntil(msgPrefix + "IME should be hidden", () -> !isImeShown(editText));
+ callOnMainSync(activity::showImeWithInputMethodManager);
+ verifyShowBehavior(activity);
+
+ callOnMainSync(activity::hideImeWithInputMethodManager);
+
+ verifyHideBehavior(activity);
}
}
@Test
- public void testShowHide_waitingAnimationEnd() {
- TestActivity activity = TestActivity.start();
+ public void testShowHideWithInputMethodManager_waitingAnimationEnd() {
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
activity.enableAnimationMonitoring();
EditText editText = activity.getEditText();
- waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
String msgPrefix = "Iteration #" + i + " ";
Log.i(TAG, msgPrefix + "start");
- mInstrumentation.runOnMainSync(activity::showIme);
- waitOnMainUntil(msgPrefix + "IME should be visible",
+ callOnMainSync(activity::showImeWithInputMethodManager);
+ waitOnMainUntil(
+ msgPrefix + "IME should be visible",
() -> !activity.isAnimating() && isImeShown(editText));
- mInstrumentation.runOnMainSync(activity::hideIme);
- waitOnMainUntil(msgPrefix + "IME should be hidden",
+
+ callOnMainSync(activity::hideImeWithInputMethodManager);
+ waitOnMainUntil(
+ msgPrefix + "IME should be hidden",
() -> !activity.isAnimating() && !isImeShown(editText));
}
}
@Test
- public void testShowHide_intervalAfterHide() {
+ public void testShowHideWithInputMethodManager_intervalAfterHide() {
// Regression test for b/221483132
- TestActivity activity = TestActivity.start();
- EditText editText = activity.getEditText();
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
// Intervals = 10, 20, 30, ..., 100, 150, 200, ...
List<Integer> intervals = new ArrayList<>();
for (int i = 10; i < 100; i += 10) intervals.add(i);
for (int i = 100; i < 1000; i += 50) intervals.add(i);
- waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
for (int intervalMillis : intervals) {
String msgPrefix = "Interval = " + intervalMillis + " ";
Log.i(TAG, msgPrefix + " start");
- mInstrumentation.runOnMainSync(activity::hideIme);
+ callOnMainSync(activity::hideImeWithInputMethodManager);
SystemClock.sleep(intervalMillis);
- mInstrumentation.runOnMainSync(activity::showIme);
- waitOnMainUntil(msgPrefix + "IME should be visible",
- () -> isImeShown(editText));
+
+ callOnMainSync(activity::showImeWithInputMethodManager);
+ verifyShowBehavior(activity);
}
}
- @Ignore("b/268556567")
@Test
- public void testShowHideInSameFrame() {
- TestActivity activity = TestActivity.start();
+ public void testShowHideWithInputMethodManager_inSameFrame() {
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
+ // hidden -> show -> hide
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Log.i(TAG, "Calling showIme() and hideIme()");
+ activity.showImeWithInputMethodManager();
+ activity.hideImeWithInputMethodManager();
+ });
+ // Wait until IMMS / IMS handles messages.
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ verifyHideBehavior(activity);
+
+ mInstrumentation.runOnMainSync(activity::showImeWithInputMethodManager);
+ verifyShowBehavior(activity);
+ mInstrumentation.waitForIdleSync();
+
+ // shown -> hide -> show
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Log.i(TAG, "Calling hideIme() and showIme()");
+ activity.hideImeWithInputMethodManager();
+ activity.showImeWithInputMethodManager();
+ });
+ // Wait until IMMS / IMS handles messages.
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ verifyShowBehavior(activity);
+ }
+
+ /**
+ * Test IME hidden by calling show and hide IME consecutively with
+ * {@link android.view.inputmethod.InputMethodManager} APIs in
+ * {@link android.app.Activity#onCreate}.
+ *
+ * <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED}
+ * window flag to avoid some softInputMode visibility flags may take presence over
+ * {@link android.view.inputmethod.InputMethodManager} APIs (e.g. use showSoftInput to show
+ * IME in {@link android.app.Activity#onCreate} but being hidden by
+ * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN} window flag after the
+ * activity window focused).</p>
+ */
+ @Test
+ public void testShowHideWithInputMethodManager_onCreate() {
+ if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) {
+ return;
+ }
+ // Show and hide with InputMethodManager at onCreate()
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Arrays.asList(
+ REQUEST_FOCUS_ON_CREATE,
+ INPUT_METHOD_MANAGER_SHOW_ON_CREATE,
+ INPUT_METHOD_MANAGER_HIDE_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
+
+ verifyHideBehavior(activity);
+ }
+
+ @Test
+ public void testShowWithInputMethodManager_notRequestFocus() {
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+
+ // Show InputMethodManager without requesting focus
+ callOnMainSync(activity::showImeWithInputMethodManager);
+
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ EditText editText = activity.getEditText();
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+ } else {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+ }
+ // The Ime should always be hidden because view never gains focus.
+ verifyImeIsAlwaysHidden(editText);
+ }
+
+ @Test
+ public void testShowHideWithWindowInsetsController_waitingVisibilityChange() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+ // Test only once if window flags set to save time.
+ int iterNum = hasUnfocusableWindowFlags(activity) ? 1 : NUM_TEST_ITERATIONS;
+ for (int i = 0; i < iterNum; i++) {
+ String msgPrefix = "Iteration #" + i + " ";
+ Log.i(TAG, msgPrefix + "start");
+ mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+ verifyShowBehavior(activity);
+ mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+ verifyHideBehavior(activity);
+ }
+ }
+
+ @Test
+ public void testShowHideWithWindowInsetsController_waitingAnimationEnd() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
activity.enableAnimationMonitoring();
EditText editText = activity.getEditText();
- waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
+ for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
+ String msgPrefix = "Iteration #" + i + " ";
+ Log.i(TAG, msgPrefix + "start");
+ mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+ waitOnMainUntil(
+ msgPrefix + "IME should be visible",
+ () -> !activity.isAnimating() && isImeShown(editText));
+
+ mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+ waitOnMainUntil(
+ msgPrefix + "IME should be hidden",
+ () -> !activity.isAnimating() && !isImeShown(editText));
+ }
+ }
+ @Test
+ public void testShowHideWithWindowInsetsController_intervalAfterHide() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
+ // Intervals = 10, 20, 30, ..., 100, 150, 200, ...
+ List<Integer> intervals = new ArrayList<>();
+ for (int i = 10; i < 100; i += 10) intervals.add(i);
+ for (int i = 100; i < 1000; i += 50) intervals.add(i);
+ for (int intervalMillis : intervals) {
+ String msgPrefix = "Interval = " + intervalMillis + " ";
+ Log.i(TAG, msgPrefix + " start");
+ mInstrumentation.runOnMainSync(activity::hideImeWithWindowInsetsController);
+ SystemClock.sleep(intervalMillis);
+
+ mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+ verifyShowBehavior(activity);
+ }
+ }
+
+ @Test
+ public void testShowHideWithWindowInsetsController_inSameFrame() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ // Request focus after app starts to avoid triggering auto-show behavior.
+ mInstrumentation.runOnMainSync(activity::requestFocus);
+
+ if (hasUnfocusableWindowFlags(activity)) {
+ return; // Skip to save time.
+ }
// hidden -> show -> hide
- mInstrumentation.runOnMainSync(() -> {
- Log.i(TAG, "Calling showIme() and hideIme()");
- activity.showIme();
- activity.hideIme();
- });
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Log.i(TAG, "Calling showIme() and hideIme()");
+ activity.showImeWithWindowInsetsController();
+ activity.hideImeWithWindowInsetsController();
+ });
// Wait until IMMS / IMS handles messages.
SystemClock.sleep(1000);
mInstrumentation.waitForIdleSync();
- waitOnMainUntil("IME should be invisible after show/hide", () -> !isImeShown(editText));
+ verifyHideBehavior(activity);
- mInstrumentation.runOnMainSync(activity::showIme);
- waitOnMainUntil("IME should be visible",
- () -> !activity.isAnimating() && isImeShown(editText));
+ mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+ verifyShowBehavior(activity);
mInstrumentation.waitForIdleSync();
// shown -> hide -> show
- mInstrumentation.runOnMainSync(() -> {
- Log.i(TAG, "Calling hideIme() and showIme()");
- activity.hideIme();
- activity.showIme();
- });
+ mInstrumentation.runOnMainSync(
+ () -> {
+ Log.i(TAG, "Calling hideIme() and showIme()");
+ activity.hideImeWithWindowInsetsController();
+ activity.showImeWithWindowInsetsController();
+ });
// Wait until IMMS / IMS handles messages.
SystemClock.sleep(1000);
mInstrumentation.waitForIdleSync();
- waitOnMainUntil("IME should be visible after hide/show",
- () -> !activity.isAnimating() && isImeShown(editText));
+ verifyShowBehavior(activity);
}
- public static class TestActivity extends Activity {
+ @Test
+ public void testShowWithWindowInsetsController_onCreate_requestFocus() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ // Show with InputMethodManager at onCreate()
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Arrays.asList(
+ REQUEST_FOCUS_ON_CREATE, WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
- private EditText mEditText;
- private boolean mIsAnimating;
+ verifyShowBehavior(activity);
+ }
- private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
- new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
- @Override
- public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
- WindowInsetsAnimation.Bounds bounds) {
- mIsAnimating = true;
- return super.onStart(animation, bounds);
- }
+ @Test
+ public void testShowWithWindowInsetsController_onCreate_notRequestFocus() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ // Show and hide with InputMethodManager at onCreate()
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
- @Override
- public void onEnd(WindowInsetsAnimation animation) {
- super.onEnd(animation);
- mIsAnimating = false;
- }
+ // Ime is shown but with a fallback InputConnection
+ verifyShowBehaviorNotRequestFocus(activity);
+ }
- @Override
- public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- return insets;
- }
- };
+ @Test
+ public void testShowWithWindowInsetsController_afterStart_notRequestFocus() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ // Show and hide with InputMethodManager at onCreate()
+ Intent intent = createIntent(mWindowFocusFlags, mSoftInputFlags, Collections.emptyList());
+ TestActivity activity = TestActivity.start(intent);
+ mInstrumentation.runOnMainSync(activity::showImeWithWindowInsetsController);
+
+ // Ime is shown but with a fallback InputConnection
+ verifyShowBehaviorNotRequestFocus(activity);
+ }
- public static TestActivity start() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent()
- .setAction(Intent.ACTION_MAIN)
- .setClass(instrumentation.getContext(), TestActivity.class)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return (TestActivity) instrumentation.startActivitySync(intent);
+ /**
+ * Test IME hidden by calling show and hide IME consecutively with
+ * {@link android.view.WindowInsetsController} APIs in {@link android.app.Activity#onCreate}.
+ *
+ * <p> Note for developers: Use {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_UNCHANGED}
+ * window flag to avoid some softInputMode visibility flags may take presence over
+ * {@link android.view.WindowInsetsController} APIs (e.g. use showSoftInput to show
+ * IME in {@link android.app.Activity#onCreate} but being hidden by
+ * {@link WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN} window flag after the
+ * activity window focused).</p>
+ */
+ @Test
+ public void testHideWithWindowInsetsController_onCreate_requestFocus() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ if (mSoftInputFlags != SOFT_INPUT_STATE_UNCHANGED) {
+ return;
}
+ // Show and hide with InputMethodManager at onCreate()
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Arrays.asList(
+ REQUEST_FOCUS_ON_CREATE,
+ WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE,
+ WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
+
+ verifyHideBehavior(activity);
+ }
+
+ @Test
+ public void testScreenOffOn() throws Exception {
+ Intent intent1 =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent1);
+ // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+ callOnMainSync(activity::showImeWithInputMethodManager);
+
+ Thread.sleep(1000);
+ verifyShowBehavior(activity);
+
+ UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- LinearLayout rootView = new LinearLayout(this);
- rootView.setOrientation(LinearLayout.VERTICAL);
- mEditText = new EditText(this);
- rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
- setContentView(rootView);
+ if (uiDevice.isScreenOn()) {
+ uiDevice.sleep();
}
+ Thread.sleep(1000);
+ if (!uiDevice.isScreenOn()) {
+ uiDevice.wakeUp();
+ }
+
+ verifyShowBehavior(activity);
+ }
+
+ // TODO: Add tests for activities that don't handle the rotation.
+ @Test
+ public void testRotateScreenWithKeyboardOn() throws Exception {
+ Intent intent =
+ createIntent(
+ mWindowFocusFlags,
+ mSoftInputFlags,
+ Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
+ TestActivity activity = TestActivity.start(intent);
+ // Show Ime with InputMethodManager to ensure the keyboard is shown on the second activity
+ callOnMainSync(activity::showImeWithInputMethodManager);
+ Thread.sleep(2000);
+ verifyShowBehavior(activity);
+
+ UiDevice uiDevice = UiDevice.getInstance(mInstrumentation);
+
+ uiDevice.setOrientationRight();
+ uiDevice.waitForIdle();
+ Thread.sleep(1000);
+ Log.i(TAG, "Rotate screen right");
+ assertThat(uiDevice.isNaturalOrientation()).isFalse();
+ verifyRotateBehavior(activity);
- public EditText getEditText() {
- return mEditText;
+ uiDevice.setOrientationLeft();
+ uiDevice.waitForIdle();
+ Thread.sleep(1000);
+ Log.i(TAG, "Rotate screen left");
+ assertThat(uiDevice.isNaturalOrientation()).isFalse();
+ verifyRotateBehavior(activity);
+
+ uiDevice.setOrientationNatural();
+ uiDevice.waitForIdle();
+ }
+
+ private static void verifyShowBehavior(TestActivity activity) {
+ if (hasUnfocusableWindowFlags(activity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+ return;
}
+ EditText editText = activity.getEditText();
- public void showIme() {
- Log.i(TAG, "TestActivity.showIme");
- mEditText.requestFocus();
- InputMethodManager imm = getSystemService(InputMethodManager.class);
- imm.showSoftInput(mEditText, 0);
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ waitOnMainUntilImeIsShown(editText);
+ }
+
+ private static void verifyHideBehavior(TestActivity activity) {
+ if (hasUnfocusableWindowFlags(activity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(activity);
+ return;
}
+ EditText editText = activity.getEditText();
- public void hideIme() {
- Log.i(TAG, "TestActivity.hideIme");
- InputMethodManager imm = getSystemService(InputMethodManager.class);
- imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ verifyWindowAndViewFocus(editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ waitOnMainUntilImeIsHidden(editText);
+ }
+
+ private static void verifyShowBehaviorNotRequestFocus(TestActivity activity) {
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ EditText editText = activity.getEditText();
+
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ false, /*expectViewFocus*/ false);
+ verifyImeIsAlwaysHidden(editText);
+ } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+ || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+ verifyImeIsAlwaysHidden(editText);
+ } else {
+ verifyWindowAndViewFocus(
+ editText, /*expectWindowFocus*/ true, /*expectViewFocus*/ false);
+ // Ime is shown but with a fallback InputConnection
+ waitOnMainUntilImeIsShown(editText);
}
+ }
- public void enableAnimationMonitoring() {
- // Enable WindowInsetsAnimation.
- // Note that this has a side effect of disabling InsetsAnimationThreadControlRunner.
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- getWindow().setDecorFitsSystemWindows(false);
- mEditText.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
- });
+ private static void verifyRotateBehavior(TestActivity activity) {
+ // Get the new TestActivity after recreation.
+ TestActivity newActivity = TestActivity.getLastCreatedInstance();
+ assertThat(newActivity).isNotNull();
+ assertThat(newActivity).isNotEqualTo(activity);
+
+ EditText newEditText = newActivity.getEditText();
+ int softInputMode = newActivity.getWindow().getAttributes().softInputMode;
+ int softInputVisibility = softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+
+ if (hasUnfocusableWindowFlags(newActivity)) {
+ verifyImeAlwaysHiddenWithWindowFlagSet(newActivity);
+ return;
}
- public boolean isAnimating() {
- return mIsAnimating;
+ if (softInputVisibility == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+ // After rotation, the keyboard would be hidden only when the flag is
+ // SOFT_INPUT_STATE_ALWAYS_HIDDEN. However, SOFT_INPUT_STATE_HIDDEN is different because
+ // it requires appending SOFT_INPUT_IS_FORWARD_NAVIGATION flag, which won't be added
+ // when rotating the devices (rotating doesn't navigate forward to the next app window.)
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsHidden(newEditText);
+
+ } else {
+ // Other cases, keyboard would be shown.
+ verifyWindowAndViewFocus(newEditText, /*expectWindowFocus*/ true, /*expectViewFocus*/
+ true);
+ waitOnMainUntilImeIsShown(newEditText);
}
}
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
new file mode 100644
index 000000000000..12104b298dac
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
@@ -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 com.android.inputmethod.stresstest;
+
+import android.app.Instrumentation;
+import android.os.RemoteException;
+import android.support.test.uiautomator.UiDevice;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.IOException;
+
+/**
+ * Do setup and cleanup for Ime stress tests, including disabling lock and auto-rotate screen,
+ * pressing home and enabling a simple test Ime during the tests.
+ */
+public class ImeStressTestRule extends TestWatcher {
+ private static final String LOCK_SCREEN_OFF_COMMAND = "locksettings set-disabled true";
+ private static final String LOCK_SCREEN_ON_COMMAND = "locksettings set-disabled false";
+ private static final String SET_PORTRAIT_MODE_COMMAND = "settings put system user_rotation 0";
+ private static final String SET_LANDSCAPE_MODE_COMMAND = "settings put system user_rotation 1";
+ private static final String SIMPLE_IME_ID =
+ "com.android.apps.inputmethod.simpleime/.SimpleInputMethodService";
+ private static final String ENABLE_IME_COMMAND = "ime enable " + SIMPLE_IME_ID;
+ private static final String SET_IME_COMMAND = "ime set " + SIMPLE_IME_ID;
+ private static final String DISABLE_IME_COMMAND = "ime disable " + SIMPLE_IME_ID;
+ private static final String RESET_IME_COMMAND = "ime reset";
+
+ @NonNull private final Instrumentation mInstrumentation;
+ @NonNull private final UiDevice mUiDevice;
+ // Whether the screen orientation is set to portrait.
+ private boolean mIsPortrait;
+ // Whether to use a simple test Ime or system default Ime for test.
+ private final boolean mUseSimpleTestIme;
+
+ public ImeStressTestRule(boolean useSimpleTestIme) {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mUiDevice = UiDevice.getInstance(mInstrumentation);
+ // Default is portrait mode
+ mIsPortrait = true;
+ mUseSimpleTestIme = useSimpleTestIme;
+ }
+
+ public void setIsPortrait(boolean isPortrait) {
+ mIsPortrait = isPortrait;
+ }
+
+ @Override
+ protected void starting(Description description) {
+ disableLockScreen();
+ setOrientation();
+ mUiDevice.pressHome();
+ if (mUseSimpleTestIme) {
+ enableSimpleIme();
+ } else {
+ resetImeToDefault();
+ }
+
+ mInstrumentation.waitForIdleSync();
+ }
+
+ @Override
+ protected void finished(Description description) {
+ if (mUseSimpleTestIme) {
+ disableSimpleIme();
+ }
+ unfreezeRotation();
+ restoreLockScreen();
+ }
+
+ private void disableLockScreen() {
+ try {
+ executeShellCommand(LOCK_SCREEN_OFF_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not disable lock screen.", e);
+ }
+ }
+
+ private void restoreLockScreen() {
+ try {
+ executeShellCommand(LOCK_SCREEN_ON_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not enable lock screen.", e);
+ }
+ }
+
+ private void setOrientation() {
+ try {
+ mUiDevice.freezeRotation();
+ executeShellCommand(
+ mIsPortrait ? SET_PORTRAIT_MODE_COMMAND : SET_LANDSCAPE_MODE_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not set screen orientation.", e);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not freeze rotation.", e);
+ }
+ }
+
+ private void unfreezeRotation() {
+ try {
+ mUiDevice.unfreezeRotation();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not unfreeze screen rotation.", e);
+ }
+ }
+
+ private void enableSimpleIme() {
+ try {
+ executeShellCommand(ENABLE_IME_COMMAND);
+ executeShellCommand(SET_IME_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not enable SimpleTestIme.", e);
+ }
+ }
+
+ private void disableSimpleIme() {
+ try {
+ executeShellCommand(DISABLE_IME_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not disable SimpleTestIme.", e);
+ }
+ }
+
+ private void resetImeToDefault() {
+ try {
+ executeShellCommand(RESET_IME_COMMAND);
+ } catch (IOException e) {
+ throw new RuntimeException("Could not reset Ime to default.", e);
+ }
+ }
+
+ private @NonNull String executeShellCommand(@NonNull String cmd) throws IOException {
+ return mUiDevice.executeShellCommand(cmd);
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index ba2ba3c75bc2..f3c81942ae42 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -16,15 +16,38 @@
package com.android.inputmethod.stresstest;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
import static com.android.compatibility.common.util.SystemUtil.eventually;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -32,27 +55,96 @@ import java.util.concurrent.atomic.AtomicReference;
/** Utility methods for IME stress test. */
public final class ImeStressTestUtil {
- private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(3);
+
+ private ImeStressTestUtil() {}
+
+ private static final int[] WINDOW_FOCUS_FLAGS =
+ new int[] {
+ LayoutParams.FLAG_NOT_FOCUSABLE,
+ LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ LayoutParams.FLAG_LOCAL_FOCUS_MODE
+ };
+
+ private static final int[] SOFT_INPUT_VISIBILITY_FLAGS =
+ new int[] {
+ LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
+ LayoutParams.SOFT_INPUT_STATE_UNCHANGED,
+ LayoutParams.SOFT_INPUT_STATE_HIDDEN,
+ LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN,
+ LayoutParams.SOFT_INPUT_STATE_VISIBLE,
+ LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE,
+ };
- private ImeStressTestUtil() {
+ private static final int[] SOFT_INPUT_ADJUST_FLAGS =
+ new int[] {
+ LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED,
+ LayoutParams.SOFT_INPUT_ADJUST_RESIZE,
+ LayoutParams.SOFT_INPUT_ADJUST_PAN,
+ LayoutParams.SOFT_INPUT_ADJUST_NOTHING
+ };
+
+ public static final String SOFT_INPUT_FLAGS = "soft_input_flags";
+ public static final String WINDOW_FLAGS = "window_flags";
+ public static final String UNFOCUSABLE_VIEW = "unfocusable_view";
+ public static final String REQUEST_FOCUS_ON_CREATE = "request_focus_on_create";
+ public static final String INPUT_METHOD_MANAGER_SHOW_ON_CREATE =
+ "input_method_manager_show_on_create";
+ public static final String INPUT_METHOD_MANAGER_HIDE_ON_CREATE =
+ "input_method_manager_hide_on_create";
+ public static final String WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE =
+ "window_insets_controller_show_on_create";
+ public static final String WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE =
+ "window_insets_controller_hide_on_create";
+
+ /** Parameters for show/hide ime parameterized tests. */
+ public static ArrayList<Object[]> getWindowAndSoftInputFlagParameters() {
+ ArrayList<Object[]> params = new ArrayList<>();
+
+ // Set different window focus flags and keep soft input flags as default values (4 cases)
+ for (int windowFocusFlags : WINDOW_FOCUS_FLAGS) {
+ params.add(
+ new Object[] {
+ windowFocusFlags,
+ LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED,
+ LayoutParams.SOFT_INPUT_ADJUST_RESIZE
+ });
+ }
+ // Set the combinations of different softInputVisibility, softInputAdjustment flags,
+ // keep the window focus flag as default value ( 6 * 4 = 24 cases)
+ for (int softInputVisibility : SOFT_INPUT_VISIBILITY_FLAGS) {
+ for (int softInputAdjust : SOFT_INPUT_ADJUST_FLAGS) {
+ params.add(
+ new Object[] {
+ 0x0 /* No window focus flags */, softInputVisibility, softInputAdjust
+ });
+ }
+ }
+ return params;
}
/** Checks if the IME is shown on the window that the given view belongs to. */
public static boolean isImeShown(View view) {
WindowInsets insets = view.getRootWindowInsets();
+ if (insets == null) {
+ return false;
+ }
return insets.isVisible(WindowInsets.Type.ime());
}
/** Calls the callable on the main thread and returns the result. */
public static <V> V callOnMainSync(Callable<V> callable) {
AtomicReference<V> result = new AtomicReference<>();
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- try {
- result.set(callable.call());
- } catch (Exception e) {
- throw new RuntimeException("Exception was thrown", e);
- }
- });
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(
+ () -> {
+ try {
+ result.set(callable.call());
+ } catch (Exception e) {
+ throw new RuntimeException("Exception was thrown", e);
+ }
+ });
return result.get();
}
@@ -67,14 +159,335 @@ public final class ImeStressTestUtil {
/** Waits until IME is shown, or throws on timeout. */
public static void waitOnMainUntilImeIsShown(View view) {
- eventually(() -> assertWithMessage("IME should be shown").that(
- callOnMainSync(() -> isImeShown(view))).isTrue(), TIMEOUT);
+ eventually(
+ () ->
+ assertWithMessage("IME should be shown")
+ .that(callOnMainSync(() -> isImeShown(view)))
+ .isTrue(),
+ TIMEOUT);
}
/** Waits until IME is hidden, or throws on timeout. */
public static void waitOnMainUntilImeIsHidden(View view) {
- //eventually(() -> assertThat(callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT);
- eventually(() -> assertWithMessage("IME should be hidden").that(
- callOnMainSync(() -> isImeShown(view))).isFalse(), TIMEOUT);
+ eventually(
+ () ->
+ assertWithMessage("IME should be hidden")
+ .that(callOnMainSync(() -> isImeShown(view)))
+ .isFalse(),
+ TIMEOUT);
+ }
+
+ /** Waits until window get focus, or throws on timeout. */
+ public static void waitOnMainUntilWindowGainsFocus(View view) {
+ eventually(
+ () ->
+ assertWithMessage("Window should gain focus")
+ .that(callOnMainSync(view::hasWindowFocus))
+ .isTrue(),
+ TIMEOUT);
+ }
+
+ /** Waits until view get focus, or throws on timeout. */
+ public static void waitOnMainUntilViewGainsFocus(View view) {
+ eventually(
+ () ->
+ assertWithMessage("View should gain focus")
+ .that(callOnMainSync(view::hasFocus))
+ .isTrue(),
+ TIMEOUT);
+ }
+
+ /** Verify IME is always hidden within the given time duration. */
+ public static void verifyImeIsAlwaysHidden(View view) {
+ always(
+ () ->
+ assertWithMessage("IME should be hidden")
+ .that(callOnMainSync(() -> isImeShown(view)))
+ .isFalse(),
+ TIMEOUT);
+ }
+
+ /** Verify the window never gains focus within the given time duration. */
+ public static void verifyWindowNeverGainsFocus(View view) {
+ always(
+ () ->
+ assertWithMessage("window should never gain focus")
+ .that(callOnMainSync(view::hasWindowFocus))
+ .isFalse(),
+ TIMEOUT);
+ }
+
+ /** Verify the view never gains focus within the given time duration. */
+ public static void verifyViewNeverGainsFocus(View view) {
+ always(
+ () ->
+ assertWithMessage("view should never gain ime focus")
+ .that(callOnMainSync(view::hasFocus))
+ .isFalse(),
+ TIMEOUT);
+ }
+
+ /**
+ * Make sure that a {@link Runnable} always finishes without throwing a {@link Exception} in the
+ * given duration
+ *
+ * @param r The {@link Runnable} to run.
+ * @param timeoutMillis The number of milliseconds to wait for {@code r} to not throw
+ */
+ public static void always(ThrowingRunnable r, long timeoutMillis) {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ r.run();
+ if (System.currentTimeMillis() - start >= timeoutMillis) {
+ return;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ignored) {
+ // Do nothing
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public static boolean hasUnfocusableWindowFlags(Activity activity) {
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ return (windowFlags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0
+ || (windowFlags & LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+ || (windowFlags & LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0;
+ }
+
+ public static void verifyWindowAndViewFocus(
+ View view, boolean expectWindowFocus, boolean expectViewFocus) {
+ if (expectWindowFocus) {
+ waitOnMainUntilWindowGainsFocus(view);
+ } else {
+ verifyWindowNeverGainsFocus(view);
+ }
+ if (expectViewFocus) {
+ waitOnMainUntilViewGainsFocus(view);
+ } else {
+ verifyViewNeverGainsFocus(view);
+ }
+ }
+
+ public static void verifyImeAlwaysHiddenWithWindowFlagSet(TestActivity activity) {
+ int windowFlags = activity.getWindow().getAttributes().flags;
+ View view = activity.getEditText();
+ if ((windowFlags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) {
+ // When FLAG_NOT_FOCUSABLE is set true, the view will never gain window focus. The IME
+ // will always be hidden even though the view can get focus itself.
+ verifyWindowAndViewFocus(view, /*expectWindowFocus*/ false, /*expectViewFocus*/ true);
+ verifyImeIsAlwaysHidden(view);
+ } else if ((windowFlags & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM) != 0
+ || (windowFlags & WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE) != 0) {
+ // When FLAG_ALT_FOCUSABLE_IM or FLAG_LOCAL_FOCUS_MODE is set, the view can gain both
+ // window focus and view focus but not IME focus. The IME will always be hidden.
+ verifyWindowAndViewFocus(view, /*expectWindowFocus*/ true, /*expectViewFocus*/ true);
+ verifyImeIsAlwaysHidden(view);
+ }
+ }
+
+ /** Activity to help test show/hide behavior of IME. */
+ public static class TestActivity extends Activity {
+ private static final String TAG = "ImeStressTestUtil.TestActivity";
+ private EditText mEditText;
+ private boolean mIsAnimating;
+ private static WeakReference<TestActivity> sLastCreatedInstance =
+ new WeakReference<>(null);
+
+ private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ mIsAnimating = true;
+ return super.onStart(animation, bounds);
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ super.onEnd(animation);
+ mIsAnimating = false;
+ }
+
+ @Override
+ public WindowInsets onProgress(
+ WindowInsets insets, List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+ };
+
+ /** Create intent with extras. */
+ public static Intent createIntent(
+ int windowFlags, int softInputFlags, List<String> extras) {
+ Intent intent =
+ new Intent()
+ .putExtra(WINDOW_FLAGS, windowFlags)
+ .putExtra(SOFT_INPUT_FLAGS, softInputFlags);
+ for (String extra : extras) {
+ intent.putExtra(extra, true);
+ }
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate()");
+ sLastCreatedInstance = new WeakReference<>(this);
+ boolean isUnfocusableView = getIntent().getBooleanExtra(UNFOCUSABLE_VIEW, false);
+ boolean requestFocus = getIntent().getBooleanExtra(REQUEST_FOCUS_ON_CREATE, false);
+ int softInputFlags = getIntent().getIntExtra(SOFT_INPUT_FLAGS, 0);
+ int windowFlags = getIntent().getIntExtra(WINDOW_FLAGS, 0);
+ boolean showWithInputMethodManagerOnCreate =
+ getIntent().getBooleanExtra(INPUT_METHOD_MANAGER_SHOW_ON_CREATE, false);
+ boolean hideWithInputMethodManagerOnCreate =
+ getIntent().getBooleanExtra(INPUT_METHOD_MANAGER_HIDE_ON_CREATE, false);
+ boolean showWithWindowInsetsControllerOnCreate =
+ getIntent().getBooleanExtra(WINDOW_INSETS_CONTROLLER_SHOW_ON_CREATE, false);
+ boolean hideWithWindowInsetsControllerOnCreate =
+ getIntent().getBooleanExtra(WINDOW_INSETS_CONTROLLER_HIDE_ON_CREATE, false);
+
+ getWindow().addFlags(windowFlags);
+ getWindow().setSoftInputMode(softInputFlags);
+
+ LinearLayout rootView = new LinearLayout(this);
+ rootView.setOrientation(LinearLayout.VERTICAL);
+ mEditText = new EditText(this);
+ if (isUnfocusableView) {
+ mEditText.setFocusableInTouchMode(false);
+ }
+ rootView.addView(mEditText, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ setContentView(rootView);
+
+ if (requestFocus) {
+ requestFocus();
+ }
+ if (showWithInputMethodManagerOnCreate) {
+ showImeWithInputMethodManager();
+ }
+ if (hideWithInputMethodManagerOnCreate) {
+ hideImeWithInputMethodManager();
+ }
+ if (showWithWindowInsetsControllerOnCreate) {
+ showImeWithWindowInsetsController();
+ }
+ if (hideWithWindowInsetsControllerOnCreate) {
+ hideImeWithWindowInsetsController();
+ }
+ }
+
+ /** Get the last created TestActivity instance. */
+ @Nullable
+ public static TestActivity getLastCreatedInstance() {
+ return sLastCreatedInstance.get();
+ }
+
+ /** Show IME with InputMethodManager. */
+ public boolean showImeWithInputMethodManager() {
+ boolean showResult =
+ getInputMethodManager()
+ .showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
+ if (showResult) {
+ Log.i(TAG, "IMM#showSoftInput successfully");
+ } else {
+ Log.i(TAG, "IMM#showSoftInput failed");
+ }
+ return showResult;
+ }
+
+ /** Hide IME with InputMethodManager. */
+ public boolean hideImeWithInputMethodManager() {
+ boolean hideResult =
+ getInputMethodManager().hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ if (hideResult) {
+ Log.i(TAG, "IMM#hideSoftInput successfully");
+ } else {
+ Log.i(TAG, "IMM#hideSoftInput failed");
+ }
+ return hideResult;
+ }
+
+ /** Show IME with WindowInsetsController */
+ public void showImeWithWindowInsetsController() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Log.i(TAG, "showImeWithWIC()");
+ WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
+ assertWithMessage("WindowInsetsController shouldn't be null.")
+ .that(windowInsetsController)
+ .isNotNull();
+ windowInsetsController.show(WindowInsets.Type.ime());
+ }
+
+ /** Hide IME with WindowInsetsController. */
+ public void hideImeWithWindowInsetsController() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ return;
+ }
+ Log.i(TAG, "hideImeWithWIC()");
+ WindowInsetsController windowInsetsController = mEditText.getWindowInsetsController();
+ assertWithMessage("WindowInsetsController shouldn't be null.")
+ .that(windowInsetsController)
+ .isNotNull();
+ windowInsetsController.hide(WindowInsets.Type.ime());
+ }
+
+ private InputMethodManager getInputMethodManager() {
+ return getSystemService(InputMethodManager.class);
+ }
+
+ public EditText getEditText() {
+ return mEditText;
+ }
+
+ /** Start TestActivity with intent. */
+ public static TestActivity start(Intent intent) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ intent.setAction(Intent.ACTION_MAIN)
+ .setClass(instrumentation.getContext(), TestActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return (TestActivity) instrumentation.startActivitySync(intent);
+ }
+
+ /** Start the second TestActivity with intent. */
+ public TestActivity startSecondTestActivity(Intent intent) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ intent.setClass(TestActivity.this, TestActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return (TestActivity) instrumentation.startActivitySync(intent);
+ }
+
+ public void enableAnimationMonitoring() {
+ // Enable WindowInsetsAnimation.
+ // Note that this has a side effect of disabling InsetsAnimationThreadControlRunner.
+ InstrumentationRegistry.getInstrumentation()
+ .runOnMainSync(
+ () -> {
+ getWindow().setDecorFitsSystemWindows(false);
+ mEditText.setWindowInsetsAnimationCallback(
+ mWindowInsetsAnimationCallback);
+ });
+ }
+
+ public boolean isAnimating() {
+ return mIsAnimating;
+ }
+
+ public void requestFocus() {
+ boolean requestFocusResult = getEditText().requestFocus();
+ if (requestFocusResult) {
+ Log.i(TAG, "Request focus successfully");
+ } else {
+ Log.i(TAG, "Request focus failed");
+ }
+ }
}
}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
index 573b3b695a90..f4a04a163ebb 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -42,6 +42,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import org.junit.After;
@@ -74,13 +75,12 @@ public final class NotificationTest {
// This is for AOSP System UI for phones. When testing customized System UI, please modify here.
private static final BySelector REPLY_SEND_BUTTON_SELECTOR =
- By.res("com.android.systemui", "remote_input_send");
+ By.res("com.android.systemui", "remote_input_send").enabled(true);
- @Rule
- public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
-
- @Rule
- public ScreenCaptureRule mScreenCaptureRule =
+ @Rule(order = 0) public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+ @Rule(order = 1) public ImeStressTestRule mImeStressTestRule =
+ new ImeStressTestRule(true /* useSimpleTestIme */);
+ @Rule(order = 2) public ScreenCaptureRule mScreenCaptureRule =
new ScreenCaptureRule("/sdcard/InputMethodStressTest");
private Context mContext;
@@ -119,7 +119,15 @@ public final class NotificationTest {
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_A);
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_B);
mUiDevice.pressKeyCode(KeyEvent.KEYCODE_C);
- mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR.enabled(true)), TIMEOUT).click();
+ UiObject2 sendButton = mUiDevice.wait(
+ Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+ if (sendButton == null) {
+ // If the screen is too small, sendButton may be hidden by IME.
+ // Dismiss IME and try again.
+ mUiDevice.pressBack();
+ sendButton = mUiDevice.wait(Until.findObject(REPLY_SEND_BUTTON_SELECTOR), TIMEOUT);
+ }
+ sendButton.click();
// Verify that IME is gone.
assertThat(mUiDevice.wait(Until.gone(By.pkg(getImePackage(mContext))), TIMEOUT)).isTrue();
}
@@ -132,7 +140,8 @@ public final class NotificationTest {
// Post inline reply notification.
PendingIntent pendingIntent = PendingIntent.getBroadcast(
- mContext, REPLY_REQUEST_CODE, new Intent().setAction(ACTION_REPLY),
+ mContext, REPLY_REQUEST_CODE,
+ new Intent().setAction(ACTION_REPLY).setClass(mContext, NotificationTest.class),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
RemoteInput remoteInput = new RemoteInput.Builder(REPLY_INPUT_KEY)
.setLabel(REPLY_INPUT_LABEL)
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index 9ffb236d3f59..70660a0a117e 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -48,10 +48,10 @@ public class WallpaperColorsTest {
}
/**
- * Check that white supports dark text and black doesn't
+ * Check that white surface supports dark text
*/
@Test
- public void colorHintsTest() {
+ public void whiteSurfaceColorHintsTest() {
Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(image);
@@ -59,28 +59,68 @@ public class WallpaperColorsTest {
int hints = WallpaperColors.fromBitmap(image).getColorHints();
boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
- boolean fromBitmap = (hints & WallpaperColors.HINT_FROM_BITMAP) != 0;
Assert.assertTrue("White surface should support dark text.", supportsDarkText);
Assert.assertFalse("White surface shouldn't support dark theme.", supportsDarkTheme);
- Assert.assertTrue("From bitmap should be true if object was created "
- + "using WallpaperColors#fromBitmap.", fromBitmap);
-
- canvas.drawColor(Color.BLACK);
- hints = WallpaperColors.fromBitmap(image).getColorHints();
- supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
- supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
- Assert.assertFalse("Black surface shouldn't support dark text.", supportsDarkText);
- Assert.assertTrue("Black surface should support dark theme.", supportsDarkTheme);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
- canvas.drawColor(Color.WHITE);
canvas.drawRect(0, 0, 8, 8, paint);
supportsDarkText = (WallpaperColors.fromBitmap(image)
.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
Assert.assertFalse("Light surface shouldn't support dark text "
+ "when it contains dark pixels.", supportsDarkText);
+ }
+
+ /**
+ * Check that x-small white region supports dark text when max number of dark pixels = 0
+ */
+ @Test
+ public void xSmallWhiteSurfaceColorHintsTest() {
+ Bitmap xsmall_image = Bitmap.createBitmap(1, 5, Bitmap.Config.ARGB_8888);
+ Canvas xsmall_canvas = new Canvas(xsmall_image);
+
+ xsmall_canvas.drawColor(Color.WHITE);
+ int hints = WallpaperColors.fromBitmap(xsmall_image).getColorHints();
+ boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+ Assert.assertTrue("X-small white surface should support dark text.",
+ supportsDarkText);
+ Assert.assertFalse("X-small white surface shouldn't support dark theme.",
+ supportsDarkTheme);
+ }
+
+ /**
+ * Check that black surface doesn't support dark text
+ */
+ @Test
+ public void blackSurfaceColorHintsTest() {
+ Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(image);
+
+ canvas.drawColor(Color.BLACK);
+ int hints = WallpaperColors.fromBitmap(image).getColorHints();
+ boolean supportsDarkText = (hints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
+ boolean supportsDarkTheme = (hints & WallpaperColors.HINT_SUPPORTS_DARK_THEME) != 0;
+ Assert.assertFalse("Black surface shouldn't support dark text.", supportsDarkText);
+ Assert.assertTrue("Black surface should support dark theme.", supportsDarkTheme);
+ }
+
+ /**
+ * Check that bitmap hint properly indicates when object created via WallpaperColors#fromBitmap
+ * versus WallpaperColors() public constructor
+ */
+ @Test
+ public void bitmapHintsTest() {
+ Bitmap image = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(image);
+
+ canvas.drawColor(Color.WHITE);
+ int hints = WallpaperColors.fromBitmap(image).getColorHints();
+
+ boolean fromBitmap = (hints & WallpaperColors.HINT_FROM_BITMAP) != 0;
+ Assert.assertTrue("From bitmap should be true if object was created "
+ + "using WallpaperColors#fromBitmap.", fromBitmap);
WallpaperColors colors = new WallpaperColors(Color.valueOf(Color.GREEN), null, null);
fromBitmap = (colors.getColorHints() & WallpaperColors.HINT_FROM_BITMAP) != 0;
diff --git a/tests/Internal/src/android/service/wallpaper/OWNERS b/tests/Internal/src/android/service/wallpaper/OWNERS
new file mode 100644
index 000000000000..5a26d0e1f62b
--- /dev/null
+++ b/tests/Internal/src/android/service/wallpaper/OWNERS
@@ -0,0 +1,4 @@
+dupin@google.com
+santie@google.com
+pomini@google.com
+poultney@google.com \ No newline at end of file
diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
index 153ca79e346b..0c5e8d481131 100644
--- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
+++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java
@@ -85,4 +85,17 @@ public class WallpaperServiceTest {
assertEquals("onAmbientModeChanged should have been called", 2, zoomChangedCount[0]);
}
+ @Test
+ public void testNotifyColorsOfDestroyedEngine_doesntCrash() {
+ WallpaperService service = new WallpaperService() {
+ @Override
+ public Engine onCreateEngine() {
+ return new Engine();
+ }
+ };
+ WallpaperService.Engine engine = service.onCreateEngine();
+ engine.detach();
+
+ engine.notifyColorsChanged();
+ }
}
diff --git a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
new file mode 100644
index 000000000000..d16e90e26aaa
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.internal.app;
+
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.os.LocaleList;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.LocaleStore.LocaleInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Unit tests for the {@link AppLocaleCollector}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppLocaleCollectorTest {
+ private static final String TAG = "AppLocaleCollectorTest";
+ private AppLocaleCollector mAppLocaleCollector;
+ private LocaleStore.LocaleInfo mAppCurrentLocale;
+ private Set<LocaleInfo> mAllAppActiveLocales;
+ private Set<LocaleInfo> mImeLocales;
+ private Set<LocaleInfo> mSystemCurrentLocales;
+ private Set<LocaleInfo> mSystemSupportedLocales;
+ private AppLocaleStore.AppLocaleResult mResult;
+ private static final String PKG1 = "pkg1";
+ private static final int NONE = LocaleInfo.SUGGESTION_TYPE_NONE;
+ private static final int SIM = LocaleInfo.SUGGESTION_TYPE_SIM;
+ private static final int CFG = LocaleInfo.SUGGESTION_TYPE_CFG;
+ private static final int SIM_CFG = SIM | CFG;
+ private static final int CURRENT = LocaleInfo.SUGGESTION_TYPE_CURRENT;
+ private static final int SYSTEM = LocaleInfo.SUGGESTION_TYPE_SYSTEM_LANGUAGE;
+ private static final int OTHERAPP = LocaleInfo.SUGGESTION_TYPE_OTHER_APP_LANGUAGE;
+ private static final int IME = LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE;
+ private static final int SYSTEM_AVAILABLE =
+ LocaleInfo.SUGGESTION_TYPE_SYSTEM_AVAILABLE_LANGUAGE;
+
+ @Before
+ public void setUp() throws Exception {
+ mAppLocaleCollector = spy(
+ new AppLocaleCollector(InstrumentationRegistry.getContext(), PKG1));
+ }
+
+ @Test
+ public void testGetSystemCurrentLocales() {
+ LocaleList.setDefault(
+ LocaleList.forLanguageTags("en-US-u-mu-fahrenhe,ar-JO-u-mu-fahrenhe-nu-latn"));
+
+ Set<LocaleStore.LocaleInfo> list =
+ mAppLocaleCollector.getSystemCurrentLocales();
+
+ LocaleList expected = LocaleList.forLanguageTags("en-US,ar-JO-u-nu-latn");
+ assertEquals(list.size(), expected.size());
+ for (LocaleStore.LocaleInfo info : list) {
+ assertTrue(expected.indexOf(info.getLocale()) != -1);
+ }
+ }
+
+ @Test
+ public void testGetSupportedLocaleList_filterNonAppsupportedSystemLanguage() {
+ mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
+
+ // App supports five locales
+ HashSet<Locale> appSupported =
+ getAppSupportedLocales(new String[] {
+ "en-US",
+ "fr",
+ "ar",
+ "es",
+ "bn"
+ });
+ // There are six locales in system current locales.
+ mSystemCurrentLocales = getSystemCurrentLocales(new String[] {
+ "en-US",
+ "fr-FR",
+ "ar-JO",
+ "ca-AD",
+ "da-DK",
+ "es-US"
+ });
+ mAllAppActiveLocales = Collections.emptySet();
+ mImeLocales = Collections.emptySet();
+ mSystemSupportedLocales = Collections.emptySet();
+ mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+ appSupported);
+
+ doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
+ doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
+ doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
+ doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
+ doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
+ anyObject(), eq(null), eq(true));
+ doReturn(mSystemCurrentLocales).when(
+ mAppLocaleCollector).getSystemCurrentLocales();
+
+ Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
+
+ // The result would show four rather than six locales in the suggested region.
+ HashMap<String, Integer> expectedResult = new HashMap<>();
+ expectedResult.put("en-US", CURRENT); // The locale current App activates.
+ expectedResult.put("ar-JO", SYSTEM_AVAILABLE);
+ expectedResult.put("fr-FR", SYSTEM_AVAILABLE);
+ expectedResult.put("es-US", SYSTEM_AVAILABLE);
+ expectedResult.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
+
+ assertEquals(result.size(), expectedResult.size());
+ for (LocaleStore.LocaleInfo info: result) {
+ int suggestionFlags = expectedResult.getOrDefault(info.getId(), -1);
+ assertEquals(info.mSuggestionFlags, suggestionFlags);
+ }
+ }
+
+ @Test
+ public void testGetSupportedLocaleList_withActiveLocalesFromOtherAppAndIme() {
+ mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
+ mAllAppActiveLocales = initAllAppActivatedLocales();
+ mImeLocales = initImeLocales();
+ mSystemSupportedLocales = initSystemSupportedLocales();
+ mSystemCurrentLocales = initSystemCurrentLocales();
+ mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+ initAppSupportedLocale());
+
+ doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
+ doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
+ doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
+ doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
+ doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
+ anyObject(), eq(null), eq(true));
+ doReturn(mSystemCurrentLocales).when(
+ mAppLocaleCollector).getSystemCurrentLocales();
+
+ Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
+
+ HashMap<String, Integer> expectedResult = getExpectedResult();
+ assertEquals(result.size(), expectedResult.size());
+ for (LocaleInfo source : result) {
+ int suggestionFlags = expectedResult.getOrDefault(source.getId(), -1);
+ assertEquals(source.mSuggestionFlags, suggestionFlags);
+ }
+ }
+
+ private HashMap<String, Integer> getExpectedResult() {
+ HashMap<String, Integer> map = new HashMap<>();
+ map.put("en-US", CURRENT); // The locale current App activates.
+ map.put("fr", NONE); // The locale App and system support.
+ map.put("zu", NONE); // The locale App and system support.
+ map.put("en", NONE); // Use en because System supports en while APP supports en-CA, en-GB.
+ map.put("ko", NONE); // The locale App and system support.
+ map.put("en-AU", OTHERAPP); // The locale other App activates and current App supports.
+ map.put("en-CA", OTHERAPP); // The locale other App activates and current App supports.
+ map.put("en-IN", IME); // The locale IME supports.
+ map.put("ja-JP",
+ OTHERAPP | SYSTEM_AVAILABLE | IME); // The locale exists in OTHERAPP, SYSTEM and IME
+ map.put("zh-Hant-TW", SYSTEM_AVAILABLE); // The locale system activates.
+ map.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
+ return map;
+ }
+
+ private Set<LocaleInfo> initSystemSupportedLocales() {
+ return Set.of(
+ createLocaleInfo("en", NONE),
+ createLocaleInfo("fr", NONE),
+ createLocaleInfo("zu", NONE),
+ createLocaleInfo("ko", NONE),
+ // will be filtered because current App doesn't support.
+ createLocaleInfo("es-US", SIM_CFG)
+ );
+ }
+
+ private Set<LocaleInfo> initSystemCurrentLocales() {
+ return Set.of(createLocaleInfo("zh-Hant-TW", SYSTEM_AVAILABLE),
+ createLocaleInfo("ja-JP", SYSTEM_AVAILABLE),
+ // will be filtered because current App activates this locale.
+ createLocaleInfo("en-US", SYSTEM_AVAILABLE));
+ }
+
+ private Set<LocaleInfo> initAllAppActivatedLocales() {
+ return Set.of(
+ createLocaleInfo("en-CA", OTHERAPP),
+ createLocaleInfo("en-AU", OTHERAPP),
+ createLocaleInfo("ja-JP", OTHERAPP),
+ // will be filtered because current App activates this locale.
+ createLocaleInfo("en-US", OTHERAPP));
+ }
+
+ private Set<LocaleInfo> initImeLocales() {
+ return Set.of(
+ // will be filtered because system activates zh-Hant-TW.
+ createLocaleInfo("zh-TW", IME),
+ // will be filtered because current App's activats this locale.
+ createLocaleInfo("en-US", IME),
+ createLocaleInfo("ja-JP", IME),
+ createLocaleInfo("en-IN", IME));
+ }
+
+ private HashSet<Locale> initAppSupportedLocale() {
+ HashSet<Locale> hs = new HashSet();
+ hs.add(Locale.forLanguageTag("en-US"));
+ hs.add(Locale.forLanguageTag("en-CA"));
+ hs.add(Locale.forLanguageTag("en-GB"));
+ hs.add(Locale.forLanguageTag("zh-TW"));
+ hs.add(Locale.forLanguageTag("ja"));
+ hs.add(Locale.forLanguageTag("fr"));
+ hs.add(Locale.forLanguageTag("zu"));
+ hs.add(Locale.forLanguageTag("ko"));
+ // will be filtered because it's not in the system language.
+ hs.add(Locale.forLanguageTag("mn"));
+ return hs;
+ }
+
+ private Set<LocaleStore.LocaleInfo> getSystemCurrentLocales(String []languageTags) {
+ HashSet<LocaleStore.LocaleInfo> hs = new HashSet<>(languageTags.length);
+ for (String tag:languageTags) {
+ hs.add(createLocaleInfo(tag, SYSTEM_AVAILABLE));
+ }
+ return hs;
+ }
+
+ private HashSet<Locale> getAppSupportedLocales(String []languageTags) {
+ HashSet<Locale> hs = new HashSet<>(languageTags.length);
+ for (String language:languageTags) {
+ hs.add(Locale.forLanguageTag(language));
+ }
+ return hs;
+ }
+
+ private LocaleInfo createLocaleInfo(String languageTag, int suggestionFlag) {
+ LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag(languageTag));
+ localeInfo.mSuggestionFlags = suggestionFlag;
+ localeInfo.setTranslated(true);
+ return localeInfo;
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java b/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java
new file mode 100644
index 000000000000..f6568816b4f7
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/app/LocaleStoreTest.java
@@ -0,0 +1,263 @@
+/*
+ * 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.internal.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.LocaleList;
+import android.view.inputmethod.InputMethodSubtype;
+import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.LocaleStore.LocaleInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IllformedLocaleException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/** Unit tests for the {@link LocaleStore}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocaleStoreTest {
+ @Test
+ public void testTransformImeLanguageTagToLocaleInfo() {
+ List<InputMethodSubtype> list = List.of(
+ new InputMethodSubtypeBuilder().setLanguageTag("en-US").build(),
+ new InputMethodSubtypeBuilder().setLanguageTag("zh-TW").build(),
+ new InputMethodSubtypeBuilder().setLanguageTag("ja-JP").build());
+
+ Set<LocaleInfo> localeSet = LocaleStore.transformImeLanguageTagToLocaleInfo(list);
+
+ Set<String> expectedLanguageTag = Set.of("en-US", "zh-TW", "ja-JP");
+ assertEquals(localeSet.size(), expectedLanguageTag.size());
+ for (LocaleInfo info : localeSet) {
+ assertEquals(info.mSuggestionFlags, LocaleInfo.SUGGESTION_TYPE_IME_LANGUAGE);
+ assertTrue(expectedLanguageTag.contains(info.getId()));
+ }
+ }
+
+ @Test
+ public void convertExplicitLocales_noExplicitLcoales_returnEmptyHashMap() {
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(
+ LocaleList.getEmptyLocaleList(), supportedLocale);
+
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void convertExplicitLocales_hasEmptyLocale_receiveException() {
+ Locale[] locales = {Locale.forLanguageTag(""), Locale.forLanguageTag("en-US")};
+ LocaleList localelist = new LocaleList(locales);
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ boolean isReceiveException = false;
+ try {
+ LocaleStore.convertExplicitLocales(localelist, supportedLocale);
+ } catch (IllformedLocaleException e) {
+ isReceiveException = true;
+ }
+
+ assertTrue(isReceiveException);
+ }
+
+ @Test
+ public void convertExplicitLocales_hasSameLocale_returnNonSameLocales() {
+ LocaleList locales = LocaleList.forLanguageTags("en-US,en-US");
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(locales, supportedLocale);
+
+ // Only has "en" and "en-US".
+ assertTrue(result.size() == 2);
+ }
+
+ @Test
+ public void convertExplicitLocales_hasEnUs_resultHasParentEn() {
+ LocaleList locales = LocaleList.forLanguageTags("en-US,ja-JP");
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(locales, supportedLocale);
+
+ assertEquals(result.get("en").getId(), "en");
+ }
+
+ @Test
+ public void convertExplicitLocales_hasZhTw_resultZhHantTw() {
+ LocaleList locales = LocaleList.forLanguageTags("zh-TW,en-US,en");
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(locales, supportedLocale);
+
+ assertEquals("zh-Hant-TW", result.get("zh-Hant-TW").getId());
+ }
+
+ @Test
+ public void convertExplicitLocales_nonRegularFormat_resultEmptyContry() {
+ LocaleList locales = LocaleList.forLanguageTags("de-1996,de-1901");
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(locales, supportedLocale);
+
+ assertEquals("de-1996", result.get("de-1996").getId());
+ assertTrue(result.get("de-1996").getLocale().getCountry().isEmpty());
+ }
+
+ @Test
+ public void convertExplicitLocales_differentEnFormat() {
+ LocaleList locales = LocaleList.forLanguageTags("en-Latn-US,en-US,en");
+ Collection<LocaleInfo> supportedLocale = getFakeSupportedLocales();
+
+ HashMap<String, LocaleInfo> result =
+ LocaleStore.convertExplicitLocales(locales, supportedLocale);
+ assertEquals("en", result.get("en").getId());
+ assertEquals("en-US", result.get("en-US").getId());
+ assertNull(result.get("en-Latn-US"));
+ }
+
+ @Test
+ public void getLevelLocales_languageTier_returnAllSupportLanguages() {
+ LocaleList testSupportedLocales =
+ LocaleList.forLanguageTags(
+ "en-US,zh-Hant-TW,ja-JP,en-GB,bn-IN-u-nu-arab,ks-Arab-IN,bn-IN");
+
+ Set<String> ignorableLocales = new HashSet<>();
+ ignorableLocales.add("zh-Hant-HK");
+ LocaleInfo parent = null;
+
+ Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
+ null, ignorableLocales, parent, false, testSupportedLocales);
+
+ assertEquals(5, localeInfos.size());
+ localeInfos.forEach(localeInfo -> {
+ assertTrue(localeInfo.getLocale().getCountry().isEmpty());
+ });
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("en")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("zh-Hant")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("ja")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("bn")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("ks-Arab")));
+ }
+
+ @Test
+ public void getLevelLocales_regionTierAndParentIsEn_returnEnLocales() {
+ LocaleList testSupportedLocales =
+ LocaleList.forLanguageTags(
+ "en-US,en-GB,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN");
+ Set<String> ignorableLocales = new HashSet<>();
+ ignorableLocales.add("zh-Hant-HK");
+ LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("en"));
+
+ Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
+ null, ignorableLocales, parent, false, testSupportedLocales);
+
+ assertEquals(3, localeInfos.size());
+ localeInfos.forEach(localeInfo -> {
+ assertEquals("en", localeInfo.getLocale().getLanguage());
+ });
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("en-US")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("en-GB")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("en-ZA")));
+ }
+
+ @Test
+ public void getLevelLocales_numberingTierAndParentIsBnIn_returnBnInLocales() {
+ LocaleList testSupportedLocales =
+ LocaleList.forLanguageTags(
+ "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
+ Set<String> ignorableLocales = new HashSet<>();
+ ignorableLocales.add("zh-Hant-HK");
+ LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn"));
+
+ Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
+ null, ignorableLocales, parent, false, testSupportedLocales);
+
+ assertEquals(1, localeInfos.size());
+ assertEquals("bn-IN", localeInfos.iterator().next().getLocale().toLanguageTag());
+ }
+
+ @Test
+ public void getLevelLocales_regionTierAndParentIsBnInAndIgnoreBn_returnEmpty() {
+ LocaleList testSupportedLocales =
+ LocaleList.forLanguageTags(
+ "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
+ Set<String> ignorableLocales = new HashSet<>();
+ ignorableLocales.add("bn-IN");
+ LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn-IN"));
+
+ Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
+ null, ignorableLocales, parent, false, testSupportedLocales);
+
+ assertEquals(0, localeInfos.size());
+ }
+
+ @Test
+ public void getLevelLocales_regionTierAndParentIsBnIn_returnBnLocaleFamily() {
+ LocaleList testSupportedLocales =
+ LocaleList.forLanguageTags(
+ "en-US,zh-Hant-TW,bn-IN-u-nu-arab,ks-Arab-IN,en-ZA,bn-IN,bn-IN-u-nu-adlm");
+ Set<String> ignorableLocales = new HashSet<>();
+ ignorableLocales.add("en-US");
+ LocaleInfo parent = LocaleStore.fromLocale(Locale.forLanguageTag("bn-IN"));
+
+ Set<LocaleInfo> localeInfos = LocaleStore.getLevelLocales(
+ null, ignorableLocales, parent, false, testSupportedLocales);
+
+ assertEquals(3, localeInfos.size());
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("bn-IN-u-nu-adlm")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("bn-IN-u-nu-arab")));
+ assertTrue(localeInfos.stream().anyMatch(
+ info -> info.getLocale().toLanguageTag().equals("bn-IN")));
+ }
+
+ private ArrayList<LocaleInfo> getFakeSupportedLocales() {
+ String[] locales = {"en-US", "zh-Hant-TW", "ja-JP", "en-GB", "en-US-u-nu-arab"};
+ ArrayList<LocaleInfo> supportedLocales = new ArrayList<>();
+ for (String localeTag : locales) {
+ supportedLocales.add(LocaleStore.fromLocale(Locale.forLanguageTag(localeTag)));
+ }
+ return supportedLocales;
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/app/OWNERS b/tests/Internal/src/com/android/internal/app/OWNERS
new file mode 100644
index 000000000000..d55dc78b8c0a
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/app/OWNERS
@@ -0,0 +1,2 @@
+# Locale related test
+per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS
diff --git a/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java
new file mode 100644
index 000000000000..7419ee1230d3
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/os/TimeoutRecordTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.internal.os;
+
+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 android.content.ComponentName;
+import android.content.Intent;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link TimeoutRecord}. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class TimeoutRecordTest {
+
+ @Test
+ public void forBroadcastReceiver_returnsCorrectTimeoutRecord() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName("com.example.app", "com.example.app.ExampleClass"));
+
+ TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent);
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER);
+ assertEquals(record.mReason,
+ "Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example"
+ + ".app/.ExampleClass }");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forBroadcastReceiver_withPackageAndClass_returnsCorrectTimeoutRecord() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent,
+ "com.example.app", "com.example.app.ExampleClass");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER);
+ assertEquals(record.mReason,
+ "Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example"
+ + ".app/.ExampleClass }");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forBroadcastReceiver_withTimeoutDurationMs_returnsCorrectTimeoutRecord() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName("com.example.app", "com.example.app.ExampleClass"));
+
+ TimeoutRecord record = TimeoutRecord.forBroadcastReceiver(intent, 1000L);
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER);
+ assertEquals(record.mReason,
+ "Broadcast of Intent { act=android.intent.action.MAIN cmp=com.example"
+ + ".app/.ExampleClass }, waited 1000ms");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forInputDispatchNoFocusedWindow_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forInputDispatchNoFocusedWindow("Test ANR reason");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW);
+ assertEquals(record.mReason,
+ "Test ANR reason");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forInputDispatchWindowUnresponsive_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forInputDispatchWindowUnresponsive("Test ANR reason");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE);
+ assertEquals(record.mReason, "Test ANR reason");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forServiceExec_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forServiceExec("Test ANR reason");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.SERVICE_EXEC);
+ assertEquals(record.mReason, "Test ANR reason");
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forServiceStartWithEndTime_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forServiceStartWithEndTime("Test ANR reason", 1000L);
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.SERVICE_START);
+ assertEquals(record.mReason, "Test ANR reason");
+ assertEquals(record.mEndUptimeMillis, 1000L);
+ assertTrue(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forContentProvider_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forContentProvider("Test ANR reason");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.CONTENT_PROVIDER);
+ assertEquals(record.mReason, "Test ANR reason");
+ assertFalse(record.mEndTakenBeforeLocks);
+ }
+
+ @Test
+ public void forApp_returnsCorrectTimeoutRecord() {
+ TimeoutRecord record = TimeoutRecord.forApp("Test ANR reason");
+
+ assertNotNull(record);
+ assertEquals(record.mKind, TimeoutRecord.TimeoutKind.APP_REGISTERED);
+ assertEquals(record.mReason, "Test ANR reason");
+ assertFalse(record.mEndTakenBeforeLocks);
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
index 3db011683a86..7deb8c73d1fc 100644
--- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java
@@ -86,7 +86,7 @@ public class ProtoLogImplTest {
mFile = testContext.getFileStreamPath("tracing_test.dat");
//noinspection ResultOfMethodCallIgnored
mFile.delete();
- mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader);
+ mProtoLog = new ProtoLogImpl(mFile, 1024 * 1024, mReader, 1024);
}
@After
@@ -201,24 +201,24 @@ public class ProtoLogImplTest {
@Test
public void log_logcatEnabledExternalMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% 0x%x %s %f");
ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
implSpy.log(
ProtoLogImpl.LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, null,
- new Object[]{true, 10000, 20000, 30000, 0.0001, 0.00002, "test", 0.000003});
+ new Object[]{true, 10000, 30000, "test", 0.000003});
verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
ProtoLogImpl.LogLevel.INFO),
- eq("test true 10000 % 47040 7530 1.000000e-04 2.00000e-05 test 0.000003"));
+ eq("test true 10000 % 0x7530 test 3.0E-6"));
verify(mReader).getViewerString(eq(1234));
}
@Test
public void log_logcatEnabledInvalidMessage() {
- when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %o %x %e %g %s %f");
+ when(mReader.getViewerString(anyInt())).thenReturn("test %b %d %% %x %s %f");
ProtoLogImpl implSpy = Mockito.spy(mProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
diff --git a/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java b/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
index e20ca3df57c7..9c2f74eabe02 100644
--- a/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java
@@ -36,15 +36,12 @@ import java.util.List;
public class LogDataTypeTest {
@Test
public void parseFormatString() {
- String str = "%b %d %o %x %f %e %g %s %%";
+ String str = "%b %d %x %f %s %%";
List<Integer> out = LogDataType.parseFormatString(str);
assertEquals(Arrays.asList(
LogDataType.BOOLEAN,
LogDataType.LONG,
LogDataType.LONG,
- LogDataType.LONG,
- LogDataType.DOUBLE,
- LogDataType.DOUBLE,
LogDataType.DOUBLE,
LogDataType.STRING
), out);
diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
index 4de51fb57308..43dc9de6c90a 100644
--- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
+++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/HomeActivity.java
@@ -140,9 +140,9 @@ public class HomeActivity extends AppCompatActivity implements Button.OnClickLis
handleNextBenchmark();
}
+ @SuppressWarnings("MissingSuperCall") // TODO: Fix me
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-
}
private void handleNextBenchmark() {
diff --git a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java
index c16efbda1830..d015a5695ec0 100644
--- a/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java
+++ b/tests/JankBench/app/src/main/java/com/android/benchmark/app/RunLocalBenchmarksActivity.java
@@ -367,6 +367,7 @@ public class RunLocalBenchmarksActivity extends AppCompatActivity {
}
}
+ @SuppressWarnings("MissingSuperCall") // TODO: Fix me
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
diff --git a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
index dd9b294a9596..00fc4982a213 100644
--- a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -15,7 +15,6 @@
*/
package com.android.frameworks.perftests.job;
-
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -46,7 +45,8 @@ import java.util.List;
public class JobStorePerfTests {
private static final String SOURCE_PACKAGE = "com.android.frameworks.perftests.job";
private static final int SOURCE_USER_ID = 0;
- private static final int CALLING_UID = 10079;
+ private static final int BASE_CALLING_UID = 10079;
+ private static final int MAX_UID_COUNT = 10;
private static Context sContext;
private static File sTestDir;
@@ -65,10 +65,10 @@ public class JobStorePerfTests {
sJobStore = JobStore.initAndGetForTesting(sContext, sTestDir);
for (int i = 0; i < 50; i++) {
- sFewJobs.add(createJobStatus("fewJobs", i));
+ sFewJobs.add(createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % MAX_UID_COUNT)));
}
for (int i = 0; i < 500; i++) {
- sManyJobs.add(createJobStatus("manyJobs", i));
+ sManyJobs.add(createJobStatus("manyJobs", i, BASE_CALLING_UID + (i % MAX_UID_COUNT)));
}
}
@@ -104,6 +104,64 @@ public class JobStorePerfTests {
runPersistedJobWriting(sManyJobs);
}
+ private void runPersistedJobWriting_delta(List<JobStatus> jobList,
+ List<JobStatus> jobAdditions, List<JobStatus> jobRemovals) {
+ final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
+
+ long elapsedTimeNs = 0;
+ while (benchmarkState.keepRunning(elapsedTimeNs)) {
+ sJobStore.clearForTesting();
+ for (JobStatus job : jobList) {
+ sJobStore.addForTesting(job);
+ }
+ sJobStore.writeStatusToDiskForTesting();
+
+ for (JobStatus job : jobAdditions) {
+ sJobStore.addForTesting(job);
+ }
+ for (JobStatus job : jobRemovals) {
+ sJobStore.removeForTesting(job);
+ }
+
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ sJobStore.writeStatusToDiskForTesting();
+ final long endTime = SystemClock.elapsedRealtimeNanos();
+ elapsedTimeNs = endTime - startTime;
+ }
+ }
+
+ @Test
+ public void testPersistedJobWriting_delta_fewJobs() {
+ List<JobStatus> additions = new ArrayList<>();
+ List<JobStatus> removals = new ArrayList<>();
+ final int numModifiedUids = MAX_UID_COUNT / 2;
+ for (int i = 0; i < sFewJobs.size() / 3; ++i) {
+ JobStatus job = createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % numModifiedUids));
+ if (i % 2 == 0) {
+ additions.add(job);
+ } else {
+ removals.add(job);
+ }
+ }
+ runPersistedJobWriting_delta(sFewJobs, additions, removals);
+ }
+
+ @Test
+ public void testPersistedJobWriting_delta_manyJobs() {
+ List<JobStatus> additions = new ArrayList<>();
+ List<JobStatus> removals = new ArrayList<>();
+ final int numModifiedUids = MAX_UID_COUNT / 2;
+ for (int i = 0; i < sManyJobs.size() / 3; ++i) {
+ JobStatus job = createJobStatus("fewJobs", i, BASE_CALLING_UID + (i % numModifiedUids));
+ if (i % 2 == 0) {
+ additions.add(job);
+ } else {
+ removals.add(job);
+ }
+ }
+ runPersistedJobWriting_delta(sManyJobs, additions, removals);
+ }
+
private void runPersistedJobReading(List<JobStatus> jobList, boolean rtcIsGood) {
final ManualBenchmarkState benchmarkState = mPerfManualStatusReporter.getBenchmarkState();
@@ -144,12 +202,12 @@ public class JobStorePerfTests {
runPersistedJobReading(sManyJobs, false);
}
- private static JobStatus createJobStatus(String testTag, int jobId) {
+ private static JobStatus createJobStatus(String testTag, int jobId, int callingUid) {
JobInfo jobInfo = new JobInfo.Builder(jobId,
new ComponentName(sContext, "JobStorePerfTestJobService"))
.setPersisted(true)
.build();
return JobStatus.createFromJobInfo(
- jobInfo, CALLING_UID, SOURCE_PACKAGE, SOURCE_USER_ID, testTag);
+ jobInfo, callingUid, SOURCE_PACKAGE, SOURCE_USER_ID, null, testTag);
}
}
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index c7e5a5ea3311..e59071bd1d88 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Debug.MemoryInfo;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.test.InstrumentationTestCase;
@@ -40,6 +41,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
/**
* This test is intended to measure the amount of memory applications use when
@@ -313,17 +315,19 @@ public class MemoryUsageTest extends InstrumentationTestCase {
public void run() {
try {
- String mimeType = mLaunchIntent.getType();
- if (mimeType == null && mLaunchIntent.getData() != null
+ AtomicReference<String> mimeType = new AtomicReference<>(mLaunchIntent.getType());
+ if (mimeType.get() == null && mLaunchIntent.getData() != null
&& "content".equals(mLaunchIntent.getData().getScheme())) {
- mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
- UserHandle.USER_CURRENT);
+ mAm.getMimeTypeFilterAsync(mLaunchIntent.getData(), UserHandle.USER_CURRENT,
+ new RemoteCallback(result -> {
+ mimeType.set(result.getPairValue());
+ }));
}
mAtm.startActivityAndWait(null,
getInstrumentation().getContext().getBasePackageName(),
getInstrumentation().getContext().getAttributionTag(), mLaunchIntent,
- mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
+ mimeType.get(), null, null, 0, mLaunchIntent.getFlags(), null, null,
UserHandle.USER_CURRENT_OR_SELF);
} catch (RemoteException e) {
Log.w(TAG, "Error launching app", e);
diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp
new file mode 100644
index 000000000000..254770d21818
--- /dev/null
+++ b/tests/MidiTests/Android.bp
@@ -0,0 +1,40 @@
+//
+// 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 {
+ // 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: "MidiTests",
+ srcs: ["**/*.java"],
+ static_libs: [
+ "androidx.test.rules",
+ "mockito-target-inline-minus-junit4",
+ "platform-test-annotations",
+ "services.midi",
+ "truth-prebuilt",
+ ],
+ jni_libs: ["libdexmakerjvmtiagent"],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/tests/MidiTests/AndroidManifest.xml b/tests/MidiTests/AndroidManifest.xml
new file mode 100644
index 000000000000..0ee1b4493764
--- /dev/null
+++ b/tests/MidiTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.midi" >
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.midi"
+ android:label="MidiTests"/>
+</manifest>
diff --git a/tests/MidiTests/AndroidTest.xml b/tests/MidiTests/AndroidTest.xml
new file mode 100644
index 000000000000..9320f0aac090
--- /dev/null
+++ b/tests/MidiTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<!-- 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 sample instrumentation test.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="MidiTests.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="MidiTests"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.midi"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/MidiTests/OWNERS b/tests/MidiTests/OWNERS
new file mode 100644
index 000000000000..af273a6f50e0
--- /dev/null
+++ b/tests/MidiTests/OWNERS
@@ -0,0 +1 @@
+include /services/midi/OWNERS
diff --git a/tests/MidiTests/TEST_MAPPING b/tests/MidiTests/TEST_MAPPING
new file mode 100644
index 000000000000..60416a8ab3f9
--- /dev/null
+++ b/tests/MidiTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MidiTests"
+ }
+ ]
+}
diff --git a/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java
new file mode 100644
index 000000000000..1659cc07f021
--- /dev/null
+++ b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java
@@ -0,0 +1,342 @@
+/*
+ * 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.midi;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.midi.MidiEventMultiScheduler;
+import com.android.internal.midi.MidiEventScheduler;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+/**
+ * Unit tests for com.android.internal.midi.MidiEventMultiScheduler.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MidiEventMultiSchedulerTest {
+ private byte[] generateRandomByteStream(Random rnd, int size) {
+ byte[] output = new byte[size];
+ rnd.nextBytes(output);
+ return output;
+ }
+
+ private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
+ assertEquals(expectedArray.length, outputArray.length);
+ for (int i = 0; i < outputArray.length; i++) {
+ assertEquals(expectedArray[i], outputArray[i]);
+ }
+ }
+
+ private long timeFromNow(long milliseconds) {
+ return System.nanoTime() + 1000000L * milliseconds;
+ }
+
+ @Test
+ public void testMultiScheduler() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7},
+ 0, 2, timeFromNow(100)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2},
+ 0, 2, timeFromNow(200)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4},
+ 0, 2, timeFromNow(300)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
+ 0, 2, timeFromNow(400)));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNotNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNotNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerLargeData() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+
+ Random rnd = new Random(42);
+
+ final int arraySize = 1000;
+ byte[] expectedArray = generateRandomByteStream(rnd, arraySize);
+
+ scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize,
+ timeFromNow(100)));
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ compareByteArrays(expectedArray, event.data);
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ scheduler0.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerMultiClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ multiScheduler.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerNoPreemptiveClose() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+ scheduler0.close();
+ scheduler1.close();
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
+ 0, 2, timeFromNow(100)));
+ assertTrue(multiScheduler.waitNextEvent());
+ scheduler2.close();
+ // After all schedulers are closed, waitNextEvent() should return false.
+ assertFalse(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testSchedulerSpamEvents() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals(msg[0], event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testSchedulerSpamEventsPullLater() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ }
+
+ for (int i = 0; i < 1000; i++) {
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) i, event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testSchedulerSpamEventsCallbackLater() {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
+ assertEquals(1, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ // Create a msg with size 1
+ byte[] msg = new byte[1];
+ for (int i = 0; i < 1000; i++) {
+ msg[0] = (byte) i;
+ scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
+ }
+
+ for (int i = 0; i < 1000; i++) {
+ try {
+ assertTrue(multiScheduler.waitNextEvent());
+ } catch (InterruptedException ex) {
+ }
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) i, event.data[0]);
+ }
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ }
+
+ @Test
+ public void testMultiSchedulerOutOfOrder() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
+ 0, 1,
+ timeFromNow(400)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
+ 0, 1,
+ timeFromNow(300)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
+ 0, 1,
+ timeFromNow(200)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
+ 0, 1,
+ timeFromNow(100)));
+
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf0, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf1, event.data[0]);
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ assertNull(scheduler0.getNextEvent(System.nanoTime()));
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf2, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf3, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+
+ @Test
+ public void testMultiSchedulerOutOfOrderNegativeTime() {
+ try {
+ MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
+ assertEquals(3, multiScheduler.getNumEventSchedulers());
+ MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
+ MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
+ MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
+
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
+ 0, 1,
+ timeFromNow(-100)));
+ scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
+ 0, 1,
+ timeFromNow(-200)));
+ scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
+ 0, 1,
+ timeFromNow(-300)));
+ scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
+ 0, 1,
+ timeFromNow(-400)));
+
+ assertTrue(multiScheduler.waitNextEvent());
+ MidiEventScheduler.MidiEvent event =
+ (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf0, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf1, event.data[0]);
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf2, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertTrue(multiScheduler.waitNextEvent());
+ event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
+ assertNotNull(event);
+ assertEquals(1, event.count);
+ assertEquals((byte) 0xf3, event.data[0]);
+ assertNull(scheduler1.getNextEvent(System.nanoTime()));
+ assertNull(scheduler2.getNextEvent(System.nanoTime()));
+ } catch (InterruptedException ex) {
+
+ }
+ }
+}
diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
index 8afe8411a790..17fa210a1db6 100644
--- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
+++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
@@ -295,8 +295,8 @@ public class MirrorSurfaceActivity extends Activity implements View.OnClickListe
private void updateMirror(Rect displayFrame, float scale) {
if (displayFrame.isEmpty()) {
Rect bounds = mWindowBounds;
- int defaultCropW = Math.round(bounds.width() / 2);
- int defaultCropH = Math.round(bounds.height() / 2);
+ int defaultCropW = bounds.width() / 2;
+ int defaultCropH = bounds.height() / 2;
displayFrame.set(0, 0, defaultCropW, defaultCropH);
}
diff --git a/tests/MotionPrediction/Android.bp b/tests/MotionPrediction/Android.bp
new file mode 100644
index 000000000000..6cda8f050987
--- /dev/null
+++ b/tests/MotionPrediction/Android.bp
@@ -0,0 +1,30 @@
+//
+// 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 {
+ // 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: "MotionPrediction",
+ srcs: ["**/*.kt"],
+ sdk_version: "current",
+}
diff --git a/tests/MotionPrediction/AndroidManifest.xml b/tests/MotionPrediction/AndroidManifest.xml
new file mode 100644
index 000000000000..3f8c2f278623
--- /dev/null
+++ b/tests/MotionPrediction/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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="test.motionprediction">
+
+ <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/MotionPrediction/OWNERS b/tests/MotionPrediction/OWNERS
new file mode 100644
index 000000000000..c88bfe97cab9
--- /dev/null
+++ b/tests/MotionPrediction/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/INPUT_OWNERS
diff --git a/tests/MotionPrediction/res/layout/activity_main.xml b/tests/MotionPrediction/res/layout/activity_main.xml
new file mode 100644
index 000000000000..65dc325befdb
--- /dev/null
+++ b/tests/MotionPrediction/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<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.motionprediction.MainActivity">
+
+ <test.motionprediction.DrawingView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/output" />
+
+</LinearLayout>
diff --git a/tests/MotionPrediction/res/mipmap-hdpi/ic_launcher.png b/tests/MotionPrediction/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000000..cde69bcccec6
--- /dev/null
+++ b/tests/MotionPrediction/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MotionPrediction/res/mipmap-mdpi/ic_launcher.png b/tests/MotionPrediction/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000000..c133a0cbd379
--- /dev/null
+++ b/tests/MotionPrediction/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MotionPrediction/res/mipmap-xhdpi/ic_launcher.png b/tests/MotionPrediction/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000000..bfa42f0e7b91
--- /dev/null
+++ b/tests/MotionPrediction/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MotionPrediction/res/mipmap-xxhdpi/ic_launcher.png b/tests/MotionPrediction/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..324e72cdd748
--- /dev/null
+++ b/tests/MotionPrediction/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MotionPrediction/res/mipmap-xxxhdpi/ic_launcher.png b/tests/MotionPrediction/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000000..aee44e138434
--- /dev/null
+++ b/tests/MotionPrediction/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/MotionPrediction/res/values-w820dp/dimens.xml b/tests/MotionPrediction/res/values-w820dp/dimens.xml
new file mode 100644
index 000000000000..95669e6aa6fa
--- /dev/null
+++ b/tests/MotionPrediction/res/values-w820dp/dimens.xml
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+<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/MotionPrediction/res/values/colors.xml b/tests/MotionPrediction/res/values/colors.xml
new file mode 100644
index 000000000000..139eb1d1303b
--- /dev/null
+++ b/tests/MotionPrediction/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/tests/MotionPrediction/res/values/dimens.xml b/tests/MotionPrediction/res/values/dimens.xml
new file mode 100644
index 000000000000..d26136f18c29
--- /dev/null
+++ b/tests/MotionPrediction/res/values/dimens.xml
@@ -0,0 +1,19 @@
+<!-- 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.
+-->
+<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/MotionPrediction/res/values/strings.xml b/tests/MotionPrediction/res/values/strings.xml
new file mode 100644
index 000000000000..16a2bdf34d13
--- /dev/null
+++ b/tests/MotionPrediction/res/values/strings.xml
@@ -0,0 +1,17 @@
+<!-- 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.
+-->
+<resources>
+ <string name="app_name">Motion Prediction</string>
+</resources>
diff --git a/tests/MotionPrediction/res/values/styles.xml b/tests/MotionPrediction/res/values/styles.xml
new file mode 100644
index 000000000000..cfb5e3d83c0d
--- /dev/null
+++ b/tests/MotionPrediction/res/values/styles.xml
@@ -0,0 +1,23 @@
+<!-- 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.
+-->
+<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/MotionPrediction/src/test/motionprediction/DrawingView.kt b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt
new file mode 100644
index 000000000000..229d0c8da6e9
--- /dev/null
+++ b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt
@@ -0,0 +1,106 @@
+/*
+ * 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 test.motionprediction
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionPredictor
+import android.view.View
+
+import java.util.Vector
+
+private fun drawLine(canvas: Canvas, from: MotionEvent, to: MotionEvent, paint: Paint) {
+ canvas.apply {
+ val x0 = from.getX()
+ val y0 = from.getY()
+ val x1 = to.getX()
+ val y1 = to.getY()
+ // TODO: handle historical data
+ drawLine(x0, y0, x1, y1, paint)
+ }
+}
+
+/**
+ * Draw the current stroke and predicted values
+ */
+class DrawingView(context: Context, attrs: AttributeSet) : View(context, attrs) {
+ private val TAG = "DrawingView"
+
+ val events: MutableMap<Int, Vector<MotionEvent>> = mutableMapOf<Int, Vector<MotionEvent>>()
+
+ var isPredictionAvailable = false
+ private val predictor = MotionPredictor(getContext())
+
+ private var predictionPaint = Paint()
+ private var realPaint = Paint()
+
+ init {
+ setBackgroundColor(Color.WHITE)
+ predictionPaint.color = Color.BLACK
+ predictionPaint.setStrokeWidth(5f)
+ realPaint.color = Color.RED
+ realPaint.setStrokeWidth(5f)
+ }
+
+ private fun addEvent(event: MotionEvent) {
+ if (event.getActionMasked() == ACTION_DOWN) {
+ events.remove(event.deviceId)
+ }
+ var vec = events.getOrPut(event.deviceId) { Vector<MotionEvent>() }
+ vec.add(MotionEvent.obtain(event))
+ predictor.record(event)
+ invalidate()
+ }
+
+ public override fun onTouchEvent(event: MotionEvent): Boolean {
+ isPredictionAvailable = predictor.isPredictionAvailable(event.getDeviceId(),
+ event.getSource())
+ addEvent(event)
+ return true
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ if (!isPredictionAvailable) {
+ canvas.apply {
+ drawRect(0f, 0f, 200f, 200f, realPaint)
+ }
+ }
+
+ var eventTime = 0L
+
+ // Draw real events
+ for ((_, vec) in events ) {
+ for (i in 1 until vec.size) {
+ drawLine(canvas, vec[i - 1], vec[i], realPaint)
+ }
+ eventTime = vec.lastElement().eventTime
+ }
+
+ // Draw predictions. Convert to nanos and hardcode to +20ms into the future
+ val prediction = predictor.predict(eventTime * 1000000 + 20000000)
+ if (prediction != null) {
+ val realEvents = events.get(prediction.deviceId)!!
+ drawLine(canvas, realEvents[realEvents.size - 1], prediction, predictionPaint)
+ }
+ }
+}
diff --git a/tests/MotionPrediction/src/test/motionprediction/MainActivity.kt b/tests/MotionPrediction/src/test/motionprediction/MainActivity.kt
new file mode 100644
index 000000000000..cec2c06157a1
--- /dev/null
+++ b/tests/MotionPrediction/src/test/motionprediction/MainActivity.kt
@@ -0,0 +1,29 @@
+/*
+ * 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 test.motionprediction
+
+import android.app.Activity
+import android.os.Bundle
+
+class MainActivity : Activity() {
+ val TAG = "MotionPrediction"
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index de86498afd27..a5c6d6513f50 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -26,4 +26,8 @@ java_test_host {
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["device-tests"],
+ data: [
+ ":TestOdmApp",
+ ":TestOdmPrivApp",
+ ],
}
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 7fc352405212..ddde6dbfefb0 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -7,6 +7,7 @@
<uses-sdk android:minSdkVersion="19"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
@@ -27,7 +28,8 @@
</activity>
<service android:name="com.android.onemedia.OnePlayerService"
android:exported="true"
- android:process="com.android.onemedia.service"/>
+ android:process="com.android.onemedia.service"
+ android:foregroundServiceType="mediaPlayback"/>
</application>
</manifest>
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 96bbf82cfba7..d7fa124623ce 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -44,14 +44,14 @@ import android.os.test.TestLooper;
import android.provider.DeviceConfig;
import android.util.AtomicFile;
import android.util.LongArrayQueue;
-import android.util.TypedXmlPullParser;
-import android.util.TypedXmlSerializer;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.PackageWatchdog.HealthCheckState;
import com.android.server.PackageWatchdog.MonitoredPackage;
import com.android.server.PackageWatchdog.PackageHealthObserver;
@@ -417,9 +417,9 @@ public class PackageWatchdogTest {
int failureReason, int mitigationCount) {
if (versionedPackage.getVersionCode() == VERSION_CODE) {
// Only rollback for specific versionCode
- return PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
}
- return PackageHealthObserverImpact.USER_IMPACT_NONE;
+ return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
}
};
@@ -442,13 +442,13 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyAllDifferentImpacts() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observerNone = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_NONE);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_0);
TestObserver observerHigh = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observerMid = new TestObserver(OBSERVER_NAME_3,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
TestObserver observerLow = new TestObserver(OBSERVER_NAME_4,
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
// Start observing for all impact observers
watchdog.startObservingHealth(observerNone, Arrays.asList(APP_A, APP_B, APP_C, APP_D),
@@ -499,9 +499,9 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyLeastImpactSuccessively() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observerFirst = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_LOW);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver observerSecond = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing for observerFirst and observerSecond with failure handling
watchdog.startObservingHealth(observerFirst, Arrays.asList(APP_A), LONG_DURATION);
@@ -517,7 +517,7 @@ public class PackageWatchdogTest {
assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, next action it has is high impact
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH;
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_100;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -531,7 +531,7 @@ public class PackageWatchdogTest {
assertThat(observerFirst.mMitigatedPackages).isEmpty();
// After observerSecond handles failure, it has no further actions
- observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -545,7 +545,7 @@ public class PackageWatchdogTest {
assertThat(observerSecond.mMitigatedPackages).isEmpty();
// After observerFirst handles failure, it too has no further actions
- observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
+ observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0;
observerFirst.mMitigatedPackages.clear();
observerSecond.mMitigatedPackages.clear();
@@ -566,9 +566,9 @@ public class PackageWatchdogTest {
public void testPackageFailureNotifyOneSameImpact() throws Exception {
PackageWatchdog watchdog = createWatchdog();
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing for observer1 and observer2 with failure handling
watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
@@ -592,11 +592,11 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer1 = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer2 = new TestObserver(OBSERVER_NAME_2,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
TestObserver observer3 = new TestObserver(OBSERVER_NAME_3,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
// Start observing with explicit health checks for APP_A and APP_B respectively
@@ -645,7 +645,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and APP_B
controller.setSupportedPackages(Arrays.asList(APP_A, APP_B, APP_C));
@@ -711,7 +711,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and
// package observation duration == LONG_DURATION
@@ -742,7 +742,7 @@ public class PackageWatchdogTest {
TestController controller = new TestController();
PackageWatchdog watchdog = createWatchdog(controller, true /* withPackagesReady */);
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
// Start observing with explicit health checks for APP_A and
// package observation duration == SHORT_DURATION / 2
@@ -818,7 +818,7 @@ public class PackageWatchdogTest {
// Start observing with failure handling
TestObserver observer = new TestObserver(OBSERVER_NAME_1,
- PackageHealthObserverImpact.USER_IMPACT_HIGH);
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_100);
wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
// Notify of NetworkStack failure
@@ -1073,9 +1073,9 @@ public class PackageWatchdogTest {
public void testBootLoopMitigationDoneForLowestUserImpact() {
PackageWatchdog watchdog = createWatchdog();
TestObserver bootObserver1 = new TestObserver(OBSERVER_NAME_1);
- bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LOW);
+ bootObserver1.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_10);
TestObserver bootObserver2 = new TestObserver(OBSERVER_NAME_2);
- bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_MEDIUM);
+ bootObserver2.setImpact(PackageHealthObserverImpact.USER_IMPACT_LEVEL_30);
watchdog.registerHealthObserver(bootObserver1);
watchdog.registerHealthObserver(bootObserver2);
for (int i = 0; i < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; i++) {
@@ -1446,7 +1446,7 @@ public class PackageWatchdogTest {
TestObserver(String name) {
mName = name;
- mImpact = PackageHealthObserverImpact.USER_IMPACT_MEDIUM;
+ mImpact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
}
TestObserver(String name, int impact) {
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index 241206d8919b..65b7549f22d1 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -24,18 +24,14 @@ public class MainActivity extends Activity implements OnItemClickListener {
static final String KEY_NAME = "name";
static final String KEY_CLASS = "clazz";
- static Map<String,?> make(String name) {
- Map<String,Object> ret = new HashMap<String,Object>();
- ret.put(KEY_NAME, name);
- return ret;
- }
-
- @SuppressWarnings("serial")
- static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<Map<String,?>>() {{
+ static final ArrayList<Map<String, ?>> SAMPLES = new ArrayList<>();
+ static {
for (int i = 1; i < 25; i++) {
- add(make("List Item: " + i));
+ Map<String, Object> sample = new HashMap<String, Object>();
+ sample.put(KEY_NAME, "List Item: " + i);
+ SAMPLES.add(sample);
}
- }};
+ }
Handler mHandler = new Handler();
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 9f6ce4e8425b..21007ef1396f 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -51,6 +51,7 @@ java_test_host {
data: [
":com.android.apex.apkrollback.test_v1",
":test.rebootless_apex_v1",
+ ":RollbackTest",
],
}
@@ -61,6 +62,7 @@ java_test_host {
static_libs: ["RollbackTestLib", "frameworks-base-hostutils"],
test_suites: ["general-tests"],
test_config: "NetworkStagedRollbackTest.xml",
+ data: [":RollbackTest"],
}
java_test_host {
@@ -72,6 +74,7 @@ java_test_host {
],
test_suites: ["general-tests"],
test_config: "MultiUserRollbackTest.xml",
+ data : [":RollbackTest"],
}
java_library_host {
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 35859fe1472e..ec1709cc711d 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -18,12 +18,14 @@ package com.android.tests.rollback.host;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,6 +37,7 @@ import java.util.concurrent.TimeUnit;
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class MultiUserRollbackTest extends BaseHostJUnit4Test {
+ private boolean mSupportMultiUsers;
// The user that was running originally when the test starts.
private int mOriginalUserId;
private int mSecondaryUserId = -1;
@@ -46,14 +49,20 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
@After
public void tearDown() throws Exception {
- removeSecondaryUserIfNecessary();
- runPhaseForUsers("cleanUp", mOriginalUserId);
- uninstallPackage("com.android.cts.install.lib.testapp.A");
- uninstallPackage("com.android.cts.install.lib.testapp.B");
+ if (mSupportMultiUsers) {
+ removeSecondaryUserIfNecessary();
+ runPhaseForUsers("cleanUp", mOriginalUserId);
+ uninstallPackage("com.android.cts.install.lib.testapp.A");
+ uninstallPackage("com.android.cts.install.lib.testapp.B");
+ }
}
@Before
public void setup() throws Exception {
+ assumeTrue("Device does not support multiple users",
+ getDevice().isMultiUserSupported());
+
+ mSupportMultiUsers = true;
mOriginalUserId = getDevice().getCurrentUser();
createAndStartSecondaryUser();
installPackage("RollbackTest.apk", "--user all");
@@ -90,6 +99,7 @@ public class MultiUserRollbackTest extends BaseHostJUnit4Test {
}
@Test
+ @Ignore
public void testBadUpdateRollback() throws Exception {
// Need to switch user in order to send broadcasts in device tests
assertTrue(getDevice().switchUser(mSecondaryUserId));
diff --git a/tests/RollbackTest/SampleRollbackApp/Android.bp b/tests/RollbackTest/SampleRollbackApp/Android.bp
new file mode 100644
index 000000000000..074c7bc39155
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/Android.bp
@@ -0,0 +1,33 @@
+// 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_app {
+ name: "SampleRollbackApp",
+ srcs: [
+ "src/**/*.java",
+ ],
+ resource_dirs: ["res"],
+ certificate: "platform",
+ sdk_version: "system_current",
+ min_sdk_version: "29",
+}
diff --git a/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml
new file mode 100644
index 000000000000..7fe4bae2a3fe
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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="com.android.sample.rollbackapp" >
+ <uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
+ <application
+ android:label="@string/title_activity_main">
+ <activity
+ android:name="com.android.sample.rollbackapp.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/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml
new file mode 100644
index 000000000000..3fb987bb539c
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/layout/activity_main.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:id="@+id/trigger_rollback_button"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ style="?android:attr/buttonBarButtonStyle"
+ android:text="Rollback Selected" />
+
+ <ListView
+ android:id="@+id/listView"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerHorizontal"
+ android:dividerHeight="1dp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml
new file mode 100644
index 000000000000..f650dd5d2230
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/layout/listitem_rollbackinfo.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="10dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp">
+ <TextView android:id="@+id/rollback_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="20dp"/>
+ <TextView android:id="@+id/rollback_packages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16dp"/>
+ <CheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Roll Back"/>
+ </LinearLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml
new file mode 100644
index 000000000000..a85b6800a146
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <string name="title_activity_main" description="Launcher title">Rollback Sample App</string>
+</resources> \ No newline at end of file
diff --git a/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java
new file mode 100644
index 000000000000..157d19762925
--- /dev/null
+++ b/tests/RollbackTest/SampleRollbackApp/src/com/android/sample/rollbackapp/MainActivity.java
@@ -0,0 +1,161 @@
+/*
+ * 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.sample.rollbackapp;
+
+import static android.app.PendingIntent.FLAG_MUTABLE;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.rollback.PackageRollbackInfo;
+import android.content.rollback.RollbackInfo;
+import android.content.rollback.RollbackManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class MainActivity extends Activity {
+
+ List<Integer> mIdsToRollback = new ArrayList<>();
+ Button mTriggerRollbackButton;
+ RollbackManager mRollbackManager;
+ static final String ROLLBACK_ID_EXTRA = "rollbackId";
+ static final String ACTION_NAME = MainActivity.class.getName();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ ListView rollbackListView = findViewById(R.id.listView);
+ mRollbackManager = getApplicationContext().getSystemService(RollbackManager.class);
+ initTriggerRollbackButton();
+
+ // Populate list of available rollbacks.
+ List<RollbackInfo> availableRollbacks = mRollbackManager.getAvailableRollbacks();
+ CustomAdapter adapter = new CustomAdapter(availableRollbacks);
+ rollbackListView.setAdapter(adapter);
+
+ // Register receiver for rollback status events.
+ getApplicationContext().registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context,
+ Intent intent) {
+ int rollbackId = intent.getIntExtra(ROLLBACK_ID_EXTRA, -1);
+ int rollbackStatusCode = intent.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ String rollbackStatus = "FAILED";
+ if (rollbackStatusCode == RollbackManager.STATUS_SUCCESS) {
+ rollbackStatus = "SUCCESS";
+ mTriggerRollbackButton.setClickable(false);
+ }
+ makeToast("Status for rollback ID " + rollbackId + " is " + rollbackStatus);
+ }}, new IntentFilter(ACTION_NAME), Context.RECEIVER_NOT_EXPORTED);
+ }
+
+ private void initTriggerRollbackButton() {
+ mTriggerRollbackButton = findViewById(R.id.trigger_rollback_button);
+ mTriggerRollbackButton.setClickable(false);
+ mTriggerRollbackButton.setOnClickListener(v -> {
+ // Commits all selected rollbacks. Rollback status events will be sent to our receiver.
+ for (int i = 0; i < mIdsToRollback.size(); i++) {
+ Intent intent = new Intent(ACTION_NAME);
+ intent.putExtra(ROLLBACK_ID_EXTRA, mIdsToRollback.get(i));
+ intent.setPackage(getApplicationContext().getPackageName());
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ getApplicationContext(), 0, intent, FLAG_MUTABLE);
+ mRollbackManager.commitRollback(mIdsToRollback.get(i),
+ Collections.emptyList(),
+ pendingIntent.getIntentSender());
+ }
+ });
+ }
+
+
+
+ private void makeToast(String message) {
+ runOnUiThread(() -> Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show());
+ }
+
+ public class CustomAdapter extends BaseAdapter {
+ List<RollbackInfo> mRollbackInfos;
+ LayoutInflater mInflater = LayoutInflater.from(getApplicationContext());
+
+ CustomAdapter(List<RollbackInfo> rollbackInfos) {
+ mRollbackInfos = rollbackInfos;
+ }
+
+ @Override
+ public int getCount() {
+ return mRollbackInfos.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mRollbackInfos.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mRollbackInfos.get(position).getRollbackId();
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = mInflater.inflate(R.layout.listitem_rollbackinfo, null);
+ }
+ RollbackInfo rollbackInfo = mRollbackInfos.get(position);
+ TextView rollbackIdView = view.findViewById(R.id.rollback_id);
+ rollbackIdView.setText("Rollback ID " + rollbackInfo.getRollbackId());
+ TextView rollbackPackagesTextView = view.findViewById(R.id.rollback_packages);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < rollbackInfo.getPackages().size(); i++) {
+ PackageRollbackInfo pkgInfo = rollbackInfo.getPackages().get(i);
+ sb.append(pkgInfo.getPackageName() + ": "
+ + pkgInfo.getVersionRolledBackFrom().getLongVersionCode() + " -> "
+ + pkgInfo.getVersionRolledBackTo().getLongVersionCode() + ",");
+ }
+ sb.deleteCharAt(sb.length() - 1);
+ rollbackPackagesTextView.setText(sb.toString());
+ CheckBox checkbox = view.findViewById(R.id.checkbox);
+ checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mIdsToRollback.add(rollbackInfo.getRollbackId());
+ } else {
+ mIdsToRollback.remove(Integer.valueOf(rollbackInfo.getRollbackId()));
+ }
+ mTriggerRollbackButton.setClickable(mIdsToRollback.size() > 0);
+ });
+ return view;
+ }
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
index 947453d07bd9..ad0584724fdc 100644
--- a/tests/SharedLibraryLoadingTest/AndroidTest.xml
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -22,7 +22,6 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="false" />
<option name="remount-system" value="true" />
<option name="push"
value="SharedLibraryLoadingTests_StandardSharedLibrary.apk->/product/app/SharedLibraryLoadingTests_StandardSharedLibrary.apk" />
diff --git a/tests/SilkFX/Android.bp b/tests/SilkFX/Android.bp
index 088d9a2d7f41..1e467db44545 100644
--- a/tests/SilkFX/Android.bp
+++ b/tests/SilkFX/Android.bp
@@ -25,13 +25,17 @@ package {
android_test {
name: "SilkFX",
- srcs: ["**/*.java", "**/*.kt"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
platform_apis: true,
certificate: "platform",
- static_libs: [
+ static_libs: [
"androidx.core_core",
"androidx.appcompat_appcompat",
"com.google.android.material_material",
"androidx-constraintlayout_constraintlayout",
+ "subsampling-scale-image-view",
],
}
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index 21256d8c9d0b..c293589bdbaf 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -55,5 +55,20 @@
android:exported="true">
</activity>
+ <activity android:name=".app.HdrImageViewer"
+ android:label="HDR Gainmap Image Viewer"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="image/*"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/tests/SilkFX/assets/gainmaps/city_night.jpg b/tests/SilkFX/assets/gainmaps/city_night.jpg
new file mode 100644
index 000000000000..ba26ed6a5780
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/city_night.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_palms.jpg b/tests/SilkFX/assets/gainmaps/desert_palms.jpg
new file mode 100644
index 000000000000..048178670a96
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/desert_palms.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_sunset.jpg b/tests/SilkFX/assets/gainmaps/desert_sunset.jpg
new file mode 100644
index 000000000000..919a1574a4b9
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/desert_sunset.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/desert_wanda.jpg b/tests/SilkFX/assets/gainmaps/desert_wanda.jpg
new file mode 100644
index 000000000000..f5a2ef9c53ea
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/desert_wanda.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/fountain_night.jpg b/tests/SilkFX/assets/gainmaps/fountain_night.jpg
new file mode 100644
index 000000000000..d8b2d759e4c0
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/fountain_night.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/grand_canyon.jpg b/tests/SilkFX/assets/gainmaps/grand_canyon.jpg
new file mode 100644
index 000000000000..2f605bbb0a7e
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/grand_canyon.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/lamps.jpg b/tests/SilkFX/assets/gainmaps/lamps.jpg
new file mode 100644
index 000000000000..768665f643cb
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/lamps.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/mountain_lake.jpg b/tests/SilkFX/assets/gainmaps/mountain_lake.jpg
new file mode 100644
index 000000000000..b7981fdca6da
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/mountain_lake.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/mountains.jpg b/tests/SilkFX/assets/gainmaps/mountains.jpg
new file mode 100644
index 000000000000..fe69993e0706
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/mountains.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/sunflower.jpg b/tests/SilkFX/assets/gainmaps/sunflower.jpg
new file mode 100644
index 000000000000..4b17614d66bf
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/sunflower.jpg
Binary files differ
diff --git a/tests/SilkFX/assets/gainmaps/train_station_night.jpg b/tests/SilkFX/assets/gainmaps/train_station_night.jpg
new file mode 100644
index 000000000000..ecd45ee1e629
--- /dev/null
+++ b/tests/SilkFX/assets/gainmaps/train_station_night.jpg
Binary files differ
diff --git a/tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml b/tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.xml
new file mode 100644
index 000000000000..c183c5deab4f
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/blue_sweep_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#000000"
+ android:endColor="#0000FF"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/drawable-nodpi/dark_gradient.xml b/tests/SilkFX/res/drawable-nodpi/dark_gradient.xml
new file mode 100644
index 000000000000..f20dd424c617
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/dark_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#000000"
+ android:endColor="#222222"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml b/tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.xml
new file mode 100644
index 000000000000..c600d0f66325
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/green_sweep_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#000000"
+ android:endColor="#00FF00"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml b/tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.xml
new file mode 100644
index 000000000000..d0c17fa2e1b9
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/grey_sweep_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#000000"
+ android:endColor="#FFFFFF"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/drawable-nodpi/light_gradient.xml b/tests/SilkFX/res/drawable-nodpi/light_gradient.xml
new file mode 100644
index 000000000000..c75f925647e7
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/light_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#E8E8E8"
+ android:endColor="#FFFFFF"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml b/tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.xml
new file mode 100644
index 000000000000..e3b834a46406
--- /dev/null
+++ b/tests/SilkFX/res/drawable-nodpi/red_sweep_gradient.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#000000"
+ android:endColor="#FF0000"
+ android:angle="0"/>
+</shape> \ No newline at end of file
diff --git a/tests/SilkFX/res/layout/color_grid.xml b/tests/SilkFX/res/layout/color_grid.xml
new file mode 100644
index 000000000000..37242eee7195
--- /dev/null
+++ b/tests/SilkFX/res/layout/color_grid.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<com.android.test.silkfx.hdr.ColorGrid xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/tests/SilkFX/res/layout/common_base.xml b/tests/SilkFX/res/layout/common_base.xml
index 944c6846fbf7..c0eaf9bc1476 100644
--- a/tests/SilkFX/res/layout/common_base.xml
+++ b/tests/SilkFX/res/layout/common_base.xml
@@ -24,16 +24,6 @@
<FrameLayout android:id="@+id/demo_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <com.android.test.silkfx.common.HDRIndicator
- android:layout_width="match_parent"
- android:layout_height="50dp"
- android:layout_margin="8dp" />
+ android:layout_height="match_parent" />
</LinearLayout> \ No newline at end of file
diff --git a/tests/SilkFX/res/layout/gainmap_decode_test.xml b/tests/SilkFX/res/layout/gainmap_decode_test.xml
new file mode 100644
index 000000000000..e7ef61f8dac1
--- /dev/null
+++ b/tests/SilkFX/res/layout/gainmap_decode_test.xml
@@ -0,0 +1,115 @@
+<?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.
+ -->
+
+<com.android.test.silkfx.hdr.GainmapDecodeTest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/decode_full"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Decode full" />
+
+ <Button
+ android:id="@+id/decode_subsampled4"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Decode subsampled (1/4th)" />
+
+ <Button
+ android:id="@+id/decode_scaled66"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Decode scaled (66%)" />
+
+ <Button
+ android:id="@+id/decode_crop"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Decode croppedSquare" />
+
+ <Button
+ android:id="@+id/decode_cropScaled33"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Decode croppedSquare(33%)" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/source_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/sdr_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/sdr_source"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="8dp"
+ android:scaleType="fitStart" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/gainmap_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/gainmap"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="8dp"
+ android:scaleType="fitStart" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</com.android.test.silkfx.hdr.GainmapDecodeTest> \ No newline at end of file
diff --git a/tests/SilkFX/res/layout/gainmap_image.xml b/tests/SilkFX/res/layout/gainmap_image.xml
new file mode 100644
index 000000000000..b0ed9147585e
--- /dev/null
+++ b/tests/SilkFX/res/layout/gainmap_image.xml
@@ -0,0 +1,91 @@
+<?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.
+ -->
+
+<com.android.test.silkfx.hdr.GainmapImage xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/gainmap_image">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <RadioGroup android:id="@+id/output_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <RadioButton android:id="@+id/output_sdr"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="SDR" />
+
+ <RadioButton android:id="@+id/output_gainmap"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Gainmap" />
+
+ <RadioButton android:id="@+id/output_hdr"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="HDR" />
+
+ <RadioButton android:id="@+id/output_hdr_test"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="HDR (test)" />
+ </RadioGroup>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Spinner
+ android:id="@+id/image_selection"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/gainmap_metadata"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Gainmap Metadata..." />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/error_msg"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" />
+
+ <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+ android:id="@+id/image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
+
+</com.android.test.silkfx.hdr.GainmapImage>
diff --git a/tests/SilkFX/res/layout/gainmap_metadata.xml b/tests/SilkFX/res/layout/gainmap_metadata.xml
new file mode 100644
index 000000000000..4cc3e0cbdb83
--- /dev/null
+++ b/tests/SilkFX/res/layout/gainmap_metadata.xml
@@ -0,0 +1,264 @@
+<?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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:padding="8dp"
+ android:orientation="vertical"
+ android:background="#444444">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:text="Metadata for &quot;HDR (test)&quot; (values in linear space):" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gainmapmin_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Gain Map Min:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gainmapmin_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_gainmapmin"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gainmapmax_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Gain Map Max:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gainmapmax_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_gainmapmax"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_capacitymin_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Capacity Min:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_capacitymin_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_capacitymin"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_capacitymax_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Capacity Max:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_capacitymax_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_capacitymax"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gamma_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Gamma:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_gamma_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_gamma"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_offsetsdr_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Offset SDR:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_offsetsdr_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_offsetsdr"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/gainmap_metadata_offsethdr_text"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Offset HDR:" />
+
+ <TextView
+ android:id="@+id/gainmap_metadata_offsethdr_val"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="4"
+ android:text="TODO" />
+
+ <SeekBar
+ android:id="@+id/gainmap_metadata_offsethdr"
+ android:min="0"
+ android:max="100"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/gainmap_metadata_reset"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Reset" />
+
+ <Button
+ android:id="@+id/gainmap_metadata_done"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:text="Done" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/tests/SilkFX/res/layout/gainmap_transform_test.xml b/tests/SilkFX/res/layout/gainmap_transform_test.xml
new file mode 100644
index 000000000000..5aeb53661cbc
--- /dev/null
+++ b/tests/SilkFX/res/layout/gainmap_transform_test.xml
@@ -0,0 +1,122 @@
+<?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.
+ -->
+
+<com.android.test.silkfx.hdr.GainmapTransformsTest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/original"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Original" />
+
+ <Button
+ android:id="@+id/scaled"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Scaled (1/3)" />
+
+ <Button
+ android:id="@+id/rotate_90"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Rotate 90" />
+
+ <Button
+ android:id="@+id/rotate_90_scaled"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Rot90+Scale" />
+
+ <Button
+ android:id="@+id/crop"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Crop" />
+
+ <Button
+ android:id="@+id/crop_200"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="Crop 200" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/source_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/sdr_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/sdr_source"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="8dp"
+ android:scaleType="fitStart" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_weight="1">
+
+ <TextView
+ android:id="@+id/gainmap_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <ImageView
+ android:id="@+id/gainmap"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="8dp"
+ android:scaleType="fitStart" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</com.android.test.silkfx.hdr.GainmapTransformsTest> \ No newline at end of file
diff --git a/tests/SilkFX/res/layout/gradient_sweep.xml b/tests/SilkFX/res/layout/gradient_sweep.xml
new file mode 100644
index 000000000000..261022a40380
--- /dev/null
+++ b/tests/SilkFX/res/layout/gradient_sweep.xml
@@ -0,0 +1,53 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <View android:background="@drawable/dark_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <View android:background="@drawable/light_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <View android:background="@drawable/grey_sweep_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <View android:background="@drawable/red_sweep_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <View android:background="@drawable/green_sweep_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <View android:background="@drawable/blue_sweep_gradient"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/SilkFX/res/layout/hdr_image_viewer.xml b/tests/SilkFX/res/layout/hdr_image_viewer.xml
new file mode 100644
index 000000000000..9816430cd915
--- /dev/null
+++ b/tests/SilkFX/res/layout/hdr_image_viewer.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/color_mode_controls" />
+ <include layout="@layout/gainmap_image" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 7132ae8772ea..59a6078376cf 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -26,6 +26,7 @@ import android.widget.BaseExpandableListAdapter
import android.widget.ExpandableListView
import android.widget.TextView
import com.android.test.silkfx.app.CommonDemoActivity
+import com.android.test.silkfx.app.EXTRA_COMMON_CONTROLS
import com.android.test.silkfx.app.EXTRA_LAYOUT
import com.android.test.silkfx.app.EXTRA_TITLE
import com.android.test.silkfx.hdr.GlowActivity
@@ -37,10 +38,11 @@ class Demo(val name: String, val makeIntent: (Context) -> Intent) {
constructor(name: String, activity: KClass<out Activity>) : this(name, { context ->
Intent(context, activity.java)
})
- constructor(name: String, layout: Int) : this(name, { context ->
+ constructor(name: String, layout: Int, commonControls: Boolean = true) : this(name, { context ->
Intent(context, CommonDemoActivity::class.java).apply {
putExtra(EXTRA_LAYOUT, layout)
putExtra(EXTRA_TITLE, name)
+ putExtra(EXTRA_COMMON_CONTROLS, commonControls)
}
})
}
@@ -49,7 +51,13 @@ data class DemoGroup(val groupName: String, val demos: List<Demo>)
private val AllDemos = listOf(
DemoGroup("HDR", listOf(
Demo("Glow", GlowActivity::class),
- Demo("Blingy Notifications", R.layout.bling_notifications)
+ Demo("Blingy Notifications", R.layout.bling_notifications),
+ Demo("Color Grid", R.layout.color_grid),
+ Demo("Gradient Sweep", R.layout.gradient_sweep),
+ Demo("Gainmap Image", R.layout.gainmap_image),
+ Demo("Gainmap Decode Test", R.layout.gainmap_decode_test, commonControls = false),
+ Demo("Gainmap Transform Test", R.layout.gainmap_transform_test,
+ commonControls = false)
)),
DemoGroup("Materials", listOf(
Demo("Glass", GlassActivity::class),
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
index e0a0a20bc0a0..e56ce40463f4 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/CommonDemoActivity.kt
@@ -22,6 +22,7 @@ import android.view.LayoutInflater
const val EXTRA_LAYOUT = "layout"
const val EXTRA_TITLE = "title"
+const val EXTRA_COMMON_CONTROLS = "common_controls"
class CommonDemoActivity : BaseDemoActivity() {
@@ -38,8 +39,13 @@ class CommonDemoActivity : BaseDemoActivity() {
val title = extras.getString(EXTRA_TITLE, "SilkFX")
window.setTitle(title)
- setContentView(R.layout.common_base)
+ if (extras.getBoolean(EXTRA_COMMON_CONTROLS, true)) {
+ setContentView(R.layout.common_base)
+ LayoutInflater.from(this).inflate(layout, findViewById(R.id.demo_container), true)
+ } else {
+ setContentView(layout)
+ }
+
actionBar?.title = title
- LayoutInflater.from(this).inflate(layout, findViewById(R.id.demo_container), true)
}
} \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt b/tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt
new file mode 100644
index 000000000000..7302169f4d1b
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/app/HdrImageViewer.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.silkfx.app
+
+import android.content.Intent
+import android.graphics.ImageDecoder
+import android.net.Uri
+import android.os.Bundle
+import android.os.Parcelable
+import com.android.test.silkfx.R
+import com.android.test.silkfx.hdr.GainmapImage
+
+class HdrImageViewer : BaseDemoActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.hdr_image_viewer)
+
+ val intent = this.intent ?: return finish()
+ when (intent.action) {
+ Intent.ACTION_VIEW -> {
+ val data = intent.data ?: return finish()
+ val source = ImageDecoder.createSource(contentResolver, data)
+ findViewById<GainmapImage>(R.id.gainmap_image)!!.setImageSource(source)
+ }
+ Intent.ACTION_SEND -> {
+ val uri = (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri)
+ ?: return finish()
+ val source = ImageDecoder.createSource(contentResolver, uri)
+ findViewById<GainmapImage>(R.id.gainmap_image)!!.setImageSource(source)
+ }
+ else -> {
+ finish()
+ }
+ }
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
index 4b85953a24b9..f88e6b01483b 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/BaseDrawingView.kt
@@ -23,8 +23,11 @@ import android.util.AttributeSet
import android.view.View
open class BaseDrawingView : View {
+ val sRGB = ColorSpace.get(ColorSpace.Named.SRGB)
+ val displayP3 = ColorSpace.get(ColorSpace.Named.DISPLAY_P3)
val scRGB = ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)
val bt2020 = ColorSpace.get(ColorSpace.Named.BT2020)
+ val bt2020_pq = ColorSpace.get(ColorSpace.Named.BT2020_PQ)
val lab = ColorSpace.get(ColorSpace.Named.CIE_LAB)
val density: Float
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 b41ee3a9ef2c..56ab755af47b 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -18,38 +18,24 @@ package com.android.test.silkfx.common
import android.content.Context
import android.content.pm.ActivityInfo
-import android.hardware.display.DisplayManager
-import android.os.IBinder
import android.util.AttributeSet
import android.util.Log
-import android.view.SurfaceControl
-import android.view.SurfaceControlHdrLayerInfoListener
+import android.view.Display
+import android.view.View
import android.view.Window
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import com.android.test.silkfx.R
import com.android.test.silkfx.app.WindowObserver
+import java.util.function.Consumer
class ColorModeControls : LinearLayout, WindowObserver {
- private val COLOR_MODE_HDR10 = 3
- private val SDR_WHITE_POINTS = floatArrayOf(200f, 250f, 300f, 350f, 400f, 100f, 150f)
-
constructor(context: Context) : this(context, null)
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
- displayManager = context.getSystemService(DisplayManager::class.java)!!
- displayId = context.getDisplayId()
- displayToken = SurfaceControl.getInternalDisplayToken()
- }
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
private var window: Window? = null
private var currentModeDisplay: TextView? = null
- private val displayManager: DisplayManager
- private var targetSdrWhitePointIndex = 0
- private var displayId: Int
- private var displayToken: IBinder
-
- private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
override fun onFinishInflate() {
super.onFinishInflate()
@@ -68,83 +54,54 @@ class ColorModeControls : LinearLayout, WindowObserver {
setColorMode(ActivityInfo.COLOR_MODE_HDR)
}
findViewById<Button>(R.id.mode_hdr10)!!.setOnClickListener {
- setColorMode(COLOR_MODE_HDR10)
+ setColorMode(ActivityInfo.COLOR_MODE_HDR10)
}
}
- private fun setColorMode(newMode: Int) {
- val window = window!!
- var sdrWhitepointChanged = false
- // Need to do this before setting the colorMode, as setting the colorMode will
- // trigger the attribute change listener
- if (newMode == ActivityInfo.COLOR_MODE_HDR ||
- newMode == COLOR_MODE_HDR10) {
- if (window.colorMode == newMode) {
- targetSdrWhitePointIndex = (targetSdrWhitePointIndex + 1) % SDR_WHITE_POINTS.size
- sdrWhitepointChanged = true
- }
- setBrightness(1.0f)
- } else {
- setBrightness(.4f)
- }
- window.colorMode = newMode
- if (sdrWhitepointChanged) {
- threadedRenderer?.setColorMode(newMode, whitePoint)
- }
- val whitePoint = whitePoint.toInt()
- currentModeDisplay?.run {
- text = "Current Mode: " + when (newMode) {
- ActivityInfo.COLOR_MODE_DEFAULT -> "Default/SRGB"
- ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT -> "Wide Gamut"
- ActivityInfo.COLOR_MODE_HDR -> "HDR (sdr white point $whitePoint)"
- COLOR_MODE_HDR10 -> "HDR10 (sdr white point $whitePoint)"
- else -> "Unknown"
- }
+ private val hdrSdrListener = Consumer<Display> { display ->
+ Log.d("SilkFX", "HDR/SDR changed ${display.hdrSdrRatio}")
+ post {
+ updateModeInfoDisplay()
}
}
- override fun setWindow(window: Window) {
- this.window = window
- }
-
- private fun setBrightness(level: Float) {
- // To keep window state in sync
- window?.attributes?.screenBrightness = level
- invalidate()
- // To force an 'immediate' snap to what we want
- // Imperfect, but close enough, synchronization by waiting for frame commit to set the value
- viewTreeObserver.registerFrameCommitCallback {
- try {
- SurfaceControl.setDisplayBrightness(displayToken, level)
- displayManager.setTemporaryBrightness(displayId, level)
- } catch (ex: Exception) {
- // Ignore a permission denied rejection - it doesn't meaningfully change much
- }
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ val hdrVis = if (display.isHdrSdrRatioAvailable) {
+ display.registerHdrSdrRatioChangedListener({ it.run() }, hdrSdrListener)
+ View.VISIBLE
+ } else {
+ View.GONE
}
+ findViewById<Button>(R.id.mode_hdr)!!.visibility = hdrVis
+ findViewById<Button>(R.id.mode_hdr10)!!.visibility = hdrVis
}
- private val listener = object : SurfaceControlHdrLayerInfoListener() {
- override fun onHdrInfoChanged(
- displayToken: IBinder?,
- numberOfHdrLayers: Int,
- maxW: Int,
- maxH: Int,
- flags: Int
- ) {
- Log.d("HDRInfo", "onHdrInfoChanged: numLayer = $numberOfHdrLayers ($maxW x $maxH)" +
- ", flags = $flags")
- }
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ display.unregisterHdrSdrRatioChangedListener(hdrSdrListener)
}
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
+ private fun setColorMode(newMode: Int) {
+ window!!.colorMode = newMode
+ updateModeInfoDisplay()
+ }
- threadedRenderer?.setColorMode(window!!.colorMode, whitePoint)
- listener.register(displayToken)
+ override fun setWindow(window: Window) {
+ this.window = window
}
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- listener.unregister(displayToken)
+ private fun updateModeInfoDisplay() {
+ val sdrHdrRatio = display?.hdrSdrRatio ?: 1.0f
+ val colormode = window!!.colorMode
+ currentModeDisplay?.run {
+ text = "Current Mode: " + when (colormode) {
+ ActivityInfo.COLOR_MODE_DEFAULT -> "Default/SRGB"
+ ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT -> "Wide Gamut"
+ ActivityInfo.COLOR_MODE_HDR -> "HDR (sdr/hdr ratio $sdrHdrRatio)"
+ ActivityInfo.COLOR_MODE_HDR10 -> "HDR10 (sdr/hdr ratio $sdrHdrRatio)"
+ else -> "Unknown"
+ }
+ }
}
-} \ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt
new file mode 100644
index 000000000000..6920f832333f
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/ColorGrid.kt
@@ -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.
+ */
+
+package com.android.test.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorSpace
+import android.graphics.Paint
+import android.graphics.Rect
+import android.util.AttributeSet
+import com.android.test.silkfx.common.BaseDrawingView
+
+class ColorGrid(context: Context, attrs: AttributeSet?) : BaseDrawingView(context, attrs) {
+
+ init {
+ isClickable = true
+ setOnClickListener {
+ invalidate()
+ }
+ }
+
+ fun toMaxColor(color: Int, colorspace: ColorSpace): Long {
+ val red = (Color.red(color) / 255f) * colorspace.getMaxValue(0)
+ val green = (Color.green(color) / 255f) * colorspace.getMaxValue(1)
+ val blue = (Color.blue(color) / 255f) * colorspace.getMaxValue(2)
+ val alpha = Color.alpha(color) / 255f
+ return Color.pack(red, green, blue, alpha, colorspace)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ val paint = Paint()
+ paint.isDither = true
+ paint.isAntiAlias = true
+ paint.textSize = 18.dp()
+ paint.textAlign = Paint.Align.LEFT
+
+ val labels = arrayOf("sRGB", "Display P3", "BT2020_PQ", "scRGB(max)")
+ val colorSpaces = arrayOf(sRGB, displayP3, bt2020_pq, scRGB)
+
+ val colWidth = width / colorSpaces.size.toFloat()
+ val rowHeight = minOf((height - 20.dp()) / 4f, colWidth)
+
+ val dest = Rect(0, 0, rowHeight.toInt(), colWidth.toInt())
+
+ for (colIndex in labels.indices) {
+ canvas.save()
+ canvas.translate(colIndex * colWidth, 20.dp())
+
+ paint.color = Color.WHITE
+ canvas.drawText(labels[colIndex], 0f, 1f, paint)
+
+ arrayOf(Color.WHITE, Color.RED, Color.BLUE, Color.GREEN).forEach {
+ paint.setColor(toMaxColor(it, colorSpaces[colIndex]))
+ canvas.drawRect(dest, paint)
+ canvas.translate(0f, rowHeight)
+ }
+ canvas.restore()
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt
new file mode 100644
index 000000000000..585320aee615
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapDecodeTest.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Gainmap
+import android.graphics.ImageDecoder
+import android.graphics.Paint
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.test.silkfx.R
+
+enum class DecodeMode {
+ Full,
+ Subsampled4,
+ Scaled66,
+ CropedSquared,
+ CropedSquaredScaled33
+}
+
+fun gainmapVisualizer(gainmap: Gainmap): Bitmap {
+ val map = gainmap.gainmapContents
+ val gainmapVisualizer = Bitmap.createBitmap(map.width, map.height,
+ Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(gainmapVisualizer!!)
+ val paint = Paint()
+ paint.colorFilter = ColorMatrixColorFilter(
+ floatArrayOf(
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 0f, 255f
+ )
+ )
+ canvas.drawBitmap(map, 0f, 0f, paint)
+ canvas.setBitmap(null)
+ return gainmapVisualizer
+}
+
+class GainmapDecodeTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+
+ private fun decode(mode: DecodeMode) {
+ val source = ImageDecoder.createSource(resources.assets,
+ "gainmaps/${context.assets.list("gainmaps")!![0]}")
+
+ val sourceInfo = findViewById<TextView>(R.id.source_info)!!
+
+ val gainmapImage = ImageDecoder.decodeBitmap(source) { decoder, info, source ->
+ decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+ sourceInfo.text =
+ "Original size ${info.size.width}x${info.size.height}; mime-type = ${info.mimeType}"
+
+ when (mode) {
+ DecodeMode.Full -> {}
+ DecodeMode.Subsampled4 -> {
+ decoder.setTargetSampleSize(4)
+ }
+ DecodeMode.Scaled66 -> {
+ val size = info.size
+ decoder.setTargetSize((size.width * .66).toInt(), (size.height * .66).toInt())
+ }
+ DecodeMode.CropedSquared -> {
+ val dimen = minOf(info.size.width, info.size.height)
+ decoder.crop = Rect(50, 50, dimen - 100, dimen - 100)
+ }
+ DecodeMode.CropedSquaredScaled33 -> {
+ val size = info.size
+ val targetWidth = (size.width * .33).toInt()
+ val targetHeight = (size.height * .33).toInt()
+ decoder.setTargetSize(targetWidth, targetHeight)
+ val dimen = minOf(targetWidth, targetHeight)
+ decoder.crop = Rect(50, 50, dimen - 100, dimen - 100)
+ }
+ }
+ }
+
+ val gainmapContents = gainmapImage.gainmap?.let { gainmapVisualizer(it) }
+ val sdrBitmap = gainmapImage.also { it.gainmap = null }
+
+ findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap)
+ findViewById<TextView>(R.id.sdr_label)!!.text =
+ "SDR Size: ${sdrBitmap.width}x${sdrBitmap.height}"
+
+ findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents)
+ findViewById<TextView>(R.id.gainmap_label)!!.text =
+ "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}"
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ decode(DecodeMode.Full)
+
+ findViewById<Button>(R.id.decode_full)!!.setOnClickListener {
+ decode(DecodeMode.Full)
+ }
+ findViewById<Button>(R.id.decode_subsampled4)!!.setOnClickListener {
+ decode(DecodeMode.Subsampled4)
+ }
+ findViewById<Button>(R.id.decode_scaled66)!!.setOnClickListener {
+ decode(DecodeMode.Scaled66)
+ }
+ findViewById<Button>(R.id.decode_crop)!!.setOnClickListener {
+ decode(DecodeMode.CropedSquared)
+ }
+ findViewById<Button>(R.id.decode_cropScaled33)!!.setOnClickListener {
+ decode(DecodeMode.CropedSquaredScaled33)
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
new file mode 100644
index 000000000000..7cf69b7780d9
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapImage.kt
@@ -0,0 +1,205 @@
+/*
+ * 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.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.Gainmap
+import android.graphics.ImageDecoder
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Button
+import android.widget.FrameLayout
+import android.widget.RadioGroup
+import android.widget.Spinner
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.davemorrissey.labs.subscaleview.ImageSource
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+
+class GainmapImage(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+ private val gainmapImages: Array<String>
+ private var selectedImage = -1
+ private var outputMode = R.id.output_hdr
+ private var bitmap: Bitmap? = null
+ private var gainmap: Gainmap? = null
+ private var gainmapVisualizer: Bitmap? = null
+ private lateinit var imageView: SubsamplingScaleImageView
+ private lateinit var gainmapMetadataEditor: GainmapMetadataEditor
+
+ init {
+ gainmapImages = context.assets.list("gainmaps")!!
+ }
+
+ fun setImageSource(source: ImageDecoder.Source) {
+ findViewById<Spinner>(R.id.image_selection)!!.visibility = View.GONE
+ doDecode(source)
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+
+ imageView = findViewById(R.id.image)!!
+ gainmapMetadataEditor = GainmapMetadataEditor(this, imageView)
+
+ 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()
+ }
+ }
+ }
+
+ val spinner = findViewById<Spinner>(R.id.image_selection)!!
+ val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, gainmapImages)
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ spinner.adapter = adapter
+ spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(
+ parent: AdapterView<*>?,
+ view: View?,
+ position: Int,
+ id: Long
+ ) {
+ setImage(position)
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ }
+ }
+
+ findViewById<Button>(R.id.gainmap_metadata)!!.setOnClickListener {
+ gainmapMetadataEditor.openEditor()
+ }
+
+ setImage(0)
+
+ imageView.apply {
+ isClickable = true
+ setOnClickListener {
+ animate().alpha(.5f).withEndAction {
+ animate().alpha(1f).start()
+ }.start()
+ }
+ }
+ }
+
+ private fun setImage(position: Int) {
+ if (selectedImage == position) return
+ selectedImage = position
+ val source = ImageDecoder.createSource(resources.assets,
+ "gainmaps/${gainmapImages[position]}")
+ doDecode(source)
+ }
+
+ private fun doDecode(source: ImageDecoder.Source) {
+ gainmap = null
+ bitmap = ImageDecoder.decodeBitmap(source) { decoder, info, source ->
+ decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+ }
+ if (!bitmap!!.hasGainmap()) {
+ outputMode = R.id.output_sdr
+ findViewById<TextView>(R.id.error_msg)!!.also {
+ it.visibility = View.VISIBLE
+ it.text = "Image doesn't have a gainmap, only showing in SDR"
+ }
+ findViewById<RadioGroup>(R.id.output_mode)!!.also {
+ it.check(R.id.output_sdr)
+ it.visibility = View.GONE
+ }
+ } else {
+ 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
+ if (map.config != Bitmap.Config.ALPHA_8) {
+ gainmapVisualizer = map
+ } else {
+ gainmapVisualizer = Bitmap.createBitmap(map.width, map.height,
+ Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(gainmapVisualizer!!)
+ val paint = Paint()
+ paint.colorFilter = ColorMatrixColorFilter(
+ floatArrayOf(
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 1f, 0f,
+ 0f, 0f, 0f, 0f, 255f
+ )
+ )
+ canvas.drawBitmap(map, 0f, 0f, paint)
+ canvas.setBitmap(null)
+ }
+ }
+
+ 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!!
+ }
+
+ R.id.output_hdr_test -> {
+ gainmapMetadataEditor.useEditMetadata()
+ bitmap!!.gainmap = gainmap
+ bitmap!!
+ }
+
+ R.id.output_sdr -> {
+ bitmap!!.gainmap = null; bitmap!!
+ }
+
+ R.id.output_gainmap -> gainmapVisualizer!!
+ else -> throw IllegalStateException()
+ }))
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
new file mode 100644
index 000000000000..8a653045c97b
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapMetadataEditor.kt
@@ -0,0 +1,284 @@
+/*
+ * 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.silkfx.hdr
+
+import android.graphics.Gainmap
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.PopupWindow
+import android.widget.SeekBar
+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
+)
+
+class GainmapMetadataEditor(val parent: ViewGroup, val renderView: View) {
+ private var gainmap: Gainmap? = null
+ private var showingEdits = false
+
+ private var metadataPopup: PopupWindow? = null
+
+ private var originalMetadata: GainmapMetadata = GainmapMetadata(
+ 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
+
+ private val minRatioMin = .001f
+ private val maxRatioMin = 1.0f
+ private val minRatioMax = 1.0f
+ private val maxRatioMax = 16.0f
+ private val minCapacityMin = 1.0f
+ private val maxCapacityMin = maxRatioMax
+ private val minCapacityMax = 1.001f
+ private val maxCapacityMax = maxRatioMax
+ private val minGamma = 0.1f
+ private val maxGamma = 3.0f
+ // Min and max offsets are 0.0 and 1.0 respectively
+
+ 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])
+ currentMetadata = originalMetadata.copy()
+ }
+
+ fun useOriginalMetadata() {
+ showingEdits = false
+ applyMetadata(originalMetadata)
+ }
+
+ fun useEditMetadata() {
+ showingEdits = true
+ applyMetadata(currentMetadata)
+ }
+
+ fun closeEditor() {
+ metadataPopup?.let {
+ it.dismiss()
+ metadataPopup = null
+ }
+ }
+
+ fun openEditor() {
+ if (metadataPopup != null) return
+
+ val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gainmap_metadata, null)
+
+ metadataPopup = PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT)
+ metadataPopup!!.showAtLocation(view, Gravity.CENTER, 0, 0)
+
+ (view.getParent() as ViewGroup).removeView(view)
+ parent.addView(view)
+
+ view.findViewById<Button>(R.id.gainmap_metadata_done)!!.setOnClickListener {
+ closeEditor()
+ }
+
+ view.findViewById<Button>(R.id.gainmap_metadata_reset)!!.setOnClickListener {
+ resetGainmapMetadata()
+ }
+
+ updateMetadataUi()
+
+ val gainmapMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = view.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+ arrayOf(gainmapMinSeek, gainmapMaxSeek, capacityMinSeek, capacityMaxSeek, gammaSeek,
+ offsetSdrSeek, offsetHdrSeek).forEach {
+ it.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener{
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ if (!fromUser) return
+ val normalized = progress.toFloat() / maxProgress
+ when (seekBar) {
+ gainmapMinSeek -> updateGainmapMin(normalized)
+ gainmapMaxSeek -> updateGainmapMax(normalized)
+ capacityMinSeek -> updateCapacityMin(normalized)
+ capacityMaxSeek -> updateCapacityMax(normalized)
+ gammaSeek -> updateGamma(normalized)
+ offsetSdrSeek -> updateOffsetSdr(normalized)
+ offsetHdrSeek -> updateOffsetHdr(normalized)
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {}
+ override fun onStopTrackingTouch(seekBar: SeekBar) {}
+ })
+ }
+ }
+
+ private fun updateMetadataUi() {
+ val gainmapMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmin)
+ val gainmapMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gainmapmax)
+ val capacityMinSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymin)
+ val capacityMaxSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_capacitymax)
+ val gammaSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_gamma)
+ val offsetSdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsetsdr)
+ val offsetHdrSeek = parent.findViewById<SeekBar>(R.id.gainmap_metadata_offsethdr)
+
+ gainmapMinSeek.setProgress(
+ ((currentMetadata.ratioMin - minRatioMin) / maxRatioMin * maxProgress).toInt())
+ gainmapMaxSeek.setProgress(
+ ((currentMetadata.ratioMax - minRatioMax) / maxRatioMax * maxProgress).toInt())
+ capacityMinSeek.setProgress(
+ ((currentMetadata.capacityMin - minCapacityMin) / maxCapacityMin * maxProgress).toInt())
+ capacityMaxSeek.setProgress(
+ ((currentMetadata.capacityMax - minCapacityMax) / maxCapacityMax * maxProgress).toInt())
+ gammaSeek.setProgress(
+ ((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())
+ offsetHdrSeek.setProgress(
+ ((1.0 - Math.log(currentMetadata.offsetHdr.toDouble() / Math.log(3.0)) / -11.0)
+ .toFloat() * maxProgress).toInt())
+
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ "%.3f".format(currentMetadata.ratioMin))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ "%.3f".format(currentMetadata.ratioMax))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ "%.3f".format(currentMetadata.capacityMin))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ "%.3f".format(currentMetadata.capacityMax))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ "%.3f".format(currentMetadata.gamma))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ "%.5f".format(currentMetadata.offsetSdr))
+ parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ "%.5f".format(currentMetadata.offsetHdr))
+ }
+
+ private fun resetGainmapMetadata() {
+ currentMetadata = originalMetadata.copy()
+ applyMetadata(currentMetadata)
+ updateMetadataUi()
+ }
+
+ 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)
+ renderView.invalidate()
+ }
+
+ private fun updateGainmapMin(normalized: Float) {
+ val newValue = minRatioMin + normalized * (maxRatioMin - minRatioMin)
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmin_val)!!.setText(
+ "%.3f".format(newValue))
+ currentMetadata.ratioMin = newValue
+ if (showingEdits) {
+ gainmap!!.setRatioMin(newValue, newValue, newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateGainmapMax(normalized: Float) {
+ val newValue = minRatioMax + normalized * (maxRatioMax - minRatioMax)
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gainmapmax_val)!!.setText(
+ "%.3f".format(newValue))
+ currentMetadata.ratioMax = newValue
+ if (showingEdits) {
+ gainmap!!.setRatioMax(newValue, newValue, newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateCapacityMin(normalized: Float) {
+ val newValue = minCapacityMin + normalized * (maxCapacityMin - minCapacityMin)
+ parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymin_val)!!.setText(
+ "%.3f".format(newValue))
+ currentMetadata.capacityMin = newValue
+ if (showingEdits) {
+ gainmap!!.setMinDisplayRatioForHdrTransition(newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateCapacityMax(normalized: Float) {
+ val newValue = minCapacityMax + normalized * (maxCapacityMax - minCapacityMax)
+ parent.findViewById<TextView>(R.id.gainmap_metadata_capacitymax_val)!!.setText(
+ "%.3f".format(newValue))
+ currentMetadata.capacityMax = newValue
+ if (showingEdits) {
+ gainmap!!.setDisplayRatioForFullHdr(newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateGamma(normalized: Float) {
+ val newValue = minGamma + normalized * (maxGamma - minGamma)
+ parent.findViewById<TextView>(R.id.gainmap_metadata_gamma_val)!!.setText(
+ "%.3f".format(newValue))
+ currentMetadata.gamma = newValue
+ if (showingEdits) {
+ gainmap!!.setGamma(newValue, newValue, newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateOffsetSdr(normalized: Float) {
+ var newValue = 0.0f
+ if (normalized > 0.0f ) {
+ newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
+ }
+ parent.findViewById<TextView>(R.id.gainmap_metadata_offsetsdr_val)!!.setText(
+ "%.5f".format(newValue))
+ currentMetadata.offsetSdr = newValue
+ if (showingEdits) {
+ gainmap!!.setEpsilonSdr(newValue, newValue, newValue)
+ renderView.invalidate()
+ }
+ }
+
+ private fun updateOffsetHdr(normalized: Float) {
+ var newValue = 0.0f
+ if (normalized > 0.0f ) {
+ newValue = Math.pow(3.0, (1.0 - normalized.toDouble()) * -11.0).toFloat()
+ }
+ parent.findViewById<TextView>(R.id.gainmap_metadata_offsethdr_val)!!.setText(
+ "%.5f".format(newValue))
+ currentMetadata.offsetHdr = newValue
+ if (showingEdits) {
+ gainmap!!.setEpsilonHdr(newValue, newValue, newValue)
+ renderView.invalidate()
+ }
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt
new file mode 100644
index 000000000000..20984fae2133
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/GainmapTransformsTest.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.silkfx.hdr
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.util.AttributeSet
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.test.silkfx.R
+
+class GainmapTransformsTest(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+
+ private val sourceImage = loadSample()
+
+ private fun loadSample(): Bitmap {
+ val source = ImageDecoder.createSource(resources.assets,
+ "gainmaps/${context.assets.list("gainmaps")!![0]}")
+
+ return ImageDecoder.decodeBitmap(source) { decoder, info, source ->
+ decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+ }
+ }
+
+ private fun process(transform: (Bitmap) -> Bitmap) {
+ val result = transform(sourceImage)
+
+ val gainmapContents = result.gainmap?.let { gainmapVisualizer(it) }
+ val sdrBitmap = result.also { it.gainmap = null }
+
+ findViewById<ImageView>(R.id.sdr_source)!!.setImageBitmap(sdrBitmap)
+ findViewById<TextView>(R.id.sdr_label)!!.text =
+ "SDR Size: ${sdrBitmap.width}x${sdrBitmap.height}"
+
+ findViewById<ImageView>(R.id.gainmap)!!.setImageBitmap(gainmapContents)
+ findViewById<TextView>(R.id.gainmap_label)!!.text =
+ "Gainmap Size: ${gainmapContents?.width ?: 0}x${gainmapContents?.height ?: 0}"
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ val sourceInfo = findViewById<TextView>(R.id.source_info)!!
+ sourceInfo.text = "Original size ${sourceImage.width}x${sourceImage.height}"
+ process { it.copy(Bitmap.Config.ARGB_8888, false) }
+
+ findViewById<Button>(R.id.original)!!.setOnClickListener {
+ process { it.copy(Bitmap.Config.ARGB_8888, false) }
+ }
+
+ findViewById<Button>(R.id.scaled)!!.setOnClickListener {
+ process { Bitmap.createScaledBitmap(it, it.width / 3, it.height / 3, true) }
+ }
+
+ findViewById<Button>(R.id.rotate_90)!!.setOnClickListener {
+ process {
+ val width: Int = it.width
+ val height: Int = it.height
+
+ val m = Matrix()
+ m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat())
+ Bitmap.createBitmap(it, 0, 0, width, height, m, false)
+ }
+ }
+
+ findViewById<Button>(R.id.rotate_90_scaled)!!.setOnClickListener {
+ process {
+ val width: Int = it.width
+ val height: Int = it.height
+
+ val m = Matrix()
+ m.setRotate(90.0f, (width / 2).toFloat(), (height / 2).toFloat())
+ m.preScale(.3f, .3f)
+ Bitmap.createBitmap(it, 0, 0, width, height, m, false)
+ }
+ }
+
+ findViewById<Button>(R.id.crop)!!.setOnClickListener {
+ process {
+ val width: Int = it.width
+ val height: Int = it.height
+ Bitmap.createBitmap(it, width / 2, height / 2,
+ width / 4, height / 4, null, false)
+ }
+ }
+
+ findViewById<Button>(R.id.crop_200)!!.setOnClickListener {
+ process {
+ val width: Int = it.width
+ val height: Int = it.height
+
+ val m = Matrix()
+ m.setRotate(200.0f, (width / 2).toFloat(), (height / 2).toFloat())
+ Bitmap.createBitmap(it, width / 2, height / 2,
+ width / 4, height / 4, m, false)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
index 599585e9d125..20acb4919c78 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/hdr/RadialGlow.kt
@@ -26,9 +26,7 @@ import android.util.AttributeSet
import com.android.test.silkfx.common.BaseDrawingView
import kotlin.math.min
-class RadialGlow : BaseDrawingView {
-
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+class RadialGlow(context: Context, attrs: AttributeSet?) : BaseDrawingView(context, attrs) {
var glowToggle = false
diff --git a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
index c11b0f3acf79..f85fb0f267d5 100644
--- a/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
+++ b/tests/SmokeTestApps/src/com/android/smoketest/triggers/CrashyApp.java
@@ -30,6 +30,7 @@ public class CrashyApp extends Activity {
setContentView(tv);
}
+ @SuppressWarnings("ReturnValueIgnored")
@Override
public void onResume() {
((String) null).length();
diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS
index 9db19a37812b..a0fcfc52704d 100644
--- a/tests/SoundTriggerTestApp/OWNERS
+++ b/tests/SoundTriggerTestApp/OWNERS
@@ -1,2 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/java/android/media/soundtrigger/OWNERS
mdooley@google.com
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 6d4ffcff7d45..3567c08d0285 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -92,7 +92,7 @@ public class SoundTriggerTestService extends Service {
super.onCreate();
IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_ACTION);
- registerReceiver(mBroadcastReceiver, filter);
+ registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
// Make sure the data directory exists, and we're the owner of it.
try {
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
deleted file mode 100644
index cc0fa1cd0840..000000000000
--- a/tests/SoundTriggerTests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
- LOCAL_SRC_FILES := $(call all-subdir-java-files)
- LOCAL_PRIVILEGED_MODULE := true
- LOCAL_CERTIFICATE := platform
- TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
-else
- LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
-endif
-
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := SoundTriggerTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS
deleted file mode 100644
index 816bc6bba639..000000000000
--- a/tests/SoundTriggerTests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /core/java/android/media/soundtrigger/OWNERS
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
deleted file mode 100644
index e36f398c53ea..000000000000
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * Copyright (C) 2014 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.soundtrigger;
-
-import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
-import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
-import android.media.AudioFormat;
-import android.os.Parcel;
-import android.test.InstrumentationTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Arrays;
-import java.util.Locale;
-import java.util.Random;
-import java.util.UUID;
-
-public class SoundTriggerTest extends InstrumentationTestCase {
- private Random mRandom = new Random();
-
- @SmallTest
- public void testKeyphraseParcelUnparcel_noUsers() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0,
- Locale.forLanguageTag("en-US"), "hello", null);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- keyphrase.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(keyphrase.getId(), unparceled.getId());
- assertNull(unparceled.getUsers());
- assertEquals(keyphrase.getLocale(), unparceled.getLocale());
- assertEquals(keyphrase.getText(), unparceled.getText());
- }
-
- @SmallTest
- public void testKeyphraseParcelUnparcel_zeroUsers() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0,
- Locale.forLanguageTag("en-US"), "hello", new int[0]);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- keyphrase.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(keyphrase.getId(), unparceled.getId());
- assertTrue(Arrays.equals(keyphrase.getUsers(), unparceled.getUsers()));
- assertEquals(keyphrase.getLocale(), unparceled.getLocale());
- assertEquals(keyphrase.getText(), unparceled.getText());
- }
-
- @SmallTest
- public void testKeyphraseParcelUnparcel_pos() throws Exception {
- Keyphrase keyphrase = new Keyphrase(1, 0,
- Locale.forLanguageTag("en-US"), "hello", new int[] {1, 2, 3, 4, 5});
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- keyphrase.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(keyphrase.getId(), unparceled.getId());
- assertTrue(Arrays.equals(keyphrase.getUsers(), unparceled.getUsers()));
- assertEquals(keyphrase.getLocale(), unparceled.getLocale());
- assertEquals(keyphrase.getText(), unparceled.getText());
- }
-
- @SmallTest
- public void testKeyphraseSoundModelParcelUnparcel_noData() throws Exception {
- Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
- "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
- "there", new int[] {1, 2});
- KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
- null, keyphrases);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- ksm.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertNull(unparceled.getData());
- assertEquals(ksm.getType(), unparceled.getType());
- assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
- }
-
- @SmallTest
- public void testKeyphraseSoundModelParcelUnparcel_zeroData() throws Exception {
- Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
- "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
- "there", new int[] {1, 2});
- KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
- new byte[0], keyphrases);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- ksm.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertEquals(ksm.getType(), unparceled.getType());
- assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
- assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
- }
-
- @SmallTest
- public void testKeyphraseSoundModelParcelUnparcel_noKeyphrases() throws Exception {
- byte[] data = new byte[10];
- mRandom.nextBytes(data);
- KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
- data, null);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- ksm.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertEquals(ksm.getType(), unparceled.getType());
- assertNull(unparceled.getKeyphrases());
- assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
- }
-
- @SmallTest
- public void testKeyphraseSoundModelParcelUnparcel_zeroKeyphrases() throws Exception {
- byte[] data = new byte[10];
- mRandom.nextBytes(data);
- KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
- data, new Keyphrase[0]);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- ksm.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertEquals(ksm.getType(), unparceled.getType());
- assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
- assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
- }
-
- @LargeTest
- public void testKeyphraseSoundModelParcelUnparcel_largeData() throws Exception {
- Keyphrase[] keyphrases = new Keyphrase[2];
- keyphrases[0] = new Keyphrase(1, 0, Locale.forLanguageTag("en-US"),
- "hello", new int[] {0});
- keyphrases[1] = new Keyphrase(2, 0, Locale.forLanguageTag("fr-FR"),
- "there", new int[] {1, 2});
- byte[] data = new byte[200 * 1024];
- mRandom.nextBytes(data);
- KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), UUID.randomUUID(),
- data, keyphrases);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- ksm.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertEquals(ksm.getType(), unparceled.getType());
- assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
- assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
- }
-
- @SmallTest
- public void testRecognitionEventParcelUnparcel_noData() throws Exception {
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1,
- true, 2, 3, 4, false, null, null);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @SmallTest
- public void testRecognitionEventParcelUnparcel_zeroData() throws Exception {
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE, 1,
- true, 2, 3, 4, false, null, new byte[1]);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @SmallTest
- public void testRecognitionEventParcelUnparcel_largeData() throws Exception {
- byte[] data = new byte[200 * 1024];
- mRandom.nextBytes(data);
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
- false, 2, 3, 4, false, null, data);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @SmallTest
- public void testRecognitionEventParcelUnparcel_largeAudioData() throws Exception {
- byte[] data = new byte[200 * 1024];
- mRandom.nextBytes(data);
- RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
- false, 2, 3, 4, true,
- (new AudioFormat.Builder())
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setSampleRate(16000)
- .build(),
- data);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @SmallTest
- public void testKeyphraseRecognitionEventParcelUnparcel_noKeyphrases() throws Exception {
- KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1, true, 2, 3, 4, false, null, null, null);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseRecognitionEvent unparceled =
- KeyphraseRecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @SmallTest
- public void testKeyphraseRecognitionEventParcelUnparcel_zeroData() throws Exception {
- KeyphraseRecognitionExtra[] kpExtra = new KeyphraseRecognitionExtra[0];
- KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_FAILURE, 2, true, 2, 3, 4, false, null, new byte[1],
- kpExtra);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseRecognitionEvent unparceled =
- KeyphraseRecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-
- @LargeTest
- public void testKeyphraseRecognitionEventParcelUnparcel_largeData() throws Exception {
- byte[] data = new byte[200 * 1024];
- mRandom.nextBytes(data);
- KeyphraseRecognitionExtra[] kpExtra = new KeyphraseRecognitionExtra[4];
- ConfidenceLevel cl1 = new ConfidenceLevel(1, 90);
- ConfidenceLevel cl2 = new ConfidenceLevel(2, 30);
- kpExtra[0] = new KeyphraseRecognitionExtra(1,
- SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION, 0,
- new ConfidenceLevel[] {cl1, cl2});
- kpExtra[1] = new KeyphraseRecognitionExtra(1,
- SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0,
- new ConfidenceLevel[] {cl2});
- kpExtra[2] = new KeyphraseRecognitionExtra(1,
- SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0, null);
- kpExtra[3] = new KeyphraseRecognitionExtra(1,
- SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER, 0,
- new ConfidenceLevel[0]);
-
- KeyphraseRecognitionEvent re = new KeyphraseRecognitionEvent(
- SoundTrigger.RECOGNITION_STATUS_FAILURE, 1, true, 2, 3, 4, false, null, data,
- kpExtra);
-
- // Write to a parcel
- Parcel parcel = Parcel.obtain();
- re.writeToParcel(parcel, 0);
-
- // Read from it
- parcel.setDataPosition(0);
- KeyphraseRecognitionEvent unparceled =
- KeyphraseRecognitionEvent.CREATOR.createFromParcel(parcel);
-
- // Verify that they are the same
- assertEquals(re, unparceled);
- }
-}
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
deleted file mode 100644
index 2c3592c640bc..000000000000
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2014 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.soundtrigger;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.soundtrigger.SoundTriggerManager;
-import android.os.ParcelUuid;
-import android.os.ServiceManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.app.ISoundTriggerService;
-
-import org.mockito.MockitoAnnotations;
-
-import java.io.DataOutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Random;
-import java.util.UUID;
-
-public class GenericSoundModelTest extends AndroidTestCase {
- static final int MSG_DETECTION_ERROR = -1;
- static final int MSG_DETECTION_RESUME = 0;
- static final int MSG_DETECTION_PAUSE = 1;
- static final int MSG_KEYPHRASE_TRIGGER = 2;
- static final int MSG_GENERIC_TRIGGER = 4;
-
- private Random random = new Random();
- private HashSet<UUID> loadedModelUuids;
- private ISoundTriggerService soundTriggerService;
- private SoundTriggerManager soundTriggerManager;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
-
- Context context = getContext();
- soundTriggerService = ISoundTriggerService.Stub.asInterface(
- ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
- soundTriggerManager = (SoundTriggerManager) context.getSystemService(
- Context.SOUND_TRIGGER_SERVICE);
-
- loadedModelUuids = new HashSet<UUID>();
- }
-
- @Override
- public void tearDown() throws Exception {
- for (UUID modelUuid : loadedModelUuids) {
- soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
- }
- super.tearDown();
- }
-
- GenericSoundModel new_sound_model() {
- // Create sound model
- byte[] data = new byte[1024];
- random.nextBytes(data);
- UUID modelUuid = UUID.randomUUID();
- UUID mVendorUuid = UUID.randomUUID();
- return new GenericSoundModel(modelUuid, mVendorUuid, data);
- }
-
- @SmallTest
- public void testUpdateGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- // Update sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
-
- // Confirm it was updated
- GenericSoundModel returnedModel =
- soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
- assertEquals(model, returnedModel);
- }
-
- @SmallTest
- public void testDeleteGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- // Update sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
-
- // Delete sound model
- soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
- loadedModelUuids.remove(model.getUuid());
-
- // Confirm it was deleted
- GenericSoundModel returnedModel =
- soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
- assertEquals(null, returnedModel);
- }
-
- @LargeTest
- public void testStartStopGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
- // Update and start sound model recognition
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
- int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
- config);
- assertEquals("Could Not Start Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-
- // Stop recognition
- r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
- assertEquals("Could Not Stop Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- }
-
- @LargeTest
- public void testTriggerGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
- // Update and start sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
- soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
-
- // Send trigger to stub HAL
- Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
- out.flush();
- socket.close();
-
- // Verify trigger was received
- verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
- }
-
- /**
- * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
- * recognition. Intended to find unexpected errors that occur in unexpected states.
- */
- @LargeTest
- public void testFuzzGenericSoundModel() throws Exception {
- int numModels = 2;
-
- final int STATUS_UNLOADED = 0;
- final int STATUS_LOADED = 1;
- final int STATUS_STARTED = 2;
-
- class ModelInfo {
- int status;
- GenericSoundModel model;
-
- public ModelInfo(GenericSoundModel model, int status) {
- this.status = status;
- this.model = model;
- }
- }
-
- Random predictableRandom = new Random(100);
-
- ArrayList modelInfos = new ArrayList<ModelInfo>();
- for(int i=0; i<numModels; i++) {
- // Create sound model
- byte[] data = new byte[1024];
- predictableRandom.nextBytes(data);
- UUID modelUuid = UUID.randomUUID();
- UUID mVendorUuid = UUID.randomUUID();
- GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
- ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
- modelInfos.add(modelInfo);
- }
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-
- int numOperationsToRun = 100;
- for(int i=0; i<numOperationsToRun; i++) {
- // Select a random model
- int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
- ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
-
- // Perform a random operation
- int operation = predictableRandom.nextInt(5);
-
- if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
- // Update and start sound model
- soundTriggerService.updateSoundModel(modelInfo.model);
- loadedModelUuids.add(modelInfo.model.getUuid());
- modelInfo.status = STATUS_LOADED;
- } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
- // Start the sound model
- int r = soundTriggerService.startRecognition(new ParcelUuid(
- modelInfo.model.getUuid()),
- spyCallback, config);
- assertEquals("Could Not Start Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- modelInfo.status = STATUS_STARTED;
- } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
- // Send trigger to stub HAL
- Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
- out.flush();
- socket.close();
-
- // Verify trigger was received
- verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
- reset(spyCallback);
- } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
- // Stop recognition
- int r = soundTriggerService.stopRecognition(new ParcelUuid(
- modelInfo.model.getUuid()),
- spyCallback);
- assertEquals("Could Not Stop Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- modelInfo.status = STATUS_LOADED;
- } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
- // Delete sound model
- soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
- loadedModelUuids.remove(modelInfo.model.getUuid());
-
- // Confirm it was deleted
- GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
- new ParcelUuid(modelInfo.model.getUuid()));
- assertEquals(null, returnedModel);
- modelInfo.status = STATUS_UNLOADED;
- }
- }
- }
-
- public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
- @Override
- public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
- }
-
- @Override
- public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
- }
-
- @Override
- public void onError(int status) {
- }
-
- @Override
- public void onRecognitionPaused() {
- }
-
- @Override
- public void onRecognitionResumed() {
- }
- }
-}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index cce0dde9e6b9..23efe548c82a 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,8 +55,11 @@ java_test_host {
"cts-install-lib-host",
],
data: [
+ ":StagedInstallInternalTestApp",
":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
+ ":com.android.apex.apkrollback.test_v2",
+ ":StagedInstallInternalTestApp",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
":TestAppAv1",
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 7490d3f2b50c..0375f66069c3 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -66,6 +66,7 @@ import java.util.function.Consumer;
@RunWith(JUnit4.class)
public class StagedInstallInternalTest {
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
+ private static final String REBOOTLESS_APEX_PACKAGE_NAME = "test.apex.rebootless";
private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
private static final TestApp APEX_WRONG_SHA_V2 = new TestApp(
@@ -114,6 +115,63 @@ public class StagedInstallInternalTest {
Uninstall.packages(TestApp.A, TestApp.B);
}
+ private boolean isSystem(PackageInfo info) {
+ return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ private boolean isUpdatedSystem(PackageInfo info) {
+ return (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
+ @Test
+ public void testUpdateSystemApp_InstallV2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ PackageInfo info;
+ // Check factory version
+ info = pm.getPackageInfo(TestApp.A,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isFalse();
+ // Check active version
+ info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isFalse();
+
+ Install.single(TestApp.A2).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ // Check factory version
+ info = pm.getPackageInfo(TestApp.A,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isFalse();
+ // Check active version
+ info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isTrue();
+ }
+
+ @Test
+ public void testUpdateSystemApp_PostInstallV2() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+
+ PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ PackageInfo info;
+ // Check factory version
+ info = pm.getPackageInfo(TestApp.A,
+ PackageManager.PackageInfoFlags.of(PackageManager.MATCH_FACTORY_ONLY));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isFalse();
+ // Check active version
+ info = pm.getPackageInfo(TestApp.A, PackageManager.PackageInfoFlags.of(0));
+ assertThat(isSystem(info)).isTrue();
+ assertThat(isUpdatedSystem(info)).isTrue();
+ }
+
@Test
public void testDuplicateApkInApexShouldFail_Commit() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
@@ -403,7 +461,8 @@ public class StagedInstallInternalTest {
{
PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX);
assertThat(apex.getLongVersionCode()).isEqualTo(1);
- assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+ assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+ .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
.isEqualTo(ApplicationInfo.FLAG_INSTALLED);
assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active");
@@ -414,7 +473,8 @@ public class StagedInstallInternalTest {
assertThat(apex.getLongVersionCode()).isEqualTo(1);
assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
.isEqualTo(ApplicationInfo.FLAG_SYSTEM);
- assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+ assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+ .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex");
}
@@ -425,7 +485,8 @@ public class StagedInstallInternalTest {
{
PackageInfo apex = pm.getPackageInfo("test.apex.rebootless", PackageManager.MATCH_APEX);
assertThat(apex.getLongVersionCode()).isEqualTo(2);
- assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0);
+ assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)
+ .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
.isEqualTo(ApplicationInfo.FLAG_INSTALLED);
assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active");
@@ -436,7 +497,8 @@ public class StagedInstallInternalTest {
assertThat(apex.getLongVersionCode()).isEqualTo(1);
assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
.isEqualTo(ApplicationInfo.FLAG_SYSTEM);
- assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo(0);
+ assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED)
+ .isEqualTo(ApplicationInfo.FLAG_INSTALLED);
assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex");
}
}
@@ -533,6 +595,28 @@ public class StagedInstallInternalTest {
assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(1);
}
+ @Test
+ public void testVendorApex_VerifyFactory() throws Exception {
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(REBOOTLESS_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX);
+ assertThat(pi.getLongVersionCode()).isEqualTo(1);
+ assertThat(pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR)
+ .isEqualTo(ApplicationInfo.PRIVATE_FLAG_VENDOR);
+ assertThat(pi.applicationInfo.sourceDir).startsWith("/vendor/apex");
+ }
+
+ @Test
+ public void testVendorApex_VerifyData() throws Exception {
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(REBOOTLESS_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX);
+ assertThat(pi.getLongVersionCode()).isEqualTo(2);
+ assertThat(pi.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR)
+ .isEqualTo(ApplicationInfo.PRIVATE_FLAG_VENDOR);
+ assertThat(pi.applicationInfo.sourceDir).startsWith("/data/apex");
+ }
+
private IPackageManagerNative getPackageManagerNative() {
IBinder binder = ServiceManager.waitForService("package_native");
assertThat(binder).isNotNull();
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index f60b4d6aad1e..f1fc503c8556 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -31,6 +31,7 @@ import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.tests.rollback.host.AbandonSessionsRule;
import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.PackageInfo;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -63,6 +64,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
private static final String APEXD_TEST_APEX = "apex.apexd_test.apex";
private static final String FAKE_APEX_SYSTEM_SERVER_APEX = "test_com.android.server.apex";
+ private static final String REBOOTLESS_V1 = "test.rebootless_apex_v1.apex";
+ private static final String REBOOTLESS_V2 = "test.rebootless_apex_v2.apex";
private static final String TEST_VENDOR_APEX_ALLOW_LIST =
"/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml";
@@ -94,7 +97,9 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
"/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
"/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex",
"/system/apex/test.rebootless_apex_v*.apex",
+ "/vendor/apex/test.rebootless_apex_v*.apex",
"/data/apex/active/test.apex.rebootless*.apex",
+ "/system/app/TestApp/TestAppAv1.apk",
TEST_VENDOR_APEX_ALLOW_LIST);
}
@@ -118,16 +123,27 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
boolean found = false;
+ boolean remountSystem = false;
+ boolean remountVendor = false;
for (String file : files) {
CommandResult result = getDevice().executeShellV2Command("ls " + file);
if (result.getStatus() == CommandStatus.SUCCESS) {
found = true;
- break;
+ if (file.startsWith("/system")) {
+ remountSystem = true;
+ } else if (file.startsWith("/vendor")) {
+ remountVendor = true;
+ }
}
}
if (found) {
- getDevice().remountSystemWritable();
+ if (remountSystem) {
+ getDevice().remountSystemWritable();
+ }
+ if (remountVendor) {
+ getDevice().remountVendorWritable();
+ }
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
}
@@ -136,20 +152,34 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
private void pushTestApex(String fileName) throws Exception {
+ pushTestApex(fileName, "system");
+ }
+
+ private void pushTestApex(String fileName, String partition) throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final File apex = buildHelper.getTestFile(fileName);
if (!getDevice().isAdbRoot()) {
getDevice().enableAdbRoot();
}
- getDevice().remountSystemWritable();
- assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName));
+ if ("system".equals(partition)) {
+ getDevice().remountSystemWritable();
+ } else if ("vendor".equals(partition)) {
+ getDevice().remountVendorWritable();
+ }
+ assertTrue(getDevice().pushFile(apex, "/" + partition + "/apex/" + fileName));
+ }
+
+ private void installTestApex(String fileName, String... extraArgs) throws Exception {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ final File apex = buildHelper.getTestFile(fileName);
+ getDevice().installPackage(apex, false, extraArgs);
}
private void pushTestVendorApexAllowList(String installerPackageName) throws Exception {
if (!getDevice().isAdbRoot()) {
getDevice().enableAdbRoot();
}
- getDevice().remountSystemWritable();
+ getDevice().remountVendorWritable();
File file = File.createTempFile("test-vendor-apex-allow-list", ".xml");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
final String fmt =
@@ -163,6 +193,25 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
/**
+ * Tests app info flags are set correctly when updating a system app.
+ */
+ @Test
+ public void testUpdateSystemApp() throws Exception {
+ // Push TestAppAv1.apk to /system
+ final File apkFile = mHostUtils.getTestFile(APK_A);
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+ getDevice().remountSystemWritable();
+ assertTrue(getDevice().pushFile(apkFile, "/system/app/TestApp/" + apkFile.getName()));
+ getDevice().reboot();
+
+ runPhase("testUpdateSystemApp_InstallV2");
+ getDevice().reboot();
+ runPhase("testUpdateSystemApp_PostInstallV2");
+ }
+
+ /**
* Tests that duplicate packages in apk-in-apex and apk should fail to install.
*/
@Test
@@ -176,6 +225,28 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
runPhase("testDuplicateApkInApexShouldFail_Verify");
}
+ /**
+ * Tests the cache of apk-in-apex is pruned and rebuilt correctly.
+ */
+ @Test
+ @LargeTest
+ public void testApkInApexPruneCache() throws Exception {
+ final String apkInApexPackageName = "com.android.cts.install.lib.testapp.A";
+
+ pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
+ getDevice().reboot();
+ PackageInfo pi = getDevice().getAppPackageInfo(apkInApexPackageName);
+ assertThat(Integer.parseInt(pi.getVersionCode())).isEqualTo(1);
+ assertThat(pi.getCodePath()).startsWith("/apex/" + APK_IN_APEX_TESTAPEX_NAME);
+
+ installPackage(APK_IN_APEX_TESTAPEX_NAME + "_v2.apex", "--staged");
+ getDevice().reboot();
+ pi = getDevice().getAppPackageInfo(apkInApexPackageName);
+ // The version code of apk-in-apex will be stale if the cache is not rebuilt
+ assertThat(Integer.parseInt(pi.getVersionCode())).isEqualTo(2);
+ assertThat(pi.getCodePath()).startsWith("/apex/" + APK_IN_APEX_TESTAPEX_NAME);
+ }
+
@Test
public void testSystemServerRestartDoesNotAffectStagedSessions() throws Exception {
runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Commit");
@@ -477,6 +548,33 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
runPhase("testVendorApexCorrectInstaller_nonStaged");
}
+ /**
+ * Tests correctness of {@link android.content.pm.ApplicationInfo} for APEXes on /vendor.
+ */
+ @Test
+ @LargeTest
+ public void testVendorApex_Staged() throws Exception {
+ pushTestApex(REBOOTLESS_V1, "vendor");
+ getDevice().reboot();
+ runPhase("testVendorApex_VerifyFactory");
+ installTestApex(REBOOTLESS_V2, "--staged");
+ getDevice().reboot();
+ runPhase("testVendorApex_VerifyData");
+ }
+
+ /**
+ * Tests correctness of {@link android.content.pm.ApplicationInfo} for APEXes on /vendor.
+ */
+ @Test
+ @LargeTest
+ public void testVendorApex_NonStaged() throws Exception {
+ pushTestApex(REBOOTLESS_V1, "vendor");
+ getDevice().reboot();
+ runPhase("testVendorApex_VerifyFactory");
+ installTestApex(REBOOTLESS_V2, "--force-non-staged");
+ runPhase("testVendorApex_VerifyData");
+ }
+
@Test
public void testRebootlessUpdates() throws Exception {
pushTestApex("test.rebootless_apex_v1.apex");
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ef324e7c1377..6c89e49a0e6e 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -1156,12 +1156,12 @@ public class NotificationTestList extends TestActivity
private PendingIntent makeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
- return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
private PendingIntent makeIntent2() {
Intent intent = new Intent(this, StatusBarTest.class);
- return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
+ return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
}
diff --git a/tests/SurfaceControlViewHostTest/Android.bp b/tests/SurfaceControlViewHostTest/Android.bp
index 0127ba559500..99567b9a10b4 100644
--- a/tests/SurfaceControlViewHostTest/Android.bp
+++ b/tests/SurfaceControlViewHostTest/Android.bp
@@ -25,7 +25,10 @@ package {
android_test {
name: "SurfaceControlViewHostTest",
- srcs: ["**/*.java"],
+ srcs: [
+ "**/*.aidl",
+ "**/*.java",
+ ],
platform_apis: true,
certificate: "platform",
}
diff --git a/tests/SurfaceControlViewHostTest/AndroidManifest.xml b/tests/SurfaceControlViewHostTest/AndroidManifest.xml
index 7e9a04dfa82c..e50cbc52a5b8 100644
--- a/tests/SurfaceControlViewHostTest/AndroidManifest.xml
+++ b/tests/SurfaceControlViewHostTest/AndroidManifest.xml
@@ -24,6 +24,16 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name="SurfaceControlViewHostSyncTest"
+ android:label="View Embedding Test Sync"
+ 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=".EmbeddedWindowService"
+ android:process="com.android.test.viewembed.embedded_process"/>
</application>
diff --git a/tests/SurfaceControlViewHostTest/OWNERS b/tests/SurfaceControlViewHostTest/OWNERS
new file mode 100644
index 000000000000..0862c05e0ee4
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
new file mode 100644
index 000000000000..abc15b49ad98
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/EmbeddedWindowService.java
@@ -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 com.android.test.viewembed;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.SurfaceControlViewHost;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+public class EmbeddedWindowService extends Service {
+ private static final String TAG = "EmbeddedWindowService";
+ private SurfaceControlViewHost mVr;
+
+ private Handler mHandler;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ // Return the interface
+ return new AttachEmbeddedWindow();
+ }
+
+ public static class SlowView extends TextView {
+
+ public SlowView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ try {
+ Thread.sleep(250);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ private class AttachEmbeddedWindow extends IAttachEmbeddedWindow.Stub {
+ @Override
+ public void attachEmbedded(IBinder hostToken, int width, int height,
+ IAttachEmbeddedWindowCallback callback) {
+ mHandler.post(() -> {
+ Context context = EmbeddedWindowService.this;
+ Display display = getApplicationContext().getSystemService(
+ DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+ mVr = new SurfaceControlViewHost(context, display, hostToken);
+ FrameLayout content = new FrameLayout(context);
+
+ SlowView slowView = new SlowView(context);
+ slowView.setText("INSIDE TEXT");
+ slowView.setGravity(Gravity.CENTER);
+ slowView.setTextColor(Color.BLACK);
+ slowView.setBackgroundColor(Color.CYAN);
+ content.addView(slowView);
+ WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(width, height, TYPE_APPLICATION,
+ 0, PixelFormat.OPAQUE);
+ lp.setTitle("EmbeddedWindow");
+
+ mVr.setView(content, lp);
+ try {
+ callback.onEmbeddedWindowAttached(mVr.getSurfacePackage());
+ } catch (RemoteException e) {
+ }
+ });
+ }
+ @Override
+ public void relayout(WindowManager.LayoutParams lp) {
+ mHandler.post(() -> mVr.relayout(lp));
+ }
+ }
+}
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl
new file mode 100644
index 000000000000..9e9faf03ba1c
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindow.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.viewembed;
+
+import android.os.IBinder;
+import com.android.test.viewembed.IAttachEmbeddedWindowCallback;
+import android.view.WindowManager.LayoutParams;
+
+interface IAttachEmbeddedWindow {
+ void attachEmbedded(IBinder hostToken, int width, int height, in IAttachEmbeddedWindowCallback callback);
+ void relayout(in LayoutParams lp);
+} \ No newline at end of file
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target03.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindowCallback.aidl
index 5ae55215f696..c45c24d4b872 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/b/Target03.java
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/IAttachEmbeddedWindowCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -13,7 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.content.componentalias.tests.b;
-public class Target03 extends BaseReceiver {
-}
+package com.android.test.viewembed;
+
+import android.view.SurfaceControlViewHost.SurfacePackage;
+
+interface IAttachEmbeddedWindowCallback {
+ void onEmbeddedWindowAttached(in SurfacePackage surfacePackage);
+} \ No newline at end of file
diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
new file mode 100644
index 000000000000..359eb35384c7
--- /dev/null
+++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostSyncTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.viewembed;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.SurfaceControlViewHost.SurfacePackage;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.Switch;
+import android.window.SurfaceSyncGroup;
+
+public class SurfaceControlViewHostSyncTest extends Activity implements SurfaceHolder.Callback {
+ private static final String TAG = "SurfaceControlViewHostSyncTest";
+ private SurfaceView mSv;
+
+ private final Object mLock = new Object();
+ private boolean mIsAttached;
+ private boolean mSurfaceCreated;
+
+ private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
+ private SurfacePackage mSurfacePackage;
+
+ private final Point[] mSizes = new Point[]{new Point(500, 500), new Point(700, 400),
+ new Point(300, 800), new Point(200, 200)};
+ private int mLastSizeIndex = 0;
+
+ private boolean mSync = true;
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ // Called when the connection with the service is established
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "Service Connected");
+ synchronized (mLock) {
+ mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service);
+ }
+ loadEmbedded();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "Service Disconnected");
+ mIAttachEmbeddedWindow = null;
+ }
+ };
+
+ protected void onCreate(Bundle savedInstanceState) {
+ FrameLayout content = new FrameLayout(this);
+ super.onCreate(savedInstanceState);
+ mSv = new SurfaceView(this);
+ Button button = new Button(this);
+ Switch enableSyncButton = new Switch(this);
+ content.addView(mSv, new FrameLayout.LayoutParams(
+ mSizes[0].x, mSizes[0].y, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
+ content.addView(button, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM));
+ content.addView(enableSyncButton,
+ new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM));
+ setContentView(content);
+
+ mSv.setZOrderOnTop(false);
+ mSv.getHolder().addCallback(this);
+
+ button.setText("Change Size");
+ enableSyncButton.setText("Enable Sync");
+ enableSyncButton.setChecked(true);
+ button.setOnClickListener(v -> {
+ resize();
+ });
+
+ enableSyncButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ mSync = isChecked;
+ });
+
+ Intent intent = new Intent(this, EmbeddedWindowService.class);
+ intent.setAction(IAttachEmbeddedWindow.class.getName());
+ Log.d(TAG, "bindService");
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ private void resize() {
+ if (mSurfacePackage == null) {
+ return;
+ }
+ Point size = mSizes[mLastSizeIndex % mSizes.length];
+
+ Runnable svResizeRunnable = () -> {
+ mSv.getLayoutParams().width = size.x;
+ mSv.getLayoutParams().height = size.y;
+ mSv.requestLayout();
+ };
+
+ Runnable resizeRunnable = () -> {
+ try {
+ final WindowManager.LayoutParams lp =
+ new WindowManager.LayoutParams(size.x, size.y,
+ WindowManager.LayoutParams.TYPE_APPLICATION, 0,
+ PixelFormat.TRANSPARENT);
+ mIAttachEmbeddedWindow.relayout(lp);
+ } catch (RemoteException e) {
+ }
+ };
+
+ if (mSync) {
+ SurfaceSyncGroup syncGroup = new SurfaceSyncGroup(TAG);
+ syncGroup.add(getWindow().getRootSurfaceControl(), svResizeRunnable);
+ syncGroup.add(mSurfacePackage, resizeRunnable);
+ syncGroup.markSyncReady();
+ } else {
+ svResizeRunnable.run();
+ resizeRunnable.run();
+ }
+
+ mLastSizeIndex++;
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurfaceCreated = true;
+ }
+ attachEmbedded();
+ }
+
+ private boolean isReadyToAttach() {
+ synchronized (mLock) {
+ if (!mSurfaceCreated) {
+ Log.d(TAG, "surface is not created");
+ }
+ if (mIAttachEmbeddedWindow == null) {
+ Log.d(TAG, "Service is not attached");
+ }
+ if (mIsAttached) {
+ Log.d(TAG, "Already attached");
+ }
+
+ return mSurfaceCreated && mIAttachEmbeddedWindow != null && !mIsAttached
+ && mSurfacePackage != null;
+ }
+ }
+
+ private void loadEmbedded() {
+ try {
+ mIAttachEmbeddedWindow.attachEmbedded(mSv.getHostToken(), mSizes[0].x, mSizes[0].y,
+ new IAttachEmbeddedWindowCallback.Stub() {
+ @Override
+ public void onEmbeddedWindowAttached(SurfacePackage surfacePackage) {
+ getMainThreadHandler().post(() -> {
+ mSurfacePackage = surfacePackage;
+ attachEmbedded();
+ });
+ }
+ });
+ mLastSizeIndex++;
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void attachEmbedded() {
+ if (!isReadyToAttach()) {
+ return;
+ }
+
+ synchronized (mLock) {
+ mIsAttached = true;
+ }
+ mSv.setChildSurfacePackage(mSurfacePackage);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ synchronized (mLock) {
+ mSurfaceCreated = false;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart");
+ resize();
+ }
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
index b67dc380efab..97398dc4e334 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt
@@ -15,7 +15,7 @@
*/
package com.android.test
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
import org.junit.Test
@@ -37,7 +37,7 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
1000 /* ms */))
}
- assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
@Test
@@ -51,7 +51,7 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
assertEquals(0, activity.mSurfaceProxy.waitUntilBufferDisplayed(2, 5000 /* ms */))
}
- assertThat(trace).hasFrameSequence("SurfaceView", 1..2L)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", 1..2L)
}
@Test
@@ -69,7 +69,7 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
5000 /* ms */))
}
- assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
@Test
@@ -92,7 +92,7 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
5000 /* ms */))
}
- assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
@Test
@@ -170,4 +170,4 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase
assertTrue(failures)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
index e9e0246a207d..0cc18d657cf5 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt
@@ -16,10 +16,11 @@
package com.android.test
import android.graphics.Point
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
+import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -45,10 +46,11 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
activity.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */)
}
// Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
-
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
// Verify the next buffer is submitted with the correct size
- assertThat(trace).layer("SurfaceView", 3).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 3).also {
it.hasBufferSize(defaultBufferSize)
// scaling mode is not passed down to the layer for blast
if (useBlastAdapter) {
@@ -81,9 +83,11 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
}
// verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
- assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+ LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
+ LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@Test
@@ -109,10 +113,13 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
}
// verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
- assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
- assertThat(trace).layer("SurfaceView", 3).hasBufferOrientation(Transform.ROT_90.value)
+ LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
+ LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+ LayersTraceSubject(trace).layer("SurfaceView", 3)
+ .hasBufferOrientation(Transform.ROT_90.value)
}
@Test
@@ -141,12 +148,13 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us
}
for (count in 0 until 5) {
- assertThat(trace).layer("SurfaceView", (count * 3) + 1L)
+ LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 1L)
.hasBufferSize(defaultBufferSize)
- assertThat(trace).layer("SurfaceView", (count * 3) + 2L)
- .doesNotExist()
- assertThat(trace).layer("SurfaceView", (count * 3) + 3L)
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 2L)
+ }
+ LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 3L)
.hasBufferSize(bufferSize)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
index 0802990beeab..6f4d11c3aa1b 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt
@@ -19,11 +19,12 @@ import android.graphics.Color
import android.graphics.Point
import android.graphics.Rect
import android.os.SystemClock
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertTrue
+import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@@ -43,7 +44,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
}
// verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
}
@Test
@@ -56,7 +57,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
activity.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
}
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
@@ -73,7 +74,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
activity.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
}
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
@@ -102,9 +103,11 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
}
// verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
- assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
+ LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
+ LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@Test
@@ -118,7 +121,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
activity.mSurfaceProxy.waitUntilBufferDisplayed(index + 1L, 500 /* ms */)
}
- assertThat(trace).layer("SurfaceView", index + 1L).also {
+ LayersTraceSubject(trace).layer("SurfaceView", index + 1L).also {
it.hasBufferSize(defaultBufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasBufferOrientation(transform.value)
@@ -145,7 +148,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
}
// check that the layer and buffer starts with the default size
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(defaultBufferSize)
it.hasLayerSize(defaultBufferSize)
}
@@ -169,7 +172,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
checkPixels(svBounds, Color.BLUE)
}
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasLayerSize(newSize)
it.hasBufferSize(defaultBufferSize)
}
@@ -193,7 +196,7 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
}
// check that the layer and buffer starts with the default size
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(defaultBufferSize)
it.hasLayerSize(defaultBufferSize)
}
@@ -216,9 +219,9 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA
checkPixels(svBounds, Color.BLUE)
}
- assertThat(trace).layer("SurfaceView", 1).also {
+ LayersTraceSubject(trace).layer("SurfaceView", 1).also {
it.hasLayerSize(defaultBufferSize)
it.hasBufferSize(defaultBufferSize)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
index 69012bdd7d7c..e722ba537a8e 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt
@@ -16,9 +16,10 @@
package com.android.test
import android.graphics.Point
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
+import org.junit.Assert
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
@@ -69,8 +70,10 @@ class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
}
// verify buffer size is reset to default buffer size
- assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
- assertThat(trace).layer("SurfaceView", 2).doesNotExist()
- assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
+ LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
+ Assert.assertThrows(AssertionError::class.java) {
+ LayersTraceSubject(trace).layer("SurfaceView", 2)
+ }
+ LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
index 996a1d3d79da..7378476554ec 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeScreenRecordTests.kt
@@ -18,7 +18,6 @@ package com.android.test
import android.graphics.Color
import android.graphics.Rect
import android.os.SystemClock
-import android.view.cts.surfacevalidator.PixelColor
import junit.framework.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -53,7 +52,7 @@ class SharedBufferModeScreenRecordTests(useBlastAdapter: Boolean) :
SystemClock.sleep(4000)
}
- val result = withScreenRecording(svBounds, PixelColor.RED) {
+ val result = withScreenRecording(svBounds, Color.RED) {
it.mSurfaceProxy.drawBuffer(0, Color.RED)
}
val failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames)
diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
index ee41d7941470..be3ed715d4e2 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -17,7 +17,7 @@ package com.android.test
import android.graphics.Color
import android.graphics.Rect
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,7 +39,7 @@ class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(u
}
}
- assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
/** Submit buffers as fast as possible testing that we are not blocked when dequeuing the buffer
@@ -57,7 +57,7 @@ class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(u
5000 /* ms */))
}
- assertThat(trace).hasFrameSequence("SurfaceView", numFrames..numFrames)
+ LayersTraceSubject(trace).hasFrameSequence("SurfaceView", numFrames..numFrames)
}
/** Keep overwriting the buffer without queuing buffers and check that we present the latest
@@ -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 6383da5a0a98..cf4cb8c97ea1 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt
@@ -21,9 +21,11 @@ import android.graphics.Color
import android.graphics.Rect
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.traces.common.layers.LayersTrace
+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 junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -52,8 +54,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
- return withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ return withSFTracing(TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
predicate(it)
}
@@ -61,8 +62,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
fun withTrace(predicate: () -> Unit): LayersTrace {
- return withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ return withSFTracing(TRACE_FLAGS) {
predicate()
}
}
@@ -84,8 +84,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
}
private fun stopLayerTrace() {
- val tmpDir = instrumentation.targetContext.dataDir.toPath()
- LayersTraceMonitor(tmpDir).stop()
+ LayersTraceMonitor().stop(ResultWriter())
}
fun checkPixels(bounds: Rect, @ColorInt color: Int) {
@@ -117,4 +116,4 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
private const val TRACE_FLAGS =
(1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
}
-} \ 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 093c3125f253..bba967815ba5 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -18,6 +18,8 @@ package com.android.test
import android.app.Instrumentation
import android.graphics.Point
import android.provider.Settings
+import android.tools.common.datatypes.Size
+import android.tools.common.flicker.subject.layers.LayerSubject
import androidx.test.InstrumentationRegistry
import org.junit.After
import org.junit.Before
@@ -69,6 +71,10 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
const val R8G8B8A8_UNORM = 1
val defaultBufferSize = Point(640, 480)
+ fun LayerSubject.hasBufferSize(point: Point) = hasBufferSize(Size.from(point.x, point.y))
+
+ fun LayerSubject.hasLayerSize(point: Point) = hasLayerSize(Size.from(point.x, point.y))
+
// system/window.h definitions
enum class ScalingMode() {
FREEZE, // = 0
@@ -94,4 +100,4 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
INVERSE_DISPLAY(0x08)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
index ab7f24a8d326..d1f2112f649d 100644
--- a/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
+++ b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
@@ -35,11 +35,11 @@ import android.view.WindowMetrics;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Switch;
-import android.window.SurfaceSyncer;
+import android.window.SurfaceSyncGroup;
/**
* Test app that allows the user to resize the SurfaceView and have the new buffer sync with the
- * main window. This tests that {@link SurfaceSyncer} is working correctly.
+ * main window. This tests that {@link SurfaceSyncGroup} is working correctly.
*/
public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.Callback {
private static final String TAG = "SurfaceViewSyncActivity";
@@ -49,12 +49,10 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
private RenderingThread mRenderingThread;
- private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
-
private Button mExpandButton;
private Switch mEnableSyncSwitch;
- private int mLastSyncId = -1;
+ private SurfaceSyncGroup mSyncGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -76,7 +74,7 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
}
private void updateSurfaceViewSize(Rect bounds, View container) {
- if (mLastSyncId >= 0) {
+ if (mSyncGroup != null) {
return;
}
@@ -91,8 +89,8 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
mLastExpanded = !mLastExpanded;
if (mEnableSyncSwitch.isChecked()) {
- mLastSyncId = mSurfaceSyncer.setupSync(() -> { });
- mSurfaceSyncer.addToSync(mLastSyncId, container);
+ mSyncGroup = new SurfaceSyncGroup(TAG);
+ mSyncGroup.add(container.getRootSurfaceControl(), null /* runnable */);
}
ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
@@ -112,14 +110,14 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
if (mEnableSyncSwitch.isChecked()) {
- if (mLastSyncId < 0) {
+ if (mSyncGroup == null) {
mRenderingThread.renderFrame(null, width, height);
return;
}
- mSurfaceSyncer.addToSync(mLastSyncId, mSurfaceView, frameCallback ->
+ mSyncGroup.add(mSurfaceView, frameCallback ->
mRenderingThread.renderFrame(frameCallback, width, height));
- mSurfaceSyncer.markSyncReady(mLastSyncId);
- mLastSyncId = -1;
+ mSyncGroup.markSyncReady();
+ mSyncGroup = null;
} else {
mRenderingThread.renderFrame(null, width, height);
}
@@ -133,7 +131,7 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
private static class RenderingThread extends HandlerThread {
private final SurfaceHolder mSurfaceHolder;
private Handler mHandler;
- private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private SurfaceSyncGroup.SurfaceViewFrameCallback mFrameCallback;
private final Point mSurfaceSize = new Point();
int mColorValue = 0;
@@ -147,7 +145,7 @@ public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.C
mPaint.setTextSize(100);
}
- public void renderFrame(SurfaceSyncer.SurfaceViewFrameCallback frameCallback, int width,
+ public void renderFrame(SurfaceSyncGroup.SurfaceViewFrameCallback frameCallback, int width,
int height) {
if (mHandler != null) {
mHandler.post(() -> {
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index 79744625b752..cc8bc45a7411 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -26,4 +26,7 @@ java_test_host {
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["general-tests"],
+ data: [
+ ":SystemMemoryTestDevice",
+ ],
}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
index 03b43cc5b18c..6f4f7b13af66 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt
@@ -16,14 +16,15 @@
package com.android.test.taskembed
import android.app.Instrumentation
-import android.graphics.Point
import android.graphics.Rect
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor
-import com.android.server.wm.flicker.monitor.withSFTracing
-import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
+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
import org.junit.FixMethodOrder
@@ -46,8 +47,10 @@ class ResizeTasksSyncTest {
@Before
fun setup() {
- val tmpDir = instrumentation.targetContext.dataDir.toPath()
- LayersTraceMonitor(tmpDir).stop()
+ val monitor = LayersTraceMonitor()
+ if (monitor.isEnabled) {
+ monitor.stop(ResultWriter())
+ }
val firstTaskBounds = Rect(0, 0, 1080, 1000)
val secondTaskBounds = Rect(0, 1000, 1080, 2000)
@@ -68,8 +71,7 @@ class ResizeTasksSyncTest {
val firstBounds = Rect(0, 0, 1080, 800)
val secondBounds = Rect(0, 1000, 1080, 1800)
- val trace = withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ val trace = withSFTracing(TRACE_FLAGS) {
lateinit var resizeReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds)
@@ -90,14 +92,14 @@ class ResizeTasksSyncTest {
secondBounds.offsetTo(0, 0)
// verify buffer size should be changed to expected values.
- assertThat(trace).layer(FIRST_ACTIVITY, frame.toLong()).also {
- val firstTaskSize = Point(firstBounds.width(), firstBounds.height())
+ LayersTraceSubject(trace).layer(FIRST_ACTIVITY, frame.toLong()).also {
+ val firstTaskSize = Size.from(firstBounds.width(), firstBounds.height())
it.hasLayerSize(firstTaskSize)
it.hasBufferSize(firstTaskSize)
}
- assertThat(trace).layer(SECOND_ACTIVITY, frame.toLong()).also {
- val secondTaskSize = Point(secondBounds.width(), secondBounds.height())
+ LayersTraceSubject(trace).layer(SECOND_ACTIVITY, frame.toLong()).also {
+ val secondTaskSize = Size.from(secondBounds.width(), secondBounds.height())
it.hasLayerSize(secondTaskSize)
it.hasBufferSize(secondTaskSize)
}
@@ -108,4 +110,4 @@ class ResizeTasksSyncTest {
private const val FIRST_ACTIVITY = "Activity1"
private const val SECOND_ACTIVITY = "Activity2"
}
-} \ No newline at end of file
+}
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
index a9fbfd97225d..81ec265c2c29 100644
--- a/tests/TelephonyCommonTests/Android.bp
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -47,7 +47,7 @@ android_test {
// Uncomment this and comment out the jarjar rule if you want to attach a debugger and step
// through the renamed classes.
- // platform_apis: true,
+ platform_apis: true,
libs: [
"android.test.runner",
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
index 052ce3a902c1..adefac64dbae 100644
--- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -44,6 +44,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
@@ -75,9 +76,12 @@ import java.util.stream.Collectors;
public class SmsApplicationTest {
private static final ComponentName TEST_COMPONENT_NAME =
ComponentName.unflattenFromString("com.android.test/.TestSmsApp");
+ public static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth.services";
private static final String MMS_RECEIVER_NAME = "TestMmsReceiver";
private static final String RESPOND_VIA_SMS_NAME = "TestRespondViaSmsHandler";
private static final String SEND_TO_NAME = "TestSendTo";
+ private static final String EXTERNAL_PROVIDER_CHANGE_NAME = "TestExternalProviderChangeHandler";
+ private static final String SIM_FULL_NAME = "TestSimFullHandler";
private static final int SMS_APP_UID = 10001;
private static final int FAKE_PHONE_UID = 10002;
@@ -102,6 +106,7 @@ public class SmsApplicationTest {
}).collect(Collectors.toSet());
@Mock private Context mContext;
+ @Mock private Resources mResources;
@Mock private TelephonyManager mTelephonyManager;
@Mock private RoleManager mRoleManager;
@Mock private PackageManager mPackageManager;
@@ -118,6 +123,9 @@ public class SmsApplicationTest {
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
when(mContext.createContextAsUser(isNotNull(), anyInt())).thenReturn(mContext);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getString(eq(com.android.internal.R.string.config_systemBluetoothStack)))
+ .thenReturn(BLUETOOTH_PACKAGE_NAME);
doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
.when(mPackageManager)
@@ -146,10 +154,46 @@ public class SmsApplicationTest {
}
}
+
@Test
- public void testGetDefaultSmsApplication() {
+ public void testGetDefaultSmsApplicationAsUser() {
assertEquals(TEST_COMPONENT_NAME,
- SmsApplication.getDefaultSmsApplicationAsUser(mContext, false, 0));
+ SmsApplication.getDefaultSmsApplicationAsUser(mContext, false,
+ UserHandle.SYSTEM));
+ }
+
+
+ @Test
+ public void testGetDefaultMmsApplicationAsUser() {
+ ComponentName componentName = SmsApplication.getDefaultMmsApplicationAsUser(mContext,
+ false, UserHandle.SYSTEM);
+ assertEquals(TEST_COMPONENT_NAME.getPackageName(), componentName.getPackageName());
+ assertEquals(MMS_RECEIVER_NAME, componentName.getClassName());
+ }
+
+ @Test
+ public void testGetDefaultExternalTelephonyProviderChangedApplicationAsUser() {
+ ComponentName componentName = SmsApplication
+ .getDefaultExternalTelephonyProviderChangedApplicationAsUser(mContext,
+ false, UserHandle.SYSTEM);
+ assertEquals(TEST_COMPONENT_NAME.getPackageName(), componentName.getPackageName());
+ assertEquals(EXTERNAL_PROVIDER_CHANGE_NAME, componentName.getClassName());
+ }
+
+ @Test
+ public void testGetDefaultRespondViaMessageApplicationAsUserAsUser() {
+ ComponentName componentName = SmsApplication.getDefaultRespondViaMessageApplicationAsUser(
+ mContext, false, UserHandle.SYSTEM);
+ assertEquals(TEST_COMPONENT_NAME.getPackageName(), componentName.getPackageName());
+ assertEquals(RESPOND_VIA_SMS_NAME, componentName.getClassName());
+ }
+
+ @Test
+ public void testGetDefaultSimFullApplicationAsUser() {
+ ComponentName componentName = SmsApplication.getDefaultSimFullApplicationAsUser(mContext,
+ false, UserHandle.SYSTEM);
+ assertEquals(TEST_COMPONENT_NAME.getPackageName(), componentName.getPackageName());
+ assertEquals(SIM_FULL_NAME, componentName.getClassName());
}
@Test
@@ -160,7 +204,8 @@ public class SmsApplicationTest {
setupPackageInfosForCoreApps();
assertEquals(TEST_COMPONENT_NAME,
- SmsApplication.getDefaultSmsApplicationAsUser(mContext, true, 0));
+ SmsApplication.getDefaultSmsApplicationAsUser(mContext, true,
+ UserHandle.SYSTEM));
verify(mAppOpsManager, atLeastOnce()).setUidMode(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
AppOpsManager.MODE_ALLOWED);
}
@@ -237,6 +282,10 @@ public class SmsApplicationTest {
return Collections.singletonList(makeRespondViaMessageResolveInfo());
case Intent.ACTION_SENDTO:
return Collections.singletonList(makeSendToResolveInfo());
+ case Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE:
+ return Collections.singletonList(makeExternalProviderChangeResolveInfo());
+ case Telephony.Sms.Intents.SIM_FULL_ACTION:
+ return Collections.singletonList(makeSimFullResolveInfo());
}
return Collections.emptyList();
}
@@ -294,4 +343,26 @@ public class SmsApplicationTest {
info.activityInfo = activityInfo;
return info;
}
+
+ private ResolveInfo makeExternalProviderChangeResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+
+ activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ activityInfo.name = EXTERNAL_PROVIDER_CHANGE_NAME;
+
+ info.activityInfo = activityInfo;
+ return info;
+ }
+
+ private ResolveInfo makeSimFullResolveInfo() {
+ ResolveInfo info = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+
+ activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+ activityInfo.name = SIM_FULL_NAME;
+
+ info.activityInfo = activityInfo;
+ return info;
+ }
}
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
new file mode 100644
index 000000000000..a62103e0030b
--- /dev/null
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.internal.telephony.tests;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.util.TelephonyUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class TelephonyUtilsTest {
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ // Mocked classes
+ @Mock
+ private Context mContext;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+
+ @Before
+ public void setup() {
+ doReturn(mSubscriptionManager).when(mContext)
+ .getSystemService(eq(SubscriptionManager.class));
+ }
+
+
+ @Test
+ public void getSubscriptionUserHandle_subId_invalid() {
+ int invalidSubId = -10;
+ doReturn(false).when(mSubscriptionManager).isActiveSubscriptionId(eq(invalidSubId));
+
+ TelephonyUtils.getSubscriptionUserHandle(mContext, invalidSubId);
+
+ // getSubscriptionUserHandle should not be called if subID is inactive.
+ verify(mSubscriptionManager, never()).getSubscriptionUserHandle(eq(invalidSubId));
+ }
+
+ @Test
+ public void getSubscriptionUserHandle_subId_valid() {
+ int activeSubId = 1;
+ doReturn(true).when(mSubscriptionManager).isActiveSubscriptionId(eq(activeSubId));
+
+ TelephonyUtils.getSubscriptionUserHandle(mContext, activeSubId);
+
+ // getSubscriptionUserHandle should be called if subID is active.
+ verify(mSubscriptionManager, times(1)).getSubscriptionUserHandle(eq(activeSubId));
+ }
+}
+
+
diff --git a/tests/TouchLatency/Android.bp b/tests/TouchLatency/Android.bp
index 3a9e240d9746..4ef1ead7d9c9 100644
--- a/tests/TouchLatency/Android.bp
+++ b/tests/TouchLatency/Android.bp
@@ -12,6 +12,7 @@ android_test {
manifest: "app/src/main/AndroidManifest.xml",
// omit gradle 'build' dir
srcs: ["app/src/main/java/**/*.java"],
+ static_libs: ["com.google.android.material_material"],
resource_dirs: ["app/src/main/res"],
aaptflags: ["--auto-add-overlay"],
sdk_version: "current",
diff --git a/tests/TouchLatency/OWNERS b/tests/TouchLatency/OWNERS
new file mode 100644
index 000000000000..2b7de2555587
--- /dev/null
+++ b/tests/TouchLatency/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+include platform/frameworks/native:/services/surfaceflinger/OWNERS \ No newline at end of file
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 04a878896f47..07f6bcabb907 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,12 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
- buildToolsVersion '28.0.3'
+ compileSdkVersion 33
defaultConfig {
applicationId "com.prefabulated.touchlatency"
- minSdkVersion 28
- targetSdkVersion 28
+ minSdkVersion 30
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
}
@@ -17,4 +16,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+
+ dependencies {
+ implementation 'androidx.appcompat:appcompat:1.5.1'
+ implementation 'com.google.android.material:material:1.6.0'
+ }
}
diff --git a/tests/TouchLatency/app/src/main/AndroidManifest.xml b/tests/TouchLatency/app/src/main/AndroidManifest.xml
index 98947367bd7b..5743b2586416 100644
--- a/tests/TouchLatency/app/src/main/AndroidManifest.xml
+++ b/tests/TouchLatency/app/src/main/AndroidManifest.xml
@@ -20,13 +20,13 @@
<application android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/AppTheme">
+ android:theme="@style/AppTheme"
+ android:resizeableActivity="true" >
<activity android:name=".TouchLatencyActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
-
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index 1664746f4636..7678633dfa5e 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -16,218 +16,62 @@
package com.prefabulated.touchlatency;
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
+import android.hardware.display.DisplayManager;
import android.os.Bundle;
-import android.util.AttributeSet;
-import android.util.Log;
+import android.os.Handler;
+import android.os.Trace;
import android.view.Display;
import android.view.Display.Mode;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.os.Trace;
import android.view.Window;
import android.view.WindowManager;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-
-class TouchLatencyView extends View implements View.OnTouchListener {
- private static final String LOG_TAG = "TouchLatency";
- private static final int BACKGROUND_COLOR = 0xFF400080;
- private static final int INNER_RADIUS = 70;
- private static final int BALL_DIAMETER = 200;
- private static final int SEC_TO_NANOS = 1000000000;
- private static final float FPS_UPDATE_THRESHOLD = 20;
- private static final long BALL_VELOCITY = 420;
-
- public TouchLatencyView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Trace.beginSection("TouchLatencyView constructor");
- setOnTouchListener(this);
- setWillNotDraw(false);
- mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBluePaint.setColor(0xFF0000FF);
- mBluePaint.setStyle(Paint.Style.FILL);
- mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mGreenPaint.setColor(0xFF00FF00);
- mGreenPaint.setStyle(Paint.Style.FILL);
- mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mYellowPaint.setColor(0xFFFFFF00);
- mYellowPaint.setStyle(Paint.Style.FILL);
- mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mRedPaint.setColor(0xFFFF0000);
- mRedPaint.setStyle(Paint.Style.FILL);
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mTextPaint.setColor(0xFFFFFFFF);
- mTextPaint.setTextSize(100);
- mTextPaint.setTextAlign(Align.RIGHT);
-
- mTouching = false;
-
- mLastDrawNano = 0;
- mFps = 0;
- mLastFpsUpdate = 0;
- mFrameCount = 0;
-
- mDf = new DecimalFormat("fps: #.##");
- mDf.setRoundingMode(RoundingMode.HALF_UP);
-
- Trace.endSection();
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- Trace.beginSection("TouchLatencyView onTouch");
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
- mTouching = true;
- invalidate();
-
- mTouchX = event.getX();
- mTouchY = event.getY();
- } else if (action == MotionEvent.ACTION_UP) {
- mTouching = false;
- invalidate();
- }
- Trace.endSection();
- return true;
- }
-
- private void drawTouch(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawTouch");
-
- try {
- if (!mTouching) {
- Log.d(LOG_TAG, "Filling background");
- canvas.drawColor(BACKGROUND_COLOR);
- return;
- }
-
- float deltaX = (mTouchX - mLastDrawnX);
- float deltaY = (mTouchY - mLastDrawnY);
- float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
- mLastDrawnX = mTouchX;
- mLastDrawnY = mTouchY;
-
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
- } finally {
- Trace.endSection();
- }
- }
-
- private Paint getBallColor() {
- if (mFps > 75)
- return mGreenPaint;
- else if (mFps > 45)
- return mYellowPaint;
- else
- return mRedPaint;
- }
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
- private void drawBall(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawBall");
- int width = canvas.getWidth();
- int height = canvas.getHeight();
- float fps = 0f;
+import com.google.android.material.slider.RangeSlider;
+import com.google.android.material.slider.RangeSlider.OnChangeListener;
- long t = System.nanoTime();
- long tDiff = t - mLastDrawNano;
- mLastDrawNano = t;
- mFrameCount++;
+public class TouchLatencyActivity extends AppCompatActivity {
+ private static final int REFRESH_RATE_SLIDER_MIN = 20;
+ private static final int REFRESH_RATE_SLIDER_STEP = 1;
- if (tDiff < SEC_TO_NANOS) {
- fps = 1f * SEC_TO_NANOS / tDiff;
- }
-
- long fDiff = t - mLastFpsUpdate;
- if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
- mFps = fps;
- mLastFpsUpdate = t;
- mFrameCount = 0;
- } else if (fDiff > SEC_TO_NANOS) {
- mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
- mLastFpsUpdate = t;
- mFrameCount = 0;
+ private Menu mMenu;
+ private Mode[] mDisplayModes;
+ private int mCurrentModeIndex;
+ private float mSliderPreferredRefreshRate;
+ private DisplayManager mDisplayManager;
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int i) {
+ updateOptionsMenu();
}
- final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
- final long xMax = width - BALL_DIAMETER;
- final long yMax = height - BALL_DIAMETER;
- long xOffset = pos % xMax;
- long yOffset = pos % yMax;
-
- float left, right, top, bottom;
-
- if (((pos / xMax) & 1) == 0) {
- left = xMax - xOffset;
- } else {
- left = xOffset;
+ @Override
+ public void onDisplayRemoved(int i) {
+ updateOptionsMenu();
}
- right = left + BALL_DIAMETER;
- if (((pos / yMax) & 1) == 0) {
- top = yMax - yOffset;
- } else {
- top = yOffset;
+ @Override
+ public void onDisplayChanged(int i) {
+ updateOptionsMenu();
}
- bottom = top + BALL_DIAMETER;
+ };
- // Draw the ball
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawOval(left, top, right, bottom, getBallColor());
- canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
+ private final RangeSlider.OnChangeListener mRefreshRateSliderListener = new OnChangeListener() {
+ @Override
+ public void onValueChange(@NonNull RangeSlider slider, float value, boolean fromUser) {
+ if (value == mSliderPreferredRefreshRate) return;
- invalidate();
- Trace.endSection();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Trace.beginSection("TouchLatencyView onDraw");
- if (mMode == 0) {
- drawTouch(canvas);
- } else {
- drawBall(canvas);
+ mSliderPreferredRefreshRate = value;
+ WindowManager.LayoutParams w = getWindow().getAttributes();
+ w.preferredRefreshRate = mSliderPreferredRefreshRate;
+ getWindow().setAttributes(w);
}
- Trace.endSection();
- }
-
- public void changeMode(MenuItem item) {
- Trace.beginSection("TouchLatencyView changeMode");
- final int NUM_MODES = 2;
- final String modes[] = {"Touch", "Ball"};
- mMode = (mMode + 1) % NUM_MODES;
- invalidate();
- item.setTitle(modes[mMode]);
- Trace.endSection();
- }
-
- private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
- private int mMode;
-
- private boolean mTouching;
- private float mTouchX, mTouchY;
- private float mLastDrawnX, mLastDrawnY;
-
- private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
- private float mFps;
- private DecimalFormat mDf;
-}
-
-public class TouchLatencyActivity extends Activity {
- private Mode mDisplayModes[];
- private int mCurrentModeIndex;
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -235,9 +79,9 @@ public class TouchLatencyActivity extends Activity {
Trace.beginSection("TouchLatencyActivity onCreate");
setContentView(R.layout.activity_touch_latency);
-
mTouchView = findViewById(R.id.canvasView);
+ configureDisplayListener();
WindowManager wm = getWindowManager();
Display display = wm.getDefaultDisplay();
mDisplayModes = display.getSupportedModes();
@@ -249,37 +93,89 @@ public class TouchLatencyActivity extends Activity {
break;
}
}
-
Trace.endSection();
}
+ public void updateOptionsMenu() {
+ if (mDisplayModes.length > 1) {
+ MenuItem menuItem = mMenu.findItem(R.id.display_mode);
+ Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
+ updateDisplayMode(menuItem, currentMode);
+ }
+ updateRefreshRateMenu(mMenu.findItem(R.id.frame_rate));
+ }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
+ mMenu = menu;
// Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
- if (mDisplayModes.length > 1) {
- MenuItem menuItem = menu.findItem(R.id.display_mode);
- Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
- updateDisplayMode(menuItem, currentMode);
- }
+ getMenuInflater().inflate(R.menu.menu_touch_latency, mMenu);
+ updateOptionsMenu();
Trace.endSection();
return true;
}
-
private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
int fps = (int) displayMode.getRefreshRate();
menuItem.setTitle(fps + "hz");
menuItem.setVisible(true);
}
+ private float getHighestRefreshRate() {
+ float maxRefreshRate = 0;
+ for (Display.Mode mode : getDisplay().getSupportedModes()) {
+ if (sameSizeMode(mode) && mode.getRefreshRate() > maxRefreshRate) {
+ maxRefreshRate = mode.getRefreshRate();
+ }
+ }
+ return maxRefreshRate;
+ }
+
+ private void updateRefreshRateMenu(MenuItem item) {
+ item.setActionView(R.layout.refresh_rate_layout);
+ RangeSlider slider = item.getActionView().findViewById(R.id.slider_from_layout);
+ slider.addOnChangeListener(mRefreshRateSliderListener);
+
+ float highestRefreshRate = getHighestRefreshRate();
+ slider.setValueFrom(REFRESH_RATE_SLIDER_MIN);
+ slider.setValueTo(highestRefreshRate);
+ slider.setStepSize(REFRESH_RATE_SLIDER_STEP);
+ if (mSliderPreferredRefreshRate < REFRESH_RATE_SLIDER_MIN
+ || mSliderPreferredRefreshRate > highestRefreshRate) {
+ mSliderPreferredRefreshRate = highestRefreshRate;
+ }
+ slider.setValues(mSliderPreferredRefreshRate);
+ }
+
+ private void updateMultiDisplayMenu(MenuItem item) {
+ item.setVisible(mDisplayManager.getDisplays().length > 1);
+ }
+
+ private void configureDisplayListener() {
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
+ }
+
+ private boolean sameSizeMode(Display.Mode mode) {
+ Mode currentMode = mDisplayModes[mCurrentModeIndex];
+ return currentMode.getPhysicalHeight() == mode.getPhysicalHeight()
+ && currentMode.getPhysicalWidth() == mode.getPhysicalWidth();
+ }
+
public void changeDisplayMode(MenuItem item) {
Window w = getWindow();
WindowManager.LayoutParams params = w.getAttributes();
int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length;
+ while (modeIndex != mCurrentModeIndex) {
+ // skip modes with different resolutions
+ if (sameSizeMode(mDisplayModes[modeIndex])) {
+ break;
+ }
+ modeIndex = (modeIndex + 1) % mDisplayModes.length;
+ }
+
params.preferredDisplayModeId = mDisplayModes[modeIndex].getModeId();
w.setAttributes(params);
@@ -287,7 +183,6 @@ public class TouchLatencyActivity extends Activity {
mCurrentModeIndex = modeIndex;
}
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Trace.beginSection("TouchLatencyActivity onOptionsItemSelected");
@@ -297,15 +192,28 @@ public class TouchLatencyActivity extends Activity {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- mTouchView.changeMode(item);
- } else if (id == R.id.display_mode) {
- changeDisplayMode(item);
+ switch (id) {
+ case R.id.action_settings: {
+ mTouchView.changeMode(item);
+ break;
+ }
+ case R.id.display_mode: {
+ changeDisplayMode(item);
+ break;
+ }
}
Trace.endSection();
return super.onOptionsItemSelected(item);
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mDisplayManager != null) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+ }
+
private TouchLatencyView mTouchView;
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
new file mode 100644
index 000000000000..0803e8e8510f
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
@@ -0,0 +1,219 @@
+/*
+ * 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.prefabulated.touchlatency;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+class TouchLatencyView extends View implements View.OnTouchListener {
+ private static final String LOG_TAG = "TouchLatency";
+ private static final int BACKGROUND_COLOR = 0xFF400080;
+ private static final int INNER_RADIUS = 70;
+ private static final int BALL_DIAMETER = 200;
+ private static final int SEC_TO_NANOS = 1000000000;
+ private static final float FPS_UPDATE_THRESHOLD = 20;
+ private static final long BALL_VELOCITY = 420;
+
+ public TouchLatencyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Trace.beginSection("TouchLatencyView constructor");
+ setOnTouchListener(this);
+ setWillNotDraw(false);
+ mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mBluePaint.setColor(0xFF0000FF);
+ mBluePaint.setStyle(Paint.Style.FILL);
+ mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mGreenPaint.setColor(0xFF00FF00);
+ mGreenPaint.setStyle(Paint.Style.FILL);
+ mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mYellowPaint.setColor(0xFFFFFF00);
+ mYellowPaint.setStyle(Paint.Style.FILL);
+ mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRedPaint.setColor(0xFFFF0000);
+ mRedPaint.setStyle(Paint.Style.FILL);
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setColor(0xFFFFFFFF);
+ mTextPaint.setTextSize(100);
+ mTextPaint.setTextAlign(Paint.Align.RIGHT);
+
+ mTouching = false;
+
+ mLastDrawNano = 0;
+ mFps = 0;
+ mLastFpsUpdate = 0;
+ mFrameCount = 0;
+
+ mDf = new DecimalFormat("fps: #.##");
+ mDf.setRoundingMode(RoundingMode.HALF_UP);
+
+ Trace.endSection();
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ Trace.beginSection("TouchLatencyView onTouch");
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ mTouching = true;
+ invalidate();
+
+ mTouchX = event.getX();
+ mTouchY = event.getY();
+ } else if (action == MotionEvent.ACTION_UP) {
+ mTouching = false;
+ invalidate();
+ }
+ Trace.endSection();
+ return true;
+ }
+
+ private void drawTouch(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawTouch");
+
+ try {
+ if (!mTouching) {
+ Log.d(LOG_TAG, "Filling background");
+ canvas.drawColor(BACKGROUND_COLOR);
+ return;
+ }
+
+ float deltaX = (mTouchX - mLastDrawnX);
+ float deltaY = (mTouchY - mLastDrawnY);
+ float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
+
+ mLastDrawnX = mTouchX;
+ mLastDrawnY = mTouchY;
+
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private Paint getBallColor() {
+ if (mFps > 75) {
+ return mGreenPaint;
+ } else if (mFps > 45) {
+ return mYellowPaint;
+ } else
+ return mRedPaint;
+ }
+
+ private void drawBall(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawBall");
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+ float fps = 0f;
+
+ long t = System.nanoTime();
+ long tDiff = t - mLastDrawNano;
+ mLastDrawNano = t;
+ mFrameCount++;
+
+ if (tDiff < SEC_TO_NANOS) {
+ fps = 1f * SEC_TO_NANOS / tDiff;
+ }
+
+ long fDiff = t - mLastFpsUpdate;
+ if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+ mFps = fps;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ } else if (fDiff > SEC_TO_NANOS) {
+ mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ }
+
+ final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+ final long xMax = width - BALL_DIAMETER;
+ final long yMax = height - BALL_DIAMETER;
+ long xOffset = pos % xMax;
+ long yOffset = pos % yMax;
+
+ float left, right, top, bottom;
+
+ if (((pos / xMax) & 1) == 0) {
+ left = xMax - xOffset;
+ } else {
+ left = xOffset;
+ }
+ right = left + BALL_DIAMETER;
+
+ if (((pos / yMax) & 1) == 0) {
+ top = yMax - yOffset;
+ } else {
+ top = yOffset;
+ }
+ bottom = top + BALL_DIAMETER;
+
+ // Draw the ball
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawOval(left, top, right, bottom, getBallColor());
+ canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
+
+ invalidate();
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Trace.beginSection("TouchLatencyView onDraw");
+ if (mMode == 0) {
+ drawTouch(canvas);
+ } else {
+ drawBall(canvas);
+ }
+ Trace.endSection();
+ }
+
+ public void changeMode(MenuItem item) {
+ Trace.beginSection("TouchLatencyView changeMode");
+ final int NUM_MODES = 2;
+ final String modes[] = {"Touch", "Ball"};
+ mMode = (mMode + 1) % NUM_MODES;
+ invalidate();
+ item.setTitle(modes[mMode]);
+ Trace.endSection();
+ }
+
+ private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
+ private int mMode;
+
+ private boolean mTouching;
+ private float mTouchX, mTouchY;
+ private float mLastDrawnX, mLastDrawnY;
+
+ private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+ private float mFps;
+ private DecimalFormat mDf;
+}
diff --git a/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml b/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml
new file mode 100644
index 000000000000..bb9ce609c56f
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/res/layout/refresh_rate_layout.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <com.google.android.material.slider.RangeSlider
+ android:id="@+id/slider_from_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:tickColor="@color/cardview_light_background"
+ app:trackColor="@color/cardview_light_background"
+ app:thumbColor="@color/cardview_dark_background"
+ android:visibility="visible"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index 52be91900ae8..f637f71f7ef2 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -14,15 +14,20 @@
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".TouchLatencyActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="101"
- android:showAsAction="always"
- android:title="@string/mode"/>
+ android:title="@string/mode"
+ app:showAsAction="always" />
+ <item
+ android:id="@+id/frame_rate"
+ android:title="@string/frame_rate"
+ app:showAsAction="collapseActionView" />
<item
android:id="@+id/display_mode"
- android:showAsAction="ifRoom"
android:title="@string/display_mode"
- android:visible="false"/>
+ android:visible="false"
+ app:showAsAction="always" />
</menu>
diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml
index 771992c8e5d3..cad2df78ffcd 100644
--- a/tests/TouchLatency/app/src/main/res/values/strings.xml
+++ b/tests/TouchLatency/app/src/main/res/values/strings.xml
@@ -18,4 +18,6 @@
<string name="mode">Touch</string>
<string name="display_mode">Mode</string>
+ <string name="frame_rate">Frame Rate</string>
+ <string name="multi_display">multi-display</string>
</resources>
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index 22da7c1d050b..b23a87e57754 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -16,7 +16,7 @@
<resources>
<!-- Base application theme. -->
- <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
+ <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index 03abe82a4359..f52935bae092 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.android.tools.build:gradle:7.4.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/tests/TouchLatency/gradle.properties b/tests/TouchLatency/gradle.properties
index 1d3591c8a4c9..ccd5dda1d6fa 100644
--- a/tests/TouchLatency/gradle.properties
+++ b/tests/TouchLatency/gradle.properties
@@ -15,4 +15,5 @@
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true \ No newline at end of file
+# org.gradle.parallel=true
+android.useAndroidX=true \ No newline at end of file
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
index 758de960ec79..7454180f2ae8 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 2d80b69a7665..8049c684f04f 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/tests/TouchLatency/gradlew b/tests/TouchLatency/gradlew
index cccdd3d517fc..1b6c787337ff 100755
--- a/tests/TouchLatency/gradlew
+++ b/tests/TouchLatency/gradlew
@@ -1,78 +1,129 @@
-#!/usr/bin/env sh
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# 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
+#
+# https://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.
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -81,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -89,84 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=$(save "$@")
-
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
-
-# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
-if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
- cd "$(dirname "$0")"
-fi
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/tests/TouchLatency/gradlew.bat b/tests/TouchLatency/gradlew.bat
index e95643d6a2ca..ac1b06f93825 100644
--- a/tests/TouchLatency/gradlew.bat
+++ b/tests/TouchLatency/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -35,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
index 77f98e88f1eb..a1b888aef934 100644
--- a/tests/TrustTests/Android.bp
+++ b/tests/TrustTests/Android.bp
@@ -24,7 +24,7 @@ android_test {
static_libs: [
"androidx.test.rules",
"androidx.test.ext.junit",
- "androidx.test.uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"mockito-target-minus-junit4",
"servicestests-utils",
"truth-prebuilt",
diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml
index 8b4cbfd0e44b..30cf345db34d 100644
--- a/tests/TrustTests/AndroidManifest.xml
+++ b/tests/TrustTests/AndroidManifest.xml
@@ -78,6 +78,16 @@
<action android:name="android.service.trust.TrustAgentService" />
</intent-filter>
</service>
+ <service
+ android:name=".IsActiveUnlockRunningTrustAgent"
+ android:exported="true"
+ android:label="Test Agent"
+ android:permission="android.permission.BIND_TRUST_AGENT">
+ <intent-filter>
+ <action android:name="android.service.trust.TrustAgentService" />
+ </intent-filter>
+ </service>
+
</application>
<!-- self-instrumenting test package. -->
diff --git a/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt b/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt
new file mode 100644
index 000000000000..7b68a829e23b
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/CanUnlockWithActiveUnlockTest.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.trust.test
+
+import android.app.trust.TrustManager
+import android.content.Context
+import android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.LockStateTrackingRule
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TestTrustListener
+import android.trust.test.lib.TrustAgentRule
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for testing isActiveUnlockRunning.
+ *
+ * atest TrustTests:IsActiveUnlockRunningTest
+ */
+@RunWith(AndroidJUnit4::class)
+class IsActiveUnlockRunningTest {
+ private val uiDevice = UiDevice.getInstance(getInstrumentation())
+ private val context: Context = getApplicationContext()
+ private val userId = context.userId
+ private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<IsActiveUnlockRunningTrustAgent>()
+
+ private val listener = object : TestTrustListener() {
+ var isRunning = false
+ private set
+
+ override fun onIsActiveUnlockRunningChanged(isRunning: Boolean, userId: Int) {
+ this.isRunning = isRunning
+ }
+ }
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Before
+ fun manageTrust() {
+ trustAgentRule.agent.setManagingTrust(true)
+ trustManager.registerTrustListener(listener)
+ }
+
+ @After
+ fun unregisterListener() {
+ trustManager.unregisterTrustListener(listener)
+ }
+
+ @Test
+ fun defaultState_isActiveUnlockRunningIsFalse() {
+ assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse()
+ assertThat(listener.isRunning).isFalse()
+ }
+
+ @Test
+ fun grantTrustLockedDevice_isActiveUnlockRunningIsFalse() {
+ uiDevice.sleep()
+ lockStateTrackingRule.assertLocked()
+
+ uiDevice.wakeUp()
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+
+ assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse()
+ assertThat(listener.isRunning).isFalse()
+ }
+
+ @Test
+ fun grantTrustUnlockedDevice_isActiveUnlockRunningIsTrueWhileLocked() {
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+
+ assertThat(trustManager.isActiveUnlockRunning(userId)).isTrue()
+ assertThat(listener.isRunning).isTrue()
+ }
+
+ @Test
+ fun trustRevoked_isActiveUnlockRunningIsFalse() {
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+
+ trustAgentRule.agent.revokeTrust()
+
+ assertThat(trustManager.isActiveUnlockRunning(userId)).isFalse()
+ assertThat(listener.isRunning).isFalse()
+ }
+
+ companion object {
+ private const val GRANT_MESSAGE = "granted by test"
+ private fun await(millis: Long) = Thread.sleep(millis)
+ }
+}
+
+class IsActiveUnlockRunningTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
index 6a8752abfde7..501b9d33871a 100644
--- a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
+++ b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
@@ -79,6 +79,16 @@ class UserUnlockRequestTest {
.isEqualTo(oldCount + 1)
}
+ @Test
+ fun reportUserMayRequestUnlock_differentUserId_doesNotPropagateToAgent() {
+ val oldCount = trustAgentRule.agent.onUserMayRequestUnlockCallCount
+ trustManager.reportUserMayRequestUnlock(userId + 1)
+ await()
+
+ assertThat(trustAgentRule.agent.onUserMayRequestUnlockCallCount)
+ .isEqualTo(oldCount)
+ }
+
companion object {
private const val TAG = "UserUnlockRequestTest"
private fun await() = Thread.sleep(250)
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 2031af2cf0c9..1400dde5781d 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -17,7 +17,6 @@
package android.trust.test.lib
import android.app.trust.TrustManager
-import android.app.trust.TrustManager.TrustListener
import android.content.Context
import android.util.Log
import android.view.WindowManagerGlobal
@@ -60,9 +59,10 @@ class LockStateTrackingRule : TestRule {
wait("locked per TrustListener") { lockState.locked == false }
}
- inner class Listener : TrustListener {
+ inner class Listener : TestTrustListener() {
override fun onTrustChanged(
enabled: Boolean,
+ newlyUnlocked: Boolean,
userId: Int,
flags: Int,
trustGrantedMessages: MutableList<String>
@@ -70,12 +70,6 @@ class LockStateTrackingRule : TestRule {
Log.d(TAG, "Device became trusted=$enabled")
lockState = lockState.copy(locked = !enabled)
}
-
- override fun onTrustManagedChanged(enabled: Boolean, userId: Int) {
- }
-
- override fun onTrustError(message: CharSequence) {
- }
}
data class LockState(
diff --git a/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt b/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt
new file mode 100644
index 000000000000..880497e954f1
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/TestTrustListener.kt
@@ -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 android.trust.test.lib
+
+import android.app.trust.TrustManager.TrustListener
+
+/**
+ * A listener that has default empty implementations for all [TrustListener] methods.
+ */
+open class TestTrustListener : TrustListener {
+ override fun onTrustChanged(
+ enabled: Boolean,
+ newlyUnlocked: Boolean,
+ userId: Int,
+ flags: Int,
+ trustGrantedMessages: MutableList<String>
+ ) {
+ }
+
+ override fun onTrustManagedChanged(enabled: Boolean, userId: Int) {
+ }
+
+ override fun onTrustError(message: CharSequence) {
+ }
+
+ override fun onEnabledTrustAgentsChanged(userId: Int) {
+ }
+
+ override fun onIsActiveUnlockRunningChanged(isRunning: Boolean, userId: Int) {
+ }
+}
diff --git a/tests/UiBench/Android.bp b/tests/UiBench/Android.bp
index 0d2f2ef46cab..90e61c52da68 100644
--- a/tests/UiBench/Android.bp
+++ b/tests/UiBench/Android.bp
@@ -24,5 +24,6 @@ android_test {
"androidx.recyclerview_recyclerview",
"androidx.leanback_leanback",
],
+ certificate: "platform",
test_suites: ["device-tests"],
}
diff --git a/tests/UiBench/AndroidManifest.xml b/tests/UiBench/AndroidManifest.xml
index 4fc6ec71f29c..47211c5fbad1 100644
--- a/tests/UiBench/AndroidManifest.xml
+++ b/tests/UiBench/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.android.test.uibench">
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
<application android:allowBackup="false"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index 9a9e42bfc300..9bfcc18ee301 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,6 +37,7 @@ android_test {
"vts",
],
data: [
+ ":EmojiRenderingTestApp",
":UpdatableSystemFontTestCertDer",
":UpdatableSystemFontTest_NotoColorEmoji.ttf",
":UpdatableSystemFontTest_NotoColorEmoji.sig",
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index cbe13d9aa149..fa5b7c15a6fe 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -69,6 +69,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
/**
* Tests if fonts can be updated by {@link FontManager} API.
@@ -246,7 +247,10 @@ public class UpdatableSystemFontTest {
@Test
public void updateFontFamily() throws Exception {
assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
- FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ final FontConfig.NamedFamilyList namedFamilyList = findFontFamilyOrThrow("serif");
+ assertThat(namedFamilyList.getFamilies().size()).isEqualTo(1);
+ final FontConfig.FontFamily family = namedFamilyList.getFamilies().get(0);
+
assertThat(family.getFontList()).hasSize(2);
assertThat(family.getFontList().get(0).getPostScriptName())
.isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
@@ -265,7 +269,10 @@ public class UpdatableSystemFontTest {
public void updateFontFamily_asNewFont() throws Exception {
assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
.isEqualTo(FontManager.RESULT_SUCCESS);
- FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ final FontConfig.NamedFamilyList namedFamilyList =
+ findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(namedFamilyList.getFamilies().size()).isEqualTo(1);
+ final FontConfig.FontFamily family = namedFamilyList.getFamilies().get(0);
assertThat(family.getFontList()).hasSize(2);
assertThat(family.getFontList().get(0).getPostScriptName())
.isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
@@ -373,6 +380,10 @@ public class UpdatableSystemFontTest {
try (InputStream is = new FileInputStream(certPath)) {
result = runShellCommand("mini-keyctl padd asymmetric fsv_test .fs-verity", is);
}
+ // /data/local/tmp is not readable by system server. Copy a cert file to /data/fonts
+ final String copiedCert = "/data/fonts/debug_cert.der";
+ runShellCommand("cp " + certPath + " " + copiedCert, null);
+ runShellCommand("cmd font install-debug-cert " + copiedCert, null);
// Assert that there are no errors.
assertThat(result.second).isEmpty();
String keyId = result.first.trim();
@@ -430,9 +441,15 @@ public class UpdatableSystemFontTest {
private String getFontPath(String psName) {
FontConfig fontConfig =
SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
- return fontConfig.getFontFamilies().stream()
+ final List<FontConfig.FontFamily> namedFamilies = fontConfig.getNamedFamilyLists().stream()
+ .flatMap(namedFamily -> namedFamily.getFamilies().stream()).toList();
+
+ return Stream.concat(fontConfig.getFontFamilies().stream(), namedFamilies.stream())
.flatMap(family -> family.getFontList().stream())
- .filter(font -> psName.equals(font.getPostScriptName()))
+ .filter(font -> {
+ Log.e("Debug", "PsName = " + font.getPostScriptName());
+ return psName.equals(font.getPostScriptName());
+ })
// Return the last match, because the latter family takes precedence if two families
// have the same name.
.reduce((first, second) -> second)
@@ -441,10 +458,10 @@ public class UpdatableSystemFontTest {
.getAbsolutePath();
}
- private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ private FontConfig.NamedFamilyList findFontFamilyOrThrow(String familyName) {
FontConfig fontConfig =
SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
- return fontConfig.getFontFamilies().stream()
+ return fontConfig.getNamedFamilyLists().stream()
.filter(family -> familyName.equals(family.getName()))
// Return the last match, because the latter family takes precedence if two families
// have the same name.
diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
index 782439f80fc8..e2099e652c49 100644
--- a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
+++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java
@@ -24,12 +24,15 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.usb.UsbManager;
+import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.atomic.AtomicInteger;
+
/**
* Unit tests lib for {@link android.hardware.usb.UsbManager}.
*/
@@ -42,6 +45,11 @@ public class UsbManagerTestLib {
private UsbManager mUsbManagerMock;
@Mock private android.hardware.usb.IUsbManager mMockUsbService;
+ /**
+ * Counter for tracking UsbOperation operations.
+ */
+ private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+
public UsbManagerTestLib(Context context) {
MockitoAnnotations.initMocks(this);
mContext = context;
@@ -82,10 +90,11 @@ public class UsbManagerTestLib {
}
private void testSetCurrentFunctionsMock_Matched(long functions) {
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
try {
setCurrentFunctions(functions);
- verify(mMockUsbService).setCurrentFunctions(eq(functions));
+ verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId);
} catch (RemoteException remEx) {
Log.w(TAG, "RemoteException");
}
@@ -106,9 +115,10 @@ public class UsbManagerTestLib {
}
public void testSetCurrentFunctionsEx(long functions) throws Exception {
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
setCurrentFunctions(functions);
- verify(mMockUsbService).setCurrentFunctions(eq(functions));
+ verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId);
}
public void testGetCurrentFunctions_shouldMatched() {
@@ -117,6 +127,7 @@ public class UsbManagerTestLib {
testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP);
testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI);
testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS);
+ testGetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM);
}
public void testSetCurrentFunctions_shouldMatched() {
@@ -125,5 +136,6 @@ public class UsbManagerTestLib {
testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_PTP);
testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_MIDI);
testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS);
+ testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM);
}
}
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index 861d221238ff..210e3ea2a9b2 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -98,7 +98,7 @@ public class UsbHandlerTest {
}
@Override
- protected void setEnabledFunctions(long functions, boolean force) {
+ protected void setEnabledFunctions(long functions, boolean force, int operationId) {
mCurrentFunctions = functions;
}
@@ -134,6 +134,24 @@ public class UsbHandlerTest {
protected void sendStickyBroadcast(Intent intent) {
mBroadcastedIntent = intent;
}
+
+ @Override
+ public void handlerInitDone(int operationId) {
+ }
+
+ @Override
+ public void setCurrentUsbFunctionsCb(long functions,
+ int status, int mRequest, long mFunctions, boolean mChargingFunctions){
+ }
+
+ @Override
+ public void getUsbSpeedCb(int speed){
+ }
+
+ @Override
+ public void resetCb(int status){
+ }
+
}
@Before
@@ -182,6 +200,14 @@ public class UsbHandlerTest {
@SmallTest
@Test
+ public void setFunctionsNcm() {
+ mUsbHandler.handleMessage(mUsbHandler.obtainMessage(MSG_SET_CURRENT_FUNCTIONS,
+ UsbManager.FUNCTION_NCM));
+ assertNotEquals(mUsbHandler.getEnabledFunctions() & UsbManager.FUNCTION_NCM, 0);
+ }
+
+ @SmallTest
+ @Test
public void setFunctionsNcmAndRndis() {
final long rndisPlusNcm = UsbManager.FUNCTION_RNDIS | UsbManager.FUNCTION_NCM;
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java
new file mode 100644
index 000000000000..ad701e5117fc
--- /dev/null
+++ b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java
@@ -0,0 +1,407 @@
+/*
+ * 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.usb;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Unit tests for com.android.server.usb.UsbMidiPacketConverter.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class UsbMidiPacketConverterTest {
+ private byte[] generateRandomByteStream(Random rnd, int size) {
+ byte[] output = new byte[size];
+ rnd.nextBytes(output);
+ return output;
+ }
+
+ private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
+ assertEquals(expectedArray.length, outputArray.length);
+ for (int i = 0; i < outputArray.length; i++) {
+ assertEquals(expectedArray[i], outputArray[i]);
+ }
+ }
+
+ @Test
+ public void testDecoderSinglePacket() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(2);
+ byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66};
+ byte[] expectedOutputCable0 = new byte[] {};
+ byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ }
+
+ @Test
+ public void testDecoderMultiplePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(4);
+ byte[] input = new byte[] {
+ 0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E,
+ 0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00,
+ 0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12};
+ byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E};
+ byte[] expectedOutputCable2 = new byte[] {};
+ byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2);
+ byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ compareByteArrays(expectedOutputCable2, actualOutputCable2);
+ compareByteArrays(expectedOutputCable3, actualOutputCable3);
+ }
+
+ @Test
+ public void testDecoderSysExEndFirstByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(2);
+ byte[] input = new byte[] {
+ 0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {};
+ byte[] expectedOutputCable1 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ compareByteArrays(expectedOutputCable1, actualOutputCable1);
+ }
+
+ @Test
+ public void testDecoderSysExEndSecondByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] input = new byte[] {
+ 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ 0x02, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExEndThirdByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x00, 0x01,
+ 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExStartEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderSysExStartByteEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {
+ 0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderDefaultToFirstCable() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.createDecoders(1);
+ byte[] expectedOutputCable0 = new byte[] {
+ (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ compareByteArrays(expectedOutputCable0, actualOutputCable0);
+ }
+
+ @Test
+ public void testDecoderLargePacketDoesNotCrash() {
+ for (long seed = 1001; seed < 5000; seed += 777) {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createDecoders(3);
+ Random rnd = new Random(seed);
+ byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
+ usbMidiPacketConverter.decodeMidiPackets(input, input.length);
+ usbMidiPacketConverter.pullDecodedMidiPackets(0);
+ usbMidiPacketConverter.pullDecodedMidiPackets(1);
+ usbMidiPacketConverter.pullDecodedMidiPackets(2);
+ }
+ }
+
+ @Test
+ public void testEncoderBasic() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66};
+ byte[] expectedOutput = new byte[] {
+ 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderMultiplePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(3);
+ byte[] inputCable2 = new byte[] {
+ (byte) 0xB4 /* Control Change */, 0x55, 0x6E};
+ byte[] inputCable1 = new byte[] {
+ (byte) 0xF8 /* Timing Clock (Single Byte) */,
+ (byte) 0xF3 /* Song Select (Two Bytes) */, 0x12};
+ byte[] expectedOutput = new byte[] {
+ 0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E,
+ 0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00,
+ 0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderWeavePackets() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(2);
+ byte[] inputCable1Msg1 = new byte[] {
+ (byte) 0x93 /* Note-On */, 0x23, 0x43};
+ byte[] inputCable0Msg = new byte[] {
+ (byte) 0xB4 /* Control Change */, 0x65, 0x26};
+ byte[] inputCable1Msg2 = new byte[] {
+ (byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76};
+ byte[] expectedOutput = new byte[] {
+ 0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43,
+ 0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26,
+ 0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76};
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0);
+ usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndFirstByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndSecondByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ 0x02, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExEndThirdByte() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
+ 0x02, 0x03, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
+ 0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExStartEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderSysExStartByteEnd() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */};
+ byte[] expectedOutput = new byte[] {
+ 0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderMultiplePulls() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(1);
+
+ byte[] input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, 0x44, 0x55,
+ 0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time
+ byte[] expectedOutput = new byte[] {
+ 0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+
+ input = new byte[] {
+ 0x11, // Combined with 0x66 and 0x77 above
+ 0x22, (byte) 0xF7 /* SysEx End */};
+ expectedOutput = new byte[] {
+ 0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11,
+ 0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+
+ input = new byte[] {
+ (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
+ expectedOutput = new byte[] {
+ 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
+ output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderDefaultToFirstCable() {
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(2);
+ byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33};
+ byte[] expectedOutput = new byte[] {
+ 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33};
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4);
+ byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
+ compareByteArrays(expectedOutput, output);
+ }
+
+ @Test
+ public void testEncoderLargePacketDoesNotCrash() {
+ for (long seed = 234; seed < 4000; seed += 666) {
+ Random rnd = new Random(seed);
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(4);
+ for (int cableNumber = 0; cableNumber < 4; cableNumber++) {
+ byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
+ usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber);
+ }
+ usbMidiPacketConverter.pullEncodedMidiPackets();
+ }
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final int bufferSize = 30;
+ final int numCables = 16;
+ final int bytesToEncodePerEncoding = 10;
+ byte[][] rawMidi = new byte[numCables][bufferSize];
+ for (long seed = 45; seed < 3000; seed += 300) {
+ Random rnd = new Random(seed);
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize);
+
+ // Change the last byte to SysEx End.
+ // This way the encoder is guaranteed to flush all packets.
+ rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7;
+ }
+ UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
+ usbMidiPacketConverter.createEncoders(numCables);
+ // Encode packets and interweave them
+ for (int startByte = 0; startByte < bufferSize;
+ startByte += bytesToEncodePerEncoding) {
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte,
+ startByte + bytesToEncodePerEncoding);
+ usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length,
+ cableNumber);
+ }
+ }
+ byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets();
+
+ usbMidiPacketConverter.createDecoders(numCables);
+
+ // Now decode the MIDI packets to check if they are the same as the original
+ usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length);
+ for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
+ byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber);
+ compareByteArrays(rawMidi[cableNumber], decodedRawMidi);
+ }
+ }
+ }
+}
diff --git a/tests/VectorDrawableTest/OWNERS b/tests/VectorDrawableTest/OWNERS
new file mode 100644
index 000000000000..27e16681899e
--- /dev/null
+++ b/tests/VectorDrawableTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24939
+
+include /graphics/java/android/graphics/OWNERS
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index db4898492ac5..661dd845757b 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -33,6 +33,8 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
+import java.util.Arrays;
+
public class MainInteractionSession extends VoiceInteractionSession
implements View.OnClickListener {
static final String TAG = "MainInteractionSession";
@@ -403,7 +405,7 @@ public class MainInteractionSession extends VoiceInteractionSession
@Override
public void onRequestPickOption(PickOptionRequest request) {
Log.i(TAG, "onPickOption: prompt=" + request.getVoicePrompt() + " options="
- + request.getOptions() + " extras=" + request.getExtras());
+ + Arrays.toString(request.getOptions()) + " extras=" + request.getExtras());
mConfirmButton.setText("Pick Option");
mPendingRequest = request;
setPrompt(request.getVoicePrompt());
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
index 733f602f6c14..8ae7186461d3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/StartVoiceInteractionActivity.java
@@ -24,6 +24,8 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
+import java.util.Arrays;
+
public class StartVoiceInteractionActivity extends Activity implements View.OnClickListener {
static final String TAG = "LocalVoiceInteractionActivity";
@@ -187,7 +189,8 @@ public class StartVoiceInteractionActivity extends Activity implements View.OnCl
}
@Override
public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
- Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections
+ Log.i(TAG, "Pick result: finished=" + finished
+ + " selections=" + Arrays.toString(selections)
+ " result=" + result);
StringBuilder sb = new StringBuilder();
if (finished) {
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
index ada0e21e059a..4fc3a15ac38d 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -28,6 +28,8 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
+import java.util.Arrays;
+
public class TestInteractionActivity extends Activity implements View.OnClickListener {
static final String TAG = "TestInteractionActivity";
@@ -240,7 +242,8 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis
}
@Override
public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
- Log.i(TAG, "Pick result: finished=" + finished + " selections=" + selections
+ Log.i(TAG, "Pick result: finished=" + finished
+ + " selections=" + Arrays.toString(selections)
+ " result=" + result);
StringBuilder sb = new StringBuilder();
if (finished) {
diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp
index ed86aa5f90ea..8542f885d645 100644
--- a/tests/WindowAnimationJank/Android.bp
+++ b/tests/WindowAnimationJank/Android.bp
@@ -25,7 +25,7 @@ android_test {
name: "WindowAnimationJank",
srcs: ["src/**/*.java"],
static_libs: [
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"androidx.test.janktesthelper",
"junit",
],
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
index 25314644ca7e..48a359c4d0c6 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -18,11 +18,12 @@ import android.app.UiAutomation;
import android.content.ComponentName;
import android.content.Intent;
import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
/**
* Set of helpers to manipulate test activities.
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
index a8ace162c4d0..cb7c5112cba7 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -16,9 +16,8 @@
package android.windowanimationjank;
-import android.support.test.uiautomator.UiDevice;
-
import androidx.test.jank.JankTestBase;
+import androidx.test.uiautomator.UiDevice;
/**
* This adds additional system level jank monitor and its result is merged with primary monitor
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
index d51a4ddd43e8..5550eab61a33 100644
--- a/tests/WindowInsetsTests/res/layout/controller_activity.xml
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -88,7 +88,7 @@
<TextView
android:id="@+id/textViewControllableInsets"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp" />
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index d6355f5a0464..516d4584426e 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -22,7 +22,7 @@
<!-- The item positions should match the flag values respectively. -->
<string-array name="behaviors">
- <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH (deprecated)</item>
<item>BEHAVIOR_DEFAULT</item>
<item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
</string-array>
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
index 95fd959e5587..167d560633ab 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -83,7 +83,12 @@ public class ControllerActivity extends Activity implements View.OnApplyWindowIn
final View contentView = findViewById(R.id.content);
contentView.setOnApplyWindowInsetsListener(this);
contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
- (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ (c, types) -> mTextControllableInsets.setText(
+ "ControllableInsetsTypes:\n" + insetsTypesToString(types)));
+ }
+
+ private static String insetsTypesToString(int types) {
+ return types == 0 ? "none" : WindowInsets.Type.toString(types);
}
@Override
diff --git a/tests/backup/Android.bp b/tests/backup/Android.bp
new file mode 100644
index 000000000000..3890a1f58078
--- /dev/null
+++ b/tests/backup/Android.bp
@@ -0,0 +1,54 @@
+// 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.
+
+// native test
+// ========================================
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: [
+ "frameworks_base_license",
+ ],
+}
+
+cc_binary {
+ name: "backup_helper_test",
+
+ srcs: ["backup_helper_test.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libandroidfw",
+ "libutils",
+ ],
+
+}
+
+// java test
+// ========================================
+android_app {
+ name: "BackupTest",
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+
+ optimize: {
+ enabled: false,
+ },
+
+}
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
deleted file mode 100644
index b6f34717658c..000000000000
--- a/tests/backup/Android.mk
+++ /dev/null
@@ -1,53 +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.
-
-LOCAL_PATH := $(call my-dir)
-
-# native test
-# ========================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- backup_helper_test.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := backup_helper_test
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SHARED_LIBRARIES := libandroidfw libutils
-
-include $(BUILD_EXECUTABLE)
-
-# java test
-# ========================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := BackupTest
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
diff --git a/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
index 388548691b77..0a03e8d6534a 100644
--- a/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
+++ b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
@@ -1,454 +1,448 @@
-/*
- * 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 com.android.internal;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import android.app.Activity;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.test.filters.LargeTest;
-
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.util.function.pooled.PooledPredicate;
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.Statement;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/** Compares the performance of regular lambda and pooled lambda. */
-@LargeTest
-public class LambdaPerfTest {
- private static final boolean DEBUG = false;
- private static final String TAG = LambdaPerfTest.class.getSimpleName();
-
- private static final String LAMBDA_FORM_REGULAR = "regular";
- private static final String LAMBDA_FORM_POOLED = "pooled";
-
- private static final int WARMUP_ITERATIONS = 1000;
- private static final int TEST_ITERATIONS = 3000000;
- private static final int TASK_COUNT = 10;
- private static final long DELAY_AFTER_BENCH_MS = 1000;
-
- private String mMethodName;
-
- private final Bundle mTestResults = new Bundle();
- private final ArrayList<Task> mTasks = new ArrayList<>();
-
- // The member fields are used to ensure lambda capturing. They don't have the actual meaning.
- private final Task mTask = new Task();
- private final Rect mBounds = new Rect();
- private int mTaskId;
- private long mTime;
- private boolean mTop;
-
- @Rule
- public final TestRule mRule = (base, description) -> new Statement() {
- @Override
- public void evaluate() throws Throwable {
- mMethodName = description.getMethodName();
- mTasks.clear();
- for (int i = 0; i < TASK_COUNT; i++) {
- final Task t = new Task();
- mTasks.add(t);
- }
- base.evaluate();
-
- getInstrumentation().sendStatus(Activity.RESULT_OK, mTestResults);
- }
- };
-
- @Test
- public void test1ParamConsumer() {
- evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTask)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTask);
- forAllTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void test2PrimitiveParamsConsumer() {
- // Not in Integer#IntegerCache (-128~127) for autoboxing, that will create new object.
- mTaskId = 12345;
- mTime = 54321;
-
- evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTaskId, mTime)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTaskId, mTime);
- forAllTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void test3ParamsPredicate() {
- mTop = true;
- // In Integer#IntegerCache.
- mTaskId = 10;
-
- evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mBounds, mTop, mTaskId)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
- PooledLambda.__(Task.class), mBounds, mTop, mTaskId);
- handleTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void testMessage() {
- evaluate(LAMBDA_FORM_REGULAR, () -> {
- final Message m = Message.obtain().setCallback(() -> mTask.doSomething(mTaskId, mTime));
- m.getCallback().run();
- m.recycle();
- });
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final Message m = PooledLambda.obtainMessage(Task::doSomething, mTask, mTaskId, mTime);
- m.getCallback().run();
- m.recycle();
- });
- }
-
- @Test
- public void testRunnable() {
- evaluate(LAMBDA_FORM_REGULAR, () -> {
- final Runnable r = mTask::doSomething;
- r.run();
- });
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final Runnable r = PooledLambda.obtainRunnable(Task::doSomething, mTask).recycleOnUse();
- r.run();
- });
- }
-
- @Test
- public void testMultiThread() {
- final int numThread = 3;
-
- final Runnable regularAction = () -> forAllTask(t -> t.doSomething(mTask));
- final Runnable[] regularActions = new Runnable[numThread];
- Arrays.fill(regularActions, regularAction);
- evaluateMultiThread(LAMBDA_FORM_REGULAR, regularActions);
-
- final Runnable pooledAction = () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTask);
- forAllTask(c);
- c.recycle();
- };
- final Runnable[] pooledActions = new Runnable[numThread];
- Arrays.fill(pooledActions, pooledAction);
- evaluateMultiThread(LAMBDA_FORM_POOLED, pooledActions);
- }
-
- private void forAllTask(Consumer<Task> callback) {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- callback.accept(mTasks.get(i));
- }
- }
-
- private void handleTask(Predicate<Task> callback) {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- final Task task = mTasks.get(i);
- if (callback.test(task)) {
- return;
- }
- }
- }
-
- private void evaluate(String title, Runnable action) {
- for (int i = 0; i < WARMUP_ITERATIONS; i++) {
- action.run();
- }
- performGc();
-
- final GcStatus startGcStatus = getGcStatus();
- final long startTime = SystemClock.elapsedRealtime();
- for (int i = 0; i < TEST_ITERATIONS; i++) {
- action.run();
- }
- evaluateResult(title, startGcStatus, startTime);
- }
-
- private void evaluateMultiThread(String title, Runnable[] actions) {
- performGc();
-
- final CountDownLatch latch = new CountDownLatch(actions.length);
- final GcStatus startGcStatus = getGcStatus();
- final long startTime = SystemClock.elapsedRealtime();
- for (Runnable action : actions) {
- new Thread() {
- @Override
- public void run() {
- for (int i = 0; i < TEST_ITERATIONS; i++) {
- action.run();
- }
- latch.countDown();
- };
- }.start();
- }
- try {
- latch.await();
- } catch (InterruptedException ignored) {
- }
- evaluateResult(title, startGcStatus, startTime);
- }
-
- private void evaluateResult(String title, GcStatus startStatus, long startTime) {
- final float elapsed = SystemClock.elapsedRealtime() - startTime;
- // Sleep a while to see if GC may happen.
- SystemClock.sleep(DELAY_AFTER_BENCH_MS);
- final GcStatus endStatus = getGcStatus();
- final GcInfo info = startStatus.calculateGcTime(endStatus, title, mTestResults);
- Log.i(TAG, mMethodName + "_" + title + " execution time: "
- + elapsed + "ms (avg=" + String.format("%.5f", elapsed / TEST_ITERATIONS) + "ms)"
- + " GC time: " + String.format("%.3f", info.mTotalGcTime) + "ms"
- + " GC paused time: " + String.format("%.3f", info.mTotalGcPausedTime) + "ms");
- }
-
- /** Cleans the test environment. */
- private static void performGc() {
- System.gc();
- System.runFinalization();
- System.gc();
- }
-
- private static GcStatus getGcStatus() {
- if (DEBUG) {
- Log.i(TAG, "===== Read GC dump =====");
- }
- final GcStatus status = new GcStatus();
- final List<String> vmDump = getVmDump();
- Assume.assumeFalse("VM dump is empty", vmDump.isEmpty());
- for (String line : vmDump) {
- status.visit(line);
- if (line.startsWith("DALVIK THREADS")) {
- break;
- }
- }
- return status;
- }
-
- private static List<String> getVmDump() {
- final int myPid = Process.myPid();
- // Another approach Debug#dumpJavaBacktraceToFileTimeout requires setenforce 0.
- Process.sendSignal(myPid, Process.SIGNAL_QUIT);
- // Give a chance to handle the signal.
- SystemClock.sleep(100);
-
- String dump = null;
- final String pattern = myPid + " written to: ";
- final List<String> logs = shell("logcat -v brief -d tombstoned:I *:S");
- for (int i = logs.size() - 1; i >= 0; i--) {
- final String log = logs.get(i);
- // Log pattern: Traces for pid 9717 written to: /data/anr/trace_07
- final int pos = log.indexOf(pattern);
- if (pos > 0) {
- dump = log.substring(pattern.length() + pos);
- break;
- }
- }
-
- Assume.assumeNotNull("Unable to find VM dump", dump);
- // It requires system or root uid to read the trace.
- return shell("cat " + dump);
- }
-
- private static List<String> shell(String command) {
- final ParcelFileDescriptor.AutoCloseInputStream stream =
- new ParcelFileDescriptor.AutoCloseInputStream(
- getInstrumentation().getUiAutomation().executeShellCommand(command));
- final ArrayList<String> lines = new ArrayList<>();
- try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
- String line;
- while ((line = br.readLine()) != null) {
- lines.add(line);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return lines;
- }
-
- /** An empty class which provides some methods with different type arguments. */
- static class Task {
- void doSomething() {
- }
-
- void doSomething(Task t) {
- }
-
- void doSomething(int taskId, long time) {
- }
-
- boolean doSomething(Rect bounds, boolean top, int taskId) {
- return false;
- }
- }
-
- static class ValPattern {
- static final int TYPE_COUNT = 0;
- static final int TYPE_TIME = 1;
- static final String PATTERN_COUNT = "(\\d+)";
- static final String PATTERN_TIME = "(\\d+\\.?\\d+)(\\w+)";
- final String mRawPattern;
- final Pattern mPattern;
- final int mType;
-
- int mIntValue;
- float mFloatValue;
-
- ValPattern(String p, int type) {
- mRawPattern = p;
- mPattern = Pattern.compile(
- p + (type == TYPE_TIME ? PATTERN_TIME : PATTERN_COUNT) + ".*");
- mType = type;
- }
-
- boolean visit(String line) {
- final Matcher matcher = mPattern.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- final String value = matcher.group(1);
- if (value == null) {
- return false;
- }
- if (mType == TYPE_COUNT) {
- mIntValue = Integer.parseInt(value);
- return true;
- }
- final float time = Float.parseFloat(value);
- final String unit = matcher.group(2);
- if (unit == null) {
- return false;
- }
- // Refer to art/libartbase/base/time_utils.cc
- switch (unit) {
- case "s":
- mFloatValue = time * 1000;
- break;
- case "ms":
- mFloatValue = time;
- break;
- case "us":
- mFloatValue = time / 1000;
- break;
- case "ns":
- mFloatValue = time / 1000 / 1000;
- break;
- default:
- throw new IllegalArgumentException();
- }
-
- return true;
- }
-
- @Override
- public String toString() {
- return mRawPattern + (mType == TYPE_TIME ? (mFloatValue + "ms") : mIntValue);
- }
- }
-
- /** Parses the dump pattern of Heap::DumpGcPerformanceInfo. */
- private static class GcStatus {
- private static final int TOTAL_GC_TIME_INDEX = 1;
- private static final int TOTAL_GC_PAUSED_TIME_INDEX = 5;
-
- // Refer to art/runtime/gc/heap.cc
- final ValPattern[] mPatterns = {
- new ValPattern("Total GC count: ", ValPattern.TYPE_COUNT),
- new ValPattern("Total GC time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total time waiting for GC to complete: ", ValPattern.TYPE_TIME),
- new ValPattern("Total blocking GC count: ", ValPattern.TYPE_COUNT),
- new ValPattern("Total blocking GC time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total mutator paused time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total number of allocations ", ValPattern.TYPE_COUNT),
- new ValPattern("concurrent copying paused: Sum: ", ValPattern.TYPE_TIME),
- new ValPattern("concurrent copying total time: ", ValPattern.TYPE_TIME),
- new ValPattern("concurrent copying freed: ", ValPattern.TYPE_COUNT),
- new ValPattern("Peak regions allocated ", ValPattern.TYPE_COUNT),
- };
-
- void visit(String dumpLine) {
- for (ValPattern p : mPatterns) {
- if (p.visit(dumpLine)) {
- if (DEBUG) {
- Log.i(TAG, " " + p);
- }
- }
- }
- }
-
- GcInfo calculateGcTime(GcStatus newStatus, String title, Bundle result) {
- Log.i(TAG, "===== GC status of " + title + " =====");
- final GcInfo info = new GcInfo();
- for (int i = 0; i < mPatterns.length; i++) {
- final ValPattern p = mPatterns[i];
- if (p.mType == ValPattern.TYPE_COUNT) {
- final int diff = newStatus.mPatterns[i].mIntValue - p.mIntValue;
- Log.i(TAG, " " + p.mRawPattern + diff);
- if (diff > 0) {
- result.putInt("[" + title + "] " + p.mRawPattern, diff);
- }
- continue;
- }
- final float diff = newStatus.mPatterns[i].mFloatValue - p.mFloatValue;
- Log.i(TAG, " " + p.mRawPattern + diff + "ms");
- if (diff > 0) {
- result.putFloat("[" + title + "] " + p.mRawPattern + "(ms)", diff);
- }
- if (i == TOTAL_GC_TIME_INDEX) {
- info.mTotalGcTime = diff;
- } else if (i == TOTAL_GC_PAUSED_TIME_INDEX) {
- info.mTotalGcPausedTime = diff;
- }
- }
- return info;
- }
- }
-
- private static class GcInfo {
- float mTotalGcTime;
- float mTotalGcPausedTime;
- }
-}
+/*
+ * 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 com.android.internal;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Compares the performance of regular lambda and pooled lambda. */
+@LargeTest
+public class LambdaPerfTest {
+ private static final boolean DEBUG = false;
+ private static final String TAG = LambdaPerfTest.class.getSimpleName();
+
+ private static final String LAMBDA_FORM_REGULAR = "regular";
+ private static final String LAMBDA_FORM_POOLED = "pooled";
+
+ private static final int WARMUP_ITERATIONS = 1000;
+ private static final int TEST_ITERATIONS = 3000000;
+ private static final int TASK_COUNT = 10;
+ private static final long DELAY_AFTER_BENCH_MS = 1000;
+
+ private String mMethodName;
+
+ private final Bundle mTestResults = new Bundle();
+ private final ArrayList<Task> mTasks = new ArrayList<>();
+
+ // The member fields are used to ensure lambda capturing. They don't have the actual meaning.
+ private final Task mTask = new Task();
+ private final Rect mBounds = new Rect();
+ private int mTaskId;
+ private long mTime;
+ private boolean mTop;
+
+ @Rule
+ public final TestRule mRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mMethodName = description.getMethodName();
+ mTasks.clear();
+ for (int i = 0; i < TASK_COUNT; i++) {
+ final Task t = new Task();
+ mTasks.add(t);
+ }
+ base.evaluate();
+
+ getInstrumentation().sendStatus(Activity.RESULT_OK, mTestResults);
+ }
+ };
+
+ @Test
+ public void test1ParamPredicate() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mTaskId, mTime)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mTaskId, mTime);
+ handleTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test2PrimitiveParamsPredicate() {
+ // Not in Integer#IntegerCache (-128~127) for autoboxing, that may create new object.
+ mTaskId = 12345;
+ mTime = 54321;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mTaskId, mTime)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mTaskId, mTime);
+ handleTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test3ParamsPredicate() {
+ mTop = true;
+ // In Integer#IntegerCache.
+ mTaskId = 10;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mBounds, mTop, mTaskId)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mBounds, mTop, mTaskId);
+ handleTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void testMessage() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Message m = Message.obtain().setCallback(() -> mTask.doSomething(mTaskId, mTime));
+ m.getCallback().run();
+ m.recycle();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Message m = PooledLambda.obtainMessage(Task::doSomething, mTask, mTaskId, mTime);
+ m.getCallback().run();
+ m.recycle();
+ });
+ }
+
+ @Test
+ public void testRunnable() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Runnable r = mTask::doSomething;
+ r.run();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Runnable r = PooledLambda.obtainRunnable(Task::doSomething, mTask).recycleOnUse();
+ r.run();
+ });
+ }
+
+ @Test
+ public void testMultiThread() {
+ final int numThread = 3;
+
+ final Runnable regularAction = () -> handleTask(t -> t.doSomething(mTaskId, mTime));
+ final Runnable[] regularActions = new Runnable[numThread];
+ Arrays.fill(regularActions, regularAction);
+ evaluateMultiThread(LAMBDA_FORM_REGULAR, regularActions);
+
+ final Runnable pooledAction = () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mTaskId, mTime);
+ handleTask(c);
+ c.recycle();
+ };
+ final Runnable[] pooledActions = new Runnable[numThread];
+ Arrays.fill(pooledActions, pooledAction);
+ evaluateMultiThread(LAMBDA_FORM_POOLED, pooledActions);
+ }
+
+ private void handleTask(Predicate<Task> callback) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ final Task task = mTasks.get(i);
+ if (callback.test(task)) {
+ return;
+ }
+ }
+ }
+
+ private void evaluate(String title, Runnable action) {
+ for (int i = 0; i < WARMUP_ITERATIONS; i++) {
+ action.run();
+ }
+ performGc();
+
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateMultiThread(String title, Runnable[] actions) {
+ performGc();
+
+ final CountDownLatch latch = new CountDownLatch(actions.length);
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (Runnable action : actions) {
+ new Thread() {
+ @Override
+ public void run() {
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ latch.countDown();
+ };
+ }.start();
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateResult(String title, GcStatus startStatus, long startTime) {
+ final float elapsed = SystemClock.elapsedRealtime() - startTime;
+ // Sleep a while to see if GC may happen.
+ SystemClock.sleep(DELAY_AFTER_BENCH_MS);
+ final GcStatus endStatus = getGcStatus();
+ final GcInfo info = startStatus.calculateGcTime(endStatus, title, mTestResults);
+ mTestResults.putFloat("[" + title + "-execution-time]", elapsed);
+ Log.i(TAG, mMethodName + "_" + title + " execution time: "
+ + elapsed + "ms (avg=" + String.format("%.5f", elapsed / TEST_ITERATIONS) + "ms)"
+ + " GC time: " + String.format("%.3f", info.mTotalGcTime) + "ms"
+ + " GC paused time: " + String.format("%.3f", info.mTotalGcPausedTime) + "ms");
+ }
+
+ /** Cleans the test environment. */
+ private static void performGc() {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ private static GcStatus getGcStatus() {
+ if (DEBUG) {
+ Log.i(TAG, "===== Read GC dump =====");
+ }
+ final GcStatus status = new GcStatus();
+ final List<String> vmDump = getVmDump();
+ Assume.assumeFalse("VM dump is empty", vmDump.isEmpty());
+ for (String line : vmDump) {
+ status.visit(line);
+ if (line.startsWith("DALVIK THREADS")) {
+ break;
+ }
+ }
+ return status;
+ }
+
+ private static List<String> getVmDump() {
+ final int myPid = Process.myPid();
+ // Another approach Debug#dumpJavaBacktraceToFileTimeout requires setenforce 0.
+ Process.sendSignal(myPid, Process.SIGNAL_QUIT);
+ // Give a chance to handle the signal.
+ SystemClock.sleep(100);
+
+ String dump = null;
+ final String pattern = myPid + " written to: ";
+ final List<String> logs = shell("logcat -v brief -d tombstoned:I *:S");
+ for (int i = logs.size() - 1; i >= 0; i--) {
+ final String log = logs.get(i);
+ // Log pattern: Traces for pid 9717 written to: /data/anr/trace_07
+ final int pos = log.indexOf(pattern);
+ if (pos > 0) {
+ dump = log.substring(pattern.length() + pos);
+ if (!dump.startsWith("/data/anr/")) {
+ dump = "/data/anr/" + dump;
+ }
+ break;
+ }
+ }
+
+ Assume.assumeNotNull("Unable to find VM dump", dump);
+ // It requires system or root uid to read the trace.
+ return shell("cat " + dump);
+ }
+
+ private static List<String> shell(String command) {
+ final ParcelFileDescriptor.AutoCloseInputStream stream =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ getInstrumentation().getUiAutomation().executeShellCommand(command));
+ final ArrayList<String> lines = new ArrayList<>();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return lines;
+ }
+
+ /** An empty class which provides some methods with different type arguments. */
+ static class Task {
+ void doSomething() {
+ }
+
+ boolean doSomething(int taskId, long time) {
+ return false;
+ }
+
+ boolean doSomething(Rect bounds, boolean top, int taskId) {
+ return false;
+ }
+ }
+
+ static class ValPattern {
+ static final int TYPE_COUNT = 0;
+ static final int TYPE_TIME = 1;
+ static final String PATTERN_COUNT = "(\\d+)";
+ static final String PATTERN_TIME = "(\\d+\\.?\\d+)(\\w+)";
+ final String mRawPattern;
+ final Pattern mPattern;
+ final int mType;
+
+ int mIntValue;
+ float mFloatValue;
+
+ ValPattern(String p, int type) {
+ mRawPattern = p;
+ mPattern = Pattern.compile(
+ p + (type == TYPE_TIME ? PATTERN_TIME : PATTERN_COUNT) + ".*");
+ mType = type;
+ }
+
+ boolean visit(String line) {
+ final Matcher matcher = mPattern.matcher(line);
+ if (!matcher.matches()) {
+ return false;
+ }
+ final String value = matcher.group(1);
+ if (value == null) {
+ return false;
+ }
+ if (mType == TYPE_COUNT) {
+ mIntValue = Integer.parseInt(value);
+ return true;
+ }
+ final float time = Float.parseFloat(value);
+ final String unit = matcher.group(2);
+ if (unit == null) {
+ return false;
+ }
+ // Refer to art/libartbase/base/time_utils.cc
+ switch (unit) {
+ case "s":
+ mFloatValue = time * 1000;
+ break;
+ case "ms":
+ mFloatValue = time;
+ break;
+ case "us":
+ mFloatValue = time / 1000;
+ break;
+ case "ns":
+ mFloatValue = time / 1000 / 1000;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return mRawPattern + (mType == TYPE_TIME ? (mFloatValue + "ms") : mIntValue);
+ }
+ }
+
+ /** Parses the dump pattern of Heap::DumpGcPerformanceInfo. */
+ private static class GcStatus {
+ private static final int TOTAL_GC_TIME_INDEX = 1;
+ private static final int TOTAL_GC_PAUSED_TIME_INDEX = 5;
+
+ // Refer to art/runtime/gc/heap.cc
+ final ValPattern[] mPatterns = {
+ new ValPattern("Total GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total time waiting for GC to complete: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total blocking GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total blocking GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total mutator paused time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total number of allocations ", ValPattern.TYPE_COUNT),
+ new ValPattern("concurrent copying paused: Sum: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying total time: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying freed: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Peak regions allocated ", ValPattern.TYPE_COUNT),
+ };
+
+ void visit(String dumpLine) {
+ for (ValPattern p : mPatterns) {
+ if (p.visit(dumpLine)) {
+ if (DEBUG) {
+ Log.i(TAG, " " + p);
+ }
+ }
+ }
+ }
+
+ GcInfo calculateGcTime(GcStatus newStatus, String title, Bundle result) {
+ Log.i(TAG, "===== GC status of " + title + " =====");
+ final GcInfo info = new GcInfo();
+ for (int i = 0; i < mPatterns.length; i++) {
+ final ValPattern p = mPatterns[i];
+ if (p.mType == ValPattern.TYPE_COUNT) {
+ final int diff = newStatus.mPatterns[i].mIntValue - p.mIntValue;
+ Log.i(TAG, " " + p.mRawPattern + diff);
+ if (diff > 0) {
+ result.putInt("[" + title + "] " + p.mRawPattern, diff);
+ }
+ continue;
+ }
+ final float diff = newStatus.mPatterns[i].mFloatValue - p.mFloatValue;
+ Log.i(TAG, " " + p.mRawPattern + diff + "ms");
+ if (diff > 0) {
+ result.putFloat("[" + title + "] " + p.mRawPattern + "(ms)", diff);
+ }
+ if (i == TOTAL_GC_TIME_INDEX) {
+ info.mTotalGcTime = diff;
+ } else if (i == TOTAL_GC_PAUSED_TIME_INDEX) {
+ info.mTotalGcPausedTime = diff;
+ }
+ }
+ return info;
+ }
+ }
+
+ private static class GcInfo {
+ float mTotalGcTime;
+ float mTotalGcPausedTime;
+ }
+}
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
index e5eb3c7b6394..7af76e1144f8 100644
--- a/tests/componentalias/Android.bp
+++ b/tests/componentalias/Android.bp
@@ -16,6 +16,9 @@ package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// TODO: Delete this file. It's no longer needed, but removing it on udc-dev will cause
+// a conflict on master.
+
java_defaults {
name: "ComponentAliasTests_defaults",
static_libs: [
@@ -34,54 +37,3 @@ java_defaults {
],
platform_apis: true, // We use hidden APIs in the test.
}
-
-// We build three APKs from the exact same source files, so these APKs contain the exact same tests.
-// And we run the tests on each APK, so that we can test various situations:
-// - When the alias is in the same package, target in the same package.
-// - When the alias is in the same package, target in another package.
-// - When the alias is in another package, which also contains the target.
-// - When the alias is in another package, and the target is in yet another package.
-// etc etc...
-
-android_test {
- name: "ComponentAliasTests",
- defaults: [
- "ComponentAliasTests_defaults",
- ],
- package_name: "android.content.componentalias.tests",
- manifest: "AndroidManifest.xml",
- additional_manifests: [
- "AndroidManifest_main.xml",
- "AndroidManifest_service_aliases.xml",
- "AndroidManifest_service_targets.xml",
- ],
- test_config_template: "AndroidTest-template.xml",
-}
-
-android_test {
- name: "ComponentAliasTests1",
- defaults: [
- "ComponentAliasTests_defaults",
- ],
- package_name: "android.content.componentalias.tests.sub1",
- manifest: "AndroidManifest.xml",
- additional_manifests: [
- "AndroidManifest_sub1.xml",
- "AndroidManifest_service_targets.xml",
- ],
- test_config_template: "AndroidTest-template.xml",
-}
-
-android_test {
- name: "ComponentAliasTests2",
- defaults: [
- "ComponentAliasTests_defaults",
- ],
- package_name: "android.content.componentalias.tests.sub2",
- manifest: "AndroidManifest.xml",
- additional_manifests: [
- "AndroidManifest_sub2.xml",
- "AndroidManifest_service_targets.xml",
- ],
- test_config_template: "AndroidTest-template.xml",
-}
diff --git a/tests/componentalias/AndroidManifest_main.xml b/tests/componentalias/AndroidManifest_main.xml
deleted file mode 100755
index 70e817ebf3e7..000000000000
--- a/tests/componentalias/AndroidManifest_main.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
-
- <application>
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.content.componentalias.tests" >
- </instrumentation>
-</manifest>
diff --git a/tests/componentalias/AndroidManifest_service_aliases.xml b/tests/componentalias/AndroidManifest_service_aliases.xml
deleted file mode 100644
index c96f1736c684..000000000000
--- a/tests/componentalias/AndroidManifest_service_aliases.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
- <application>
- <!--
- Note the alias components are essentially just placeholders, so the APKs don't have to
- have the implementation classes.
- -->
- <service android:name=".s.Alias00" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests/android.content.componentalias.tests.s.Target00" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_00" /></intent-filter>
- </service>
- <service android:name=".s.Alias01" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.s.Target01" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_01" /></intent-filter>
- </service>
- <service android:name=".s.Alias02" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.s.Target02" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_02" /></intent-filter>
- </service>
- <service android:name=".s.Alias03" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.s.Target03" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_03" /></intent-filter>
- </service>
- <service android:name=".s.Alias04" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.s.Target04" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_04" /></intent-filter>
- </service>
-
- <receiver android:name=".b.Alias00" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests/android.content.componentalias.tests.b.Target00" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_00" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Alias01" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target01" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_01" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Alias02" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target02" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_02" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Alias03" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target03" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_03" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Alias04" android:exported="true" android:enabled="true" >
- <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target04" />
- <intent-filter><action android:name="com.android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_04" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- </application>
-</manifest>
diff --git a/tests/componentalias/AndroidManifest_service_targets.xml b/tests/componentalias/AndroidManifest_service_targets.xml
deleted file mode 100644
index 24c0432bcf4c..000000000000
--- a/tests/componentalias/AndroidManifest_service_targets.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
- <application>
- <service android:name=".s.Target00" android:exported="true" android:enabled="true" >
- </service>
- <service android:name=".s.Target01" android:exported="true" android:enabled="true" >
- </service>
- <service android:name=".s.Target02" android:exported="true" android:enabled="true" >
- </service>
- <service android:name=".s.Target03" android:exported="true" android:enabled="true" >
- </service>
- <service android:name=".s.Target04" android:exported="true" android:enabled="true" >
- </service>
-
- <!--
- Due to http://go/intents-match-intent-filters-guide, the target intent has to have
- an intent filter that matches the original intent. (modulo the package name)
- This restriction shouldn't exist in the final version.
- -->
- <receiver android:name=".b.Target00" android:exported="true" android:enabled="true" >
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_00" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Target01" android:exported="true" android:enabled="true" >
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_01" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Target02" android:exported="true" android:enabled="true" >
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_02" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Target03" android:exported="true" android:enabled="true" >
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_03" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- <receiver android:name=".b.Target04" android:exported="true" android:enabled="true" >
- <intent-filter><action android:name="android.content.componentalias.tests.IS_RECEIVER_04" /></intent-filter>
- <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter>
- </receiver>
- </application>
-</manifest>
diff --git a/tests/componentalias/AndroidManifest_sub1.xml b/tests/componentalias/AndroidManifest_sub1.xml
deleted file mode 100755
index 21616f5edf00..000000000000
--- a/tests/componentalias/AndroidManifest_sub1.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
-
- <application>
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.content.componentalias.tests.sub1" >
- </instrumentation>
-</manifest>
diff --git a/tests/componentalias/AndroidManifest_sub2.xml b/tests/componentalias/AndroidManifest_sub2.xml
deleted file mode 100755
index c11b0cd55ef4..000000000000
--- a/tests/componentalias/AndroidManifest_sub2.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.content.componentalias.tests" >
-
- <application>
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.content.componentalias.tests.sub2" >
- </instrumentation>
-</manifest>
diff --git a/tests/componentalias/AndroidTest-template.xml b/tests/componentalias/AndroidTest-template.xml
deleted file mode 100644
index afdfe79ea4a4..000000000000
--- a/tests/componentalias/AndroidTest-template.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration>
- <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
- <option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="ComponentAliasTests.apk" />
- <option name="test-file-name" value="ComponentAliasTests1.apk" />
- <option name="test-file-name" value="ComponentAliasTests2.apk" />
- </target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <!-- Exempt the helper APKs from the BG restriction, so they can start BG services. -->
- <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests" />
- <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.sub1" />
- <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.sub2" />
-
- <option name="teardown-command" value="cmd deviceidle whitelist -android.content.componentalias.tests" />
- <option name="teardown-command" value="cmd deviceidle whitelist -android.content.componentalias.tests.sub1" />
- <option name="teardown-command" value="cmd deviceidle whitelist -android.content.componentalias.tests.sub2" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="{PACKAGE}" />
- <option name="runtime-hint" value="2m" />
- <option name="isolated-storage" value="false" />
- </test>
-</configuration>
diff --git a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
deleted file mode 100644
index 99322ee46106..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Build;
-import android.provider.DeviceConfig;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.compatibility.common.util.DeviceConfigStateHelper;
-import com.android.compatibility.common.util.ShellUtils;
-import com.android.compatibility.common.util.TestUtils;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Before;
-
-import java.util.function.Consumer;
-
-public class BaseComponentAliasTest {
- protected static final Context sContext = InstrumentationRegistry.getTargetContext();
-
- protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_COMPONENT_ALIAS);
- @Before
- public void enableComponentAliasWithCompatFlag() throws Exception {
- Assume.assumeTrue(Build.isDebuggable());
- ShellUtils.runShellCommand(
- "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
- sDeviceConfig.set("enable_experimental_component_alias", "");
- sDeviceConfig.set("component_alias_overrides", "");
-
- // Make sure the feature is actually enabled, and the aliases are loaded.
- TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
- String out = ShellUtils.runShellCommand("dumpsys activity component-alias");
-
- return out.contains("Enabled: true")
- && out.contains("android.content.componentalias.tests/.b.Alias04")
- && out.contains("android.content.componentalias.tests/.s.Alias04");
- });
- ShellUtils.runShellCommand("am wait-for-broadcast-idle");
- }
-
- @AfterClass
- public static void restoreDeviceConfig() throws Exception {
- ShellUtils.runShellCommand(
- "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
- sDeviceConfig.close();
- }
-
- protected static void log(String message) {
- Log.i(ComponentAliasTestCommon.TAG, "[" + sContext.getPackageName() + "] " + message);
- }
-
- /**
- * Defines a test target.
- */
- public static class Combo {
- public final ComponentName alias;
- public final ComponentName target;
- public final String action;
-
- public Combo(ComponentName alias, ComponentName target, String action) {
- this.alias = alias;
- this.target = target;
- this.action = action;
- }
-
- @Override
- public String toString() {
- return "Combo{"
- + "alias=" + toString(alias)
- + ", target=" + toString(target)
- + ", action='" + action + '\''
- + '}';
- }
-
- private static String toString(ComponentName cn) {
- return cn == null ? "[null]" : cn.flattenToShortString();
- }
-
- public void apply(Consumer<Combo> callback) {
- log("Testing for: " + this);
- callback.accept(this);
- }
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
deleted file mode 100644
index 7d5e0b9c6d8a..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import static android.content.componentalias.tests.ComponentAliasTestCommon.MAIN_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.SUB1_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.SUB2_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.TAG;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.ComponentName;
-import android.content.Intent;
-
-import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
-
-import org.junit.Test;
-
-import java.util.function.Consumer;
-
-public class ComponentAliasBroadcastTest extends BaseComponentAliasTest {
- private void forEachCombo(Consumer<Combo> callback) {
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".b.Alias00"),
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".b.Target00"),
- MAIN_PACKAGE + ".IS_RECEIVER_00").apply(callback);
-
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".b.Alias01"),
- new ComponentName(SUB1_PACKAGE, MAIN_PACKAGE + ".b.Target01"),
- MAIN_PACKAGE + ".IS_RECEIVER_01").apply(callback);
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".b.Alias02"),
- new ComponentName(SUB2_PACKAGE, MAIN_PACKAGE + ".b.Target02"),
- MAIN_PACKAGE + ".IS_RECEIVER_02").apply(callback);
- }
-
- @Test
- public void testBroadcast_explicitComponentName() {
- forEachCombo((c) -> {
- Intent i = new Intent().setComponent(c.alias);
- i.setAction("ACTION_BROADCAST");
- ComponentAliasMessage m;
-
- try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext, TAG)) {
- log("Sending: " + i);
- sContext.sendBroadcast(i);
-
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onReceive");
- assertThat(m.getSenderIdentity()).isEqualTo(c.target.flattenToShortString());
-
- // The broadcast intent will always have the receiving component name set.
- assertThat(m.getIntent().getComponent()).isEqualTo(c.target);
-
- receiver.ensureNoMoreMessages();
- }
- });
- }
-
- @Test
- public void testBroadcast_explicitPackageName() {
- forEachCombo((c) -> {
- // In this test, we only set the package name to the intent.
- // If the alias and target are the same package, the intent will be sent to both of them
- // *and* the one to the alias is redirected to the target, so the target will receive
- // the intent twice. This case is haled at *1 below.
-
-
- Intent i = new Intent().setPackage(c.alias.getPackageName());
- i.setAction(c.action);
- ComponentAliasMessage m;
-
- try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext, TAG)) {
- log("Sending broadcast: " + i);
- sContext.sendBroadcast(i);
-
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onReceive");
- assertThat(m.getSenderIdentity()).isEqualTo(c.target.flattenToShortString());
- assertThat(m.getIntent().getComponent()).isEqualTo(c.target);
-
- // *1 -- if the alias and target are in the same package, we expect one more
- // message.
- if (c.alias.getPackageName().equals(c.target.getPackageName())) {
- m = receiver.waitForNextMessage();
- assertThat(m.getMethodName()).isEqualTo("onReceive");
- assertThat(m.getSenderIdentity()).isEqualTo(c.target.flattenToShortString());
- assertThat(m.getIntent().getComponent()).isEqualTo(c.target);
- }
- receiver.ensureNoMoreMessages();
- }
- });
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java
deleted file mode 100644
index ee20379d971a..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import android.os.Build;
-import android.provider.DeviceConfig;
-
-import com.android.compatibility.common.util.DeviceConfigStateHelper;
-import com.android.compatibility.common.util.ShellUtils;
-import com.android.compatibility.common.util.TestUtils;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Test;
-
-public class ComponentAliasEnableWithDeviceConfigTest {
- protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_COMPONENT_ALIAS);
-
- @AfterClass
- public static void restoreDeviceConfig() throws Exception {
- sDeviceConfig.close();
- }
-
- @Test
- public void enableComponentAliasWithCompatFlag() throws Exception {
- Assume.assumeTrue(Build.isDebuggable());
-
- sDeviceConfig.set("component_alias_overrides", "");
-
- // First, disable with both compat-id and device config.
- ShellUtils.runShellCommand(
- "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
- sDeviceConfig.set("enable_experimental_component_alias", "");
-
- TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
- return ShellUtils.runShellCommand("dumpsys activity component-alias")
- .indexOf("Enabled: false") > 0;
- });
-
- // Then, enable by device config.
- sDeviceConfig.set("enable_experimental_component_alias", "true");
-
- // Make sure the feature is actually enabled.
- TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
- return ShellUtils.runShellCommand("dumpsys activity component-alias")
- .indexOf("Enabled: true") > 0;
- });
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java
deleted file mode 100644
index d41696f27880..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
-/**
- * Parcelabe containing a "message" that's meant to be delivered via BroadcastMessenger.
- *
- * To add a new field, just add a private member field, and run:
- * codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java
- */
-@DataClass(
- genConstructor = false,
- genSetters = true,
- genToString = true,
- genAidl = false)
-public final class ComponentAliasMessage implements Parcelable {
- public ComponentAliasMessage() {
- }
-
- @Nullable
- private String mMessage;
-
- @Nullable
- private String mMethodName;
-
- @Nullable
- private String mSenderIdentity;
-
- @Nullable
- private Intent mIntent;
-
- @Nullable
- private ComponentName mComponent;
-
-
-
- // Code below generated by codegen v1.0.23.
- //
- // DO NOT MODIFY!
- // CHECKSTYLE:OFF Generated code
- //
- // To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java
- //
- // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
- // Settings > Editor > Code Style > Formatter Control
- //@formatter:off
-
-
- @DataClass.Generated.Member
- public @Nullable String getMessage() {
- return mMessage;
- }
-
- @DataClass.Generated.Member
- public @Nullable String getMethodName() {
- return mMethodName;
- }
-
- @DataClass.Generated.Member
- public @Nullable String getSenderIdentity() {
- return mSenderIdentity;
- }
-
- @DataClass.Generated.Member
- public @Nullable Intent getIntent() {
- return mIntent;
- }
-
- @DataClass.Generated.Member
- public @Nullable ComponentName getComponent() {
- return mComponent;
- }
-
- @DataClass.Generated.Member
- public @NonNull ComponentAliasMessage setMessage(@NonNull String value) {
- mMessage = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull ComponentAliasMessage setMethodName(@NonNull String value) {
- mMethodName = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull ComponentAliasMessage setSenderIdentity(@NonNull String value) {
- mSenderIdentity = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull ComponentAliasMessage setIntent(@NonNull Intent value) {
- mIntent = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull ComponentAliasMessage setComponent(@NonNull ComponentName value) {
- mComponent = value;
- return this;
- }
-
- @Override
- @DataClass.Generated.Member
- public String toString() {
- // You can override field toString logic by defining methods like:
- // String fieldNameToString() { ... }
-
- return "ComponentAliasMessage { " +
- "message = " + mMessage + ", " +
- "methodName = " + mMethodName + ", " +
- "senderIdentity = " + mSenderIdentity + ", " +
- "intent = " + mIntent + ", " +
- "component = " + mComponent +
- " }";
- }
-
- @Override
- @DataClass.Generated.Member
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- // You can override field parcelling by defining methods like:
- // void parcelFieldName(Parcel dest, int flags) { ... }
-
- byte flg = 0;
- if (mMessage != null) flg |= 0x1;
- if (mMethodName != null) flg |= 0x2;
- if (mSenderIdentity != null) flg |= 0x4;
- if (mIntent != null) flg |= 0x8;
- if (mComponent != null) flg |= 0x10;
- dest.writeByte(flg);
- if (mMessage != null) dest.writeString(mMessage);
- if (mMethodName != null) dest.writeString(mMethodName);
- if (mSenderIdentity != null) dest.writeString(mSenderIdentity);
- if (mIntent != null) dest.writeTypedObject(mIntent, flags);
- if (mComponent != null) dest.writeTypedObject(mComponent, flags);
- }
-
- @Override
- @DataClass.Generated.Member
- public int describeContents() { return 0; }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- @DataClass.Generated.Member
- /* package-private */ ComponentAliasMessage(@NonNull Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- byte flg = in.readByte();
- String message = (flg & 0x1) == 0 ? null : in.readString();
- String methodName = (flg & 0x2) == 0 ? null : in.readString();
- String senderIdentity = (flg & 0x4) == 0 ? null : in.readString();
- Intent intent = (flg & 0x8) == 0 ? null : (Intent) in.readTypedObject(Intent.CREATOR);
- ComponentName component = (flg & 0x10) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
-
- this.mMessage = message;
- this.mMethodName = methodName;
- this.mSenderIdentity = senderIdentity;
- this.mIntent = intent;
- this.mComponent = component;
-
- // onConstructed(); // You can define this method to get a callback
- }
-
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<ComponentAliasMessage> CREATOR
- = new Parcelable.Creator<ComponentAliasMessage>() {
- @Override
- public ComponentAliasMessage[] newArray(int size) {
- return new ComponentAliasMessage[size];
- }
-
- @Override
- public ComponentAliasMessage createFromParcel(@NonNull Parcel in) {
- return new ComponentAliasMessage(in);
- }
- };
-
- @DataClass.Generated(
- time = 1630098801203L,
- codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java",
- inputSignatures = "private @android.annotation.Nullable java.lang.String mMessage\nprivate @android.annotation.Nullable java.lang.String mMethodName\nprivate @android.annotation.Nullable java.lang.String mSenderIdentity\nprivate @android.annotation.Nullable android.content.Intent mIntent\nprivate @android.annotation.Nullable android.content.ComponentName mComponent\nclass ComponentAliasMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true, genToString=true, genAidl=false)")
- @Deprecated
- private void __metadata() {}
-
-
- //@formatter:on
- // End of generated code
-
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java
deleted file mode 100644
index 0899886fe951..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Build;
-import android.provider.DeviceConfig;
-
-import com.android.compatibility.common.util.DeviceConfigStateHelper;
-import com.android.compatibility.common.util.ShellUtils;
-
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.Test;
-
-/**
- * Test to make sure component-alias can't be enabled on user builds.
- */
-public class ComponentAliasNotSupportedOnUserBuildTest {
- protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
- DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_COMPONENT_ALIAS);
-
- @AfterClass
- public static void restoreDeviceConfig() throws Exception {
- sDeviceConfig.close();
- }
-
- @Test
- public void enableComponentAliasWithCompatFlag() throws Exception {
- Assume.assumeFalse(Build.isDebuggable());
-
- // Try to enable it by both the device config and compat-id.
- sDeviceConfig.set("enable_experimental_component_alias", "true");
- ShellUtils.runShellCommand(
- "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
-
- // Sleep for an arbitrary amount of time, so the config would sink in, if there was
- // no "not on user builds" check.
-
- Thread.sleep(5000);
-
- // Make sure the feature is still disabled.
- assertThat(ShellUtils.runShellCommand("dumpsys activity component-alias")
- .indexOf("Enabled: false") > 0).isTrue();
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
deleted file mode 100644
index f0ff088815af..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * 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.content.componentalias.tests;
-
-import static android.content.Context.BIND_AUTO_CREATE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.MAIN_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.SUB1_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.SUB2_PACKAGE;
-import static android.content.componentalias.tests.ComponentAliasTestCommon.TAG;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.hamcrest.core.IsNot.not;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-
-import com.android.compatibility.common.util.BroadcastMessenger;
-import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
-import com.android.compatibility.common.util.ShellUtils;
-import com.android.compatibility.common.util.TestUtils;
-
-import org.junit.Assume;
-import org.junit.Test;
-
-import java.util.function.Consumer;
-
-/**
- * Test for the experimental "Component alias" feature.
- *
- * Note this test exercises the relevant APIs, but don't actually check if the aliases are
- * resolved.
- *
- * Note all the helper APKs are battery-exempted (via AndroidTest.xml), so they can run
- * BG services.
- */
-public class ComponentAliasServiceTest extends BaseComponentAliasTest {
- /**
- * Service connection used throughout the tests. It sends a message for each callback via
- * the messenger.
- */
- private static final ServiceConnection sServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- log("onServiceConnected: " + name);
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity("sServiceConnection")
- .setMethodName("onServiceConnected")
- .setComponent(name);
-
- BroadcastMessenger.send(sContext, TAG, m);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- log("onServiceDisconnected: " + name);
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity("sServiceConnection")
- .setMethodName("onServiceDisconnected")
- .setComponent(name);
-
- BroadcastMessenger.send(sContext, TAG, m);
- }
-
- @Override
- public void onBindingDied(ComponentName name) {
- log("onBindingDied: " + name);
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity("sServiceConnection")
- .setMethodName("onBindingDied");
-
- BroadcastMessenger.send(sContext, TAG, m);
- }
-
- @Override
- public void onNullBinding(ComponentName name) {
- log("onNullBinding: " + name);
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity("sServiceConnection")
- .setMethodName("onNullBinding");
-
- BroadcastMessenger.send(sContext, TAG, m);
- }
- };
-
- private void testStartAndStopService_common(
- Intent originalIntent,
- ComponentName componentNameForClient,
- ComponentName componentNameForTarget) {
-
- ComponentAliasMessage m;
-
- try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext, TAG)) {
- // Start the service.
- ComponentName result = sContext.startService(originalIntent);
- assertThat(result).isEqualTo(componentNameForClient);
-
- // Check
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onStartCommand");
- // The app sees the rewritten intent.
- assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
-
- // Verify the original intent.
- assertThat(m.getIntent().getOriginalIntent().getComponent())
- .isEqualTo(originalIntent.getComponent());
- assertThat(m.getIntent().getOriginalIntent().getPackage())
- .isEqualTo(originalIntent.getPackage());
-
- // Stop the service.
- sContext.stopService(originalIntent);
-
- // Check
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onDestroy");
-
- receiver.ensureNoMoreMessages();
- }
- }
-
- private void forEachCombo(Consumer<Combo> callback) {
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias00"),
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Target00"),
- MAIN_PACKAGE + ".IS_ALIAS_00").apply(callback);
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias01"),
- new ComponentName(SUB1_PACKAGE, MAIN_PACKAGE + ".s.Target01"),
- MAIN_PACKAGE + ".IS_ALIAS_01").apply(callback);
- new Combo(
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias02"),
- new ComponentName(SUB2_PACKAGE, MAIN_PACKAGE + ".s.Target02"),
- MAIN_PACKAGE + ".IS_ALIAS_02").apply(callback);
- }
-
- @Test
- public void testStartAndStopService_explicitComponentName() {
- forEachCombo((c) -> {
- Intent i = new Intent().setComponent(c.alias);
- testStartAndStopService_common(i, c.alias, c.target);
- });
- }
-
- @Test
- public void testStartAndStopService_explicitPackageName() {
- forEachCombo((c) -> {
- Intent i = new Intent().setPackage(c.alias.getPackageName());
- i.setAction(c.action);
-
- testStartAndStopService_common(i, c.alias, c.target);
- });
- }
-
- @Test
- public void testStartAndStopService_override() throws Exception {
- Intent i = new Intent().setPackage(MAIN_PACKAGE);
- i.setAction(MAIN_PACKAGE + ".IS_ALIAS_01");
-
- // Change some of the aliases from what's defined in <meta-data>.
-
- ComponentName aliasA = new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias01");
- ComponentName targetA = new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Target02");
-
- ComponentName aliasB = new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias02");
- ComponentName targetB = new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Target01");
-
- sDeviceConfig.set("component_alias_overrides",
- aliasA.flattenToShortString() + ":" + targetA.flattenToShortString()
- + ","
- + aliasB.flattenToShortString() + ":" + targetB.flattenToShortString());
-
- TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
- return ShellUtils.runShellCommand("dumpsys activity component-alias")
- .indexOf(aliasA.flattenToShortString()
- + " -> " + targetA.flattenToShortString()) > 0;
- });
-
-
- testStartAndStopService_common(i, aliasA, targetA);
- }
-
- private void testBindAndUnbindService_common(
- Intent originalIntent,
- ComponentName componentNameForClient,
- ComponentName componentNameForTarget) {
- ComponentAliasMessage m;
-
- try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext, TAG)) {
- // Bind to the service.
- assertThat(sContext.bindService(
- originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();
-
- // Check the target side behavior.
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onBind");
- // The app sees the rewritten intent.
- assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
-
- // Verify the original intent.
- assertThat(m.getIntent().getOriginalIntent().getComponent())
- .isEqualTo(originalIntent.getComponent());
- assertThat(m.getIntent().getOriginalIntent().getPackage())
- .isEqualTo(originalIntent.getPackage());
-
- // Check the client side behavior.
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
- // The app sees the rewritten intent.
- assertThat(m.getComponent()).isEqualTo(componentNameForClient);
-
- // Unbind.
- sContext.unbindService(sServiceConnection);
-
- // Check the target side behavior.
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onDestroy");
-
- // Note onServiceDisconnected() won't be called in this case.
- receiver.ensureNoMoreMessages();
- }
- }
-
- @Test
- public void testBindService_explicitComponentName() {
- forEachCombo((c) -> {
- Intent i = new Intent().setComponent(c.alias);
-
- testBindAndUnbindService_common(i, c.alias, c.target);
- });
-
- }
-
- @Test
- public void testBindService_explicitPackageName() {
- forEachCombo((c) -> {
- Intent i = new Intent().setPackage(c.alias.getPackageName());
- i.setAction(c.action);
-
- testBindAndUnbindService_common(i, c.alias, c.target);
- });
- }
-
- /**
- * Make sure, when the service process is killed, the client will get a callback with the
- * right component name.
- */
- @Test
- public void testBindService_serviceKilled() {
-
- // We need to kill SUB2_PACKAGE, don't run it for this package.
- Assume.assumeThat(sContext.getPackageName(), not(SUB2_PACKAGE));
-
- Intent originalIntent = new Intent().setPackage(MAIN_PACKAGE);
- originalIntent.setAction(MAIN_PACKAGE + ".IS_ALIAS_02");
-
- final ComponentName componentNameForClient =
- new ComponentName(MAIN_PACKAGE, MAIN_PACKAGE + ".s.Alias02");
- final ComponentName componentNameForTarget =
- new ComponentName(SUB2_PACKAGE, MAIN_PACKAGE + ".s.Target02");
-
- ComponentAliasMessage m;
-
- try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext, TAG)) {
- // Bind to the service.
- assertThat(sContext.bindService(
- originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();
-
- // Check the target side behavior.
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onBind");
-
- m = receiver.waitForNextMessage();
- assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
- assertThat(m.getComponent()).isEqualTo(componentNameForClient);
- // We don't need to check all the fields because these are tested else where.
-
- // Now kill the service process.
- ShellUtils.runShellCommand("su 0 killall %s", SUB2_PACKAGE);
-
- // Check the target side behavior.
- m = receiver.waitForNextMessage();
-
- assertThat(m.getMethodName()).isEqualTo("onServiceDisconnected");
- assertThat(m.getComponent()).isEqualTo(componentNameForClient);
-
- receiver.ensureNoMoreMessages();
- }
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java b/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java
deleted file mode 100644
index 1d05e72a2f3f..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.content.componentalias.tests.b;
-
-import static android.content.componentalias.tests.ComponentAliasTestCommon.TAG;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.componentalias.tests.ComponentAliasMessage;
-import android.util.Log;
-
-import com.android.compatibility.common.util.BroadcastMessenger;
-
-public class BaseReceiver extends BroadcastReceiver {
- private String getMyIdentity(Context context) {
- return (new ComponentName(context.getPackageName(), this.getClass().getCanonicalName()))
- .flattenToShortString();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.i(TAG, "onReceive: on " + getMyIdentity(context) + " intent=" + intent);
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity(getMyIdentity(context))
- .setMethodName("onReceive")
- .setIntent(intent);
- BroadcastMessenger.send(context, TAG, m);
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java
deleted file mode 100644
index f9b9886b0bb2..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.b;
-
-public class Target04 extends BaseReceiver {
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/BaseService.java b/tests/componentalias/src/android/content/componentalias/tests/s/BaseService.java
deleted file mode 100644
index 535d9b80f100..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/BaseService.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-import static android.content.componentalias.tests.ComponentAliasTestCommon.TAG;
-
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.componentalias.tests.ComponentAliasMessage;
-import android.os.Binder;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.compatibility.common.util.BroadcastMessenger;
-
-public class BaseService extends Service {
- private String getMyIdentity() {
- return (new ComponentName(this.getPackageName(), this.getClass().getCanonicalName()))
- .flattenToShortString();
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i(TAG, "onStartCommand: on " + getMyIdentity() + " intent=" + intent);
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity(getMyIdentity())
- .setMethodName("onStartCommand")
- .setIntent(intent);
- BroadcastMessenger.send(this, TAG, m);
-
- return START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- Log.i(TAG, "onDestroy: on " + getMyIdentity());
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity(getMyIdentity())
- .setMethodName("onDestroy");
- BroadcastMessenger.send(this, TAG, m);
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.i(TAG, "onBind: on " + getMyIdentity() + " intent=" + intent);
-
- ComponentAliasMessage m = new ComponentAliasMessage()
- .setSenderIdentity(getMyIdentity())
- .setMethodName("onBind")
- .setIntent(intent);
- BroadcastMessenger.send(this, TAG, m);
-
- return new Binder();
- }
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/Target00.java b/tests/componentalias/src/android/content/componentalias/tests/s/Target00.java
deleted file mode 100644
index 64b91f5695f5..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/Target00.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-public class Target00 extends BaseService {
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/Target01.java b/tests/componentalias/src/android/content/componentalias/tests/s/Target01.java
deleted file mode 100644
index bd589991d7dc..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/Target01.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-public class Target01 extends BaseService {
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/Target02.java b/tests/componentalias/src/android/content/componentalias/tests/s/Target02.java
deleted file mode 100644
index 0ddf8188768b..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/Target02.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-public class Target02 extends BaseService {
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/Target03.java b/tests/componentalias/src/android/content/componentalias/tests/s/Target03.java
deleted file mode 100644
index 0dbc0501b6f9..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/Target03.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-public class Target03 extends BaseService {
-}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/s/Target04.java b/tests/componentalias/src/android/content/componentalias/tests/s/Target04.java
deleted file mode 100644
index 099425867f02..000000000000
--- a/tests/componentalias/src/android/content/componentalias/tests/s/Target04.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.content.componentalias.tests.s;
-
-public class Target04 extends BaseService {
-}
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 e0f3f03e9cb7..421ceb797c15 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorManagerServicePermissionTest.java
@@ -50,6 +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 CombinedVibration EFFECT =
CombinedVibration.createParallel(
VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -106,7 +107,8 @@ public class VibratorManagerServicePermissionTest {
@Test
public void testVibrateWithoutPermissionFails() throws RemoteException {
expectSecurityException("VIBRATE");
- mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -115,7 +117,8 @@ public class VibratorManagerServicePermissionTest {
throws RemoteException {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.myUid(), PACKAGE_NAME, EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.myUid(), DISPLAY_ID, PACKAGE_NAME, EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -124,7 +127,8 @@ public class VibratorManagerServicePermissionTest {
expectSecurityException("UPDATE_APP_OPS_STATS");
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE);
- mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_ID, "android", EFFECT, ATTRS,
+ "testVibrate",
new Binder());
}
@@ -133,7 +137,8 @@ public class VibratorManagerServicePermissionTest {
getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
Manifest.permission.VIBRATE,
Manifest.permission.UPDATE_APP_OPS_STATS);
- mVibratorService.vibrate(Process.SYSTEM_UID, "android", EFFECT, ATTRS, "testVibrate",
+ mVibratorService.vibrate(Process.SYSTEM_UID, DISPLAY_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 1fe13fe97fbe..06cbeb5368a5 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -124,16 +124,6 @@ public class WindowManagerPermissionTests extends TestCase {
@SmallTest
public void testSET_ORIENTATION() {
try {
- mWm.updateRotation(true, false);
- fail("IWindowManager.updateRotation did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- try {
mWm.freezeRotation(-1);
fail("IWindowManager.freezeRotation did not throw SecurityException as"
+ " expected");
diff --git a/tests/testables/src/android/testing/TestWithLooperRule.java b/tests/testables/src/android/testing/TestWithLooperRule.java
new file mode 100644
index 000000000000..99b303e0c43a
--- /dev/null
+++ b/tests/testables/src/android/testing/TestWithLooperRule.java
@@ -0,0 +1,161 @@
+/*
+ * 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.testing;
+
+import android.testing.TestableLooper.LooperFrameworkMethod;
+import android.testing.TestableLooper.RunWithLooper;
+
+import org.junit.internal.runners.statements.InvokeMethod;
+import org.junit.rules.MethodRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * This rule is meant to be an alternative of using AndroidTestingRunner.
+ * It let tests to start from background thread, and assigns mainLooper or new
+ * Looper for the Statement.
+ */
+public class TestWithLooperRule implements MethodRule {
+
+ /*
+ * This rule requires to be the inner most Rule, so the next statement is RunAfters
+ * instead of another rule. You can set it by '@Rule(order = Integer.MAX_VALUE)'
+ */
+ @Override
+ public Statement apply(Statement base, FrameworkMethod method, Object target) {
+ // getting testRunner check, if AndroidTestingRunning then we skip this rule
+ RunWith runWithAnnotation = target.getClass().getAnnotation(RunWith.class);
+ if (runWithAnnotation != null) {
+ // if AndroidTestingRunner or it's subclass is in use, do nothing
+ if (AndroidTestingRunner.class.isAssignableFrom(runWithAnnotation.value())) {
+ return base;
+ }
+ }
+
+ // check if RunWithLooper annotation is used. If not skip this rule
+ RunWithLooper looperAnnotation = method.getAnnotation(RunWithLooper.class);
+ if (looperAnnotation == null) {
+ looperAnnotation = target.getClass().getAnnotation(RunWithLooper.class);
+ }
+ if (looperAnnotation == null) {
+ return base;
+ }
+
+ try {
+ wrapMethodInStatement(base, method, target);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ return base;
+ }
+
+ // This method is based on JUnit4 test runner flow. It might need to be revisited when JUnit is
+ // upgraded
+ // TODO(b/277743626): use a cleaner way to wrap each statements; may require some JUnit
+ // patching to facilitate this.
+ private void wrapMethodInStatement(Statement base, FrameworkMethod method, Object target)
+ throws Exception {
+ Statement next = base;
+ try {
+ while (next != null) {
+ switch (next.getClass().getSimpleName()) {
+ case "RunAfters":
+ this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
+ next.getClass(), "afters", method, target);
+ next = getNextStatement(next, "next");
+ break;
+ case "RunBefores":
+ this.<List<FrameworkMethod>>wrapFieldMethodFor(next,
+ next.getClass(), "befores", method, target);
+ next = getNextStatement(next, "next");
+ break;
+ case "FailOnTimeout":
+ // Note: withPotentialTimeout() from BlockJUnit4ClassRunner might use
+ // FailOnTimeout which always wraps a new thread during InvokeMethod
+ // method evaluation.
+ next = getNextStatement(next, "originalStatement");
+ break;
+ case "InvokeMethod":
+ this.<FrameworkMethod>wrapFieldMethodFor(next,
+ InvokeMethod.class, "testMethod", method, target);
+ return;
+ default:
+ throw new Exception(
+ String.format("Unexpected Statement received: [%s]",
+ next.getClass().getName())
+ );
+ }
+ }
+ } catch (Exception e) {
+ throw e;
+ }
+ }
+
+ // Wrapping the befores, afters, and InvokeMethods with LooperFrameworkMethod
+ // within the statement.
+ private <T> void wrapFieldMethodFor(Statement base, Class<?> targetClass, String fieldStr,
+ FrameworkMethod method, Object target)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field = targetClass.getDeclaredField(fieldStr);
+ field.setAccessible(true);
+ T fieldInstance = (T) field.get(base);
+ if (fieldInstance instanceof FrameworkMethod) {
+ field.set(base, looperWrap(method, target, (FrameworkMethod) fieldInstance));
+ } else {
+ // Befores and afters methods lists
+ field.set(base, looperWrap(method, target, (List<FrameworkMethod>) fieldInstance));
+ }
+ }
+
+ // Retrieve the next wrapped statement based on the selected field string
+ private Statement getNextStatement(Statement base, String fieldStr)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field nextField = base.getClass().getDeclaredField(fieldStr);
+ nextField.setAccessible(true);
+ Object value = nextField.get(base);
+ return value instanceof Statement ? (Statement) value : null;
+ }
+
+ protected FrameworkMethod looperWrap(FrameworkMethod method, Object test,
+ FrameworkMethod base) {
+ RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+ if (annotation == null) annotation = test.getClass().getAnnotation(RunWithLooper.class);
+ if (annotation != null) {
+ return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test);
+ }
+ return base;
+ }
+
+ protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test,
+ List<FrameworkMethod> methods) {
+ RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+ if (annotation == null) annotation = test.getClass().getAnnotation(RunWithLooper.class);
+ if (annotation != null) {
+ methods = new ArrayList<>(methods);
+ for (int i = 0; i < methods.size(); i++) {
+ methods.set(i, LooperFrameworkMethod.get(methods.get(i),
+ annotation.setAsMainLooper(), test));
+ }
+ }
+ return methods;
+ }
+}
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index e2668bc4281f..0f04d6ae1721 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -33,11 +33,15 @@ import android.provider.Settings;
import android.util.ArrayMap;
import android.view.LayoutInflater;
+import androidx.annotation.Nullable;
+
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.ArrayList;
+
/**
* A ContextWrapper with utilities specifically designed to make Testing easier.
*
@@ -61,6 +65,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
private final TestableContentResolver mTestableContentResolver;
private final TestableSettingsProvider mSettingsProvider;
+ private ArrayList<MockServiceResolver> mMockServiceResolvers;
private ArrayMap<String, Object> mMockSystemServices;
private ArrayMap<ComponentName, IBinder> mMockServices;
private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
@@ -214,12 +219,15 @@ public class TestableContext extends ContextWrapper implements TestRule {
/**
* Adds a mock service to be connected to by a bindService call.
* <p>
- * Normally a TestableContext will pass through all bind requests to the base context
- * but when addMockService has been called for a ComponentName being bound, then
- * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
- * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
- * when the service is unbound.
+ * Normally a TestableContext will pass through all bind requests to the base context
+ * but when addMockService has been called for a ComponentName being bound, then
+ * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
+ * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
+ * when the service is unbound.
* </p>
+ *
+ * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to
+ * ComponentNames
*/
public void addMockService(ComponentName component, IBinder service) {
if (mMockServices == null) mMockServices = new ArrayMap<>();
@@ -227,12 +235,38 @@ public class TestableContext extends ContextWrapper implements TestRule {
}
/**
+ * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}.
+ */
+ public interface MockServiceResolver {
+ @Nullable
+ ComponentName resolve(Intent service);
+ }
+
+ /**
+ * Registers a strategy to resolve service intents to registered mock services.
+ * <p>
+ * The result of the first {@link MockServiceResolver} to return a non-null
+ * {@link ComponentName} is used to look up a mock service. The mock service must be registered
+ * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component
+ * name.
+ *
+ * If none of the resolvers return a non-null value, or the first returned component name
+ * does not link to a registered mock service, the bind requests are passed to the base context
+ *
+ * The resolvers are queried in order of registration.
+ */
+ public void addMockServiceResolver(MockServiceResolver resolver) {
+ if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>();
+ mMockServiceResolvers.add(resolver);
+ }
+
+ /**
* @see #addMockService(ComponentName, IBinder)
*/
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindService(service, conn, flags);
}
@@ -243,7 +277,7 @@ public class TestableContext extends ContextWrapper implements TestRule {
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
Handler handler, UserHandle user) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindServiceAsUser(service, conn, flags, handler, user);
}
@@ -254,18 +288,36 @@ public class TestableContext extends ContextWrapper implements TestRule {
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
- if (checkMocks(service.getComponent(), conn)) return true;
+ if (checkMocks(service, conn)) return true;
return super.bindServiceAsUser(service, conn, flags, user);
}
- private boolean checkMocks(ComponentName component, ServiceConnection conn) {
- if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
- if (mActiveServices == null) mActiveServices = new ArrayMap<>();
- mActiveServices.put(conn, component);
- conn.onServiceConnected(component, mMockServices.get(component));
- return true;
+ private boolean checkMocks(Intent service, ServiceConnection conn) {
+ if (mMockServices == null) return false;
+
+ ComponentName serviceComponent = resolveMockServiceComponent(service);
+ if (serviceComponent == null) return false;
+
+ IBinder serviceImpl = mMockServices.get(serviceComponent);
+ if (serviceImpl == null) return false;
+
+ if (mActiveServices == null) mActiveServices = new ArrayMap<>();
+ mActiveServices.put(conn, serviceComponent);
+ conn.onServiceConnected(serviceComponent, serviceImpl);
+ return true;
+ }
+
+ private ComponentName resolveMockServiceComponent(Intent service) {
+ ComponentName specifiedComponentName = service.getComponent();
+ if (specifiedComponentName != null) return specifiedComponentName;
+
+ if (mMockServiceResolvers == null) return null;
+
+ for (MockServiceResolver resolver : mMockServiceResolvers) {
+ ComponentName resolvedComponent = resolver.resolve(service);
+ if (resolvedComponent != null) return resolvedComponent;
}
- return false;
+ return null;
}
/**
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index ebe9b5706bf8..edd6dd3468ef 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -30,6 +30,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import java.util.Map;
/**
@@ -45,6 +46,9 @@ public class TestableLooper {
* catch crashes.
*/
public static final boolean HOLD_MAIN_THREAD = false;
+ private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
+ private static final Field MESSAGE_NEXT_FIELD;
+ private static final Field MESSAGE_WHEN_FIELD;
private Looper mLooper;
private MessageQueue mQueue;
@@ -54,6 +58,19 @@ public class TestableLooper {
private Runnable mEmptyMessage;
private TestLooperManager mQueueWrapper;
+ static {
+ try {
+ MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Failed to initialize TestableLooper", e);
+ }
+ }
+
public TestableLooper(Looper l) throws Exception {
this(acquireLooperManager(l), l);
}
@@ -119,6 +136,33 @@ public class TestableLooper {
while (processQueuedMessages() != 0) ;
}
+ public void moveTimeForward(long milliSeconds) {
+ try {
+ Message msg = getMessageLinkedList();
+ while (msg != null) {
+ long updatedWhen = msg.getWhen() - milliSeconds;
+ if (updatedWhen < 0) {
+ updatedWhen = 0;
+ }
+ MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
+ msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
+ }
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access failed in TestableLooper: set - Message.when", e);
+ }
+ }
+
+ private Message getMessageLinkedList() {
+ try {
+ MessageQueue queue = mLooper.getQueue();
+ return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(
+ "Access failed in TestableLooper: get - MessageQueue.mMessages",
+ e);
+ }
+ }
+
private int processQueuedMessages() {
int count = 0;
mEmptyMessage = () -> { };
diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java
index c60f07d56d92..27d5b66b355e 100644
--- a/tests/testables/src/android/testing/TestableResources.java
+++ b/tests/testables/src/android/testing/TestableResources.java
@@ -59,7 +59,8 @@ public class TestableResources {
* Since resource ids are unique there is a single addOverride that will override the value
* whenever it is gotten regardless of which method is used (i.e. getColor or getDrawable).
* </p>
- * @param id The resource id to be overridden
+ *
+ * @param id The resource id to be overridden
* @param value The value of the resource, null to cause a {@link Resources.NotFoundException}
* when gotten.
*/
@@ -74,28 +75,33 @@ public class TestableResources {
* cause a {@link Resources.NotFoundException} whereas removing the override will actually
* switch back to returning the default/real value of the resource.
* </p>
- * @param id
*/
public void removeOverride(int id) {
mOverrides.remove(id);
}
private Object answer(InvocationOnMock invocationOnMock) throws Throwable {
- try {
- int id = invocationOnMock.getArgument(0);
- int index = mOverrides.indexOfKey(id);
- if (index >= 0) {
- Object value = mOverrides.valueAt(index);
- if (value == null) throw new Resources.NotFoundException();
- return value;
+ // Only try to override methods with an integer first argument
+ if (invocationOnMock.getArguments().length > 0) {
+ Object argument = invocationOnMock.getArgument(0);
+ if (argument instanceof Integer) {
+ try {
+ int id = (Integer)argument;
+ int index = mOverrides.indexOfKey(id);
+ if (index >= 0) {
+ Object value = mOverrides.valueAt(index);
+ if (value == null) throw new Resources.NotFoundException();
+ return value;
+ }
+ } catch (Resources.NotFoundException e) {
+ // Let through NotFoundException.
+ throw e;
+ } catch (Throwable t) {
+ // Generic catching for the many things that can go wrong, fall back to
+ // the real implementation.
+ Log.i(TAG, "Falling back to default resources call " + t);
+ }
}
- } catch (Resources.NotFoundException e) {
- // Let through NotFoundException.
- throw e;
- } catch (Throwable t) {
- // Generic catching for the many things that can go wrong, fall back to
- // the real implementation.
- Log.i(TAG, "Falling back to default resources call " + t);
}
return invocationOnMock.callRealMethod();
}
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index fd92c657cb2e..b850cb8e6dfd 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -49,14 +49,15 @@ public class TestableSettingsProvider extends MockContentProvider {
}
void clearValuesAndCheck(Context context) {
- int userId = UserHandle.myUserId();
- mValues.put(key("global", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
- mValues.put(key("secure", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
- mValues.put(key("system", MY_UNIQUE_KEY, userId), MY_UNIQUE_KEY);
-
+ // Ensure we swapped over to use TestableSettingsProvider
Settings.Global.clearProviderForTest();
Settings.Secure.clearProviderForTest();
Settings.System.clearProviderForTest();
+
+ // putString will eventually invoking the mocked call() method and update mValues
+ Settings.Global.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
+ Settings.Secure.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
+ Settings.System.putString(context.getContentResolver(), MY_UNIQUE_KEY, MY_UNIQUE_KEY);
// Verify that if any test is using TestableContext, they all have the correct settings
// provider.
assertEquals("Incorrect settings provider, test using incorrect Context?", MY_UNIQUE_KEY,
@@ -71,7 +72,11 @@ public class TestableSettingsProvider extends MockContentProvider {
public Bundle call(String method, String arg, Bundle extras) {
// Methods are "GET_system", "GET_global", "PUT_secure", etc.
- final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId());
+ int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.USER_CURRENT);
+ if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ userId = UserHandle.myUserId();
+ }
+
final String[] commands = method.split("_", 2);
final String op = commands[0];
final String table = commands[1];
diff --git a/tests/testables/src/com/android/internal/config/sysui/OWNERS b/tests/testables/src/com/android/internal/config/sysui/OWNERS
new file mode 100644
index 000000000000..2e96c97c8bb3
--- /dev/null
+++ b/tests/testables/src/com/android/internal/config/sysui/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/OWNERS
diff --git a/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
new file mode 100644
index 000000000000..a8815dc1f0ee
--- /dev/null
+++ b/tests/testables/src/com/android/internal/config/sysui/TestableFlagResolver.java
@@ -0,0 +1,33 @@
+/*
+ * 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.config.sysui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestableFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver {
+ private Map<String, Boolean> mOverrides = new HashMap<>();
+
+ @Override
+ public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) {
+ return mOverrides.getOrDefault(flag.mSysPropKey, flag.mDefaultValue);
+ }
+
+ public void setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, boolean isEnabled) {
+ mOverrides.put(flag.mSysPropKey, isEnabled);
+ }
+}
diff --git a/tests/testables/tests/Android.bp b/tests/testables/tests/Android.bp
index ba323d3fe47a..06449e0ce574 100644
--- a/tests/testables/tests/Android.bp
+++ b/tests/testables/tests/Android.bp
@@ -46,5 +46,8 @@ android_test {
"android.test.mock",
],
certificate: "platform",
- test_suites: ["device-tests"],
+ test_suites: [
+ "device-tests",
+ "automotive-tests",
+ ],
}
diff --git a/tests/testables/tests/com/android/internal/config/sysui/OWNERS b/tests/testables/tests/com/android/internal/config/sysui/OWNERS
new file mode 100644
index 000000000000..2e96c97c8bb3
--- /dev/null
+++ b/tests/testables/tests/com/android/internal/config/sysui/OWNERS
@@ -0,0 +1 @@
+include /packages/SystemUI/OWNERS
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 25f6a48871d3..0f491b86626c 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -19,15 +19,19 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import android.os.Handler;
import android.os.Looper;
@@ -162,7 +166,7 @@ public class TestableLooperTest {
@Test
public void testCorrectLooperExecution() throws Exception {
- boolean[] hasRun = new boolean[] { false };
+ boolean[] hasRun = new boolean[]{false};
Runnable r = () -> {
assertEquals("Should run on main looper", Looper.getMainLooper(), Looper.myLooper());
hasRun[0] = true;
@@ -177,4 +181,63 @@ public class TestableLooperTest {
testableLooper.destroy();
}
}
+
+ @Test
+ public void testDelayedDispatchNoTimeMove() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ }
+
+ @Test
+ public void testDelayedMessageDoesntSend() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+ final Message messageC = handler.obtainMessage(3);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+ handler.sendMessageDelayed(messageC, 500);
+
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ verify(handler, never()).dispatchMessage(messageC);
+ }
+
+ @Test
+ public void testMessageSendsAfterDelay() {
+ Handler handler = spy(new Handler(mTestableLooper.getLooper()));
+ InOrder inOrder = inOrder(handler);
+
+ final Message messageA = handler.obtainMessage(1);
+ final Message messageB = handler.obtainMessage(2);
+ final Message messageC = handler.obtainMessage(3);
+
+ handler.sendMessageDelayed(messageA, 0);
+ handler.sendMessageDelayed(messageB, 0);
+ handler.sendMessageDelayed(messageC, 500);
+
+ mTestableLooper.moveTimeForward(500);
+ mTestableLooper.processAllMessages();
+
+ inOrder.verify(handler).dispatchMessage(messageA);
+ inOrder.verify(handler).dispatchMessage(messageB);
+ inOrder.verify(handler).dispatchMessage(messageC);
+ }
+
}
diff --git a/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java b/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
deleted file mode 100644
index 5ab4dc60e2ac..000000000000
--- a/tests/utils/hostutils/src/com/android/fsverity/AddFsVerityCertRule.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.fsverity;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-import com.android.tradefed.util.CommandResult;
-import com.android.tradefed.util.CommandStatus;
-
-import org.junit.rules.ExternalResource;
-
-public final class AddFsVerityCertRule extends ExternalResource {
-
- private static final String APK_VERITY_STANDARD_MODE = "2";
-
- private final BaseHostJUnit4Test mHost;
- private final String mCertPath;
- private String mKeyId;
-
- public AddFsVerityCertRule(BaseHostJUnit4Test host, String certPath) {
- mHost = host;
- mCertPath = certPath;
- }
-
- @Override
- protected void before() throws Throwable {
- ITestDevice device = mHost.getDevice();
- String apkVerityMode = device.getProperty("ro.apk_verity.mode");
- assumeTrue(device.getLaunchApiLevel() >= 30
- || APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
-
- String keyId = executeCommand(
- "mini-keyctl padd asymmetric fsv_test .fs-verity < " + mCertPath).trim();
- assertThat(keyId).matches("^\\d+$");
- mKeyId = keyId;
- }
-
- @Override
- protected void after() {
- if (mKeyId == null) return;
- try {
- executeCommand("mini-keyctl unlink " + mKeyId + " .fs-verity");
- } catch (DeviceNotAvailableException e) {
- LogUtil.CLog.e(e);
- }
- mKeyId = null;
- }
-
- private String executeCommand(String cmd) throws DeviceNotAvailableException {
- CommandResult result = mHost.getDevice().executeShellV2Command(cmd);
- assertWithMessage("`" + cmd + "` failed: " + result.getStderr())
- .that(result.getStatus())
- .isEqualTo(CommandStatus.SUCCESS);
- return result.getStdout();
- }
-}
diff --git a/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java
new file mode 100644
index 000000000000..b94bb41c0988
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/FakePermissionEnforcer.java
@@ -0,0 +1,64 @@
+/*
+ * 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.os.test;
+
+import static android.permission.PermissionManager.PERMISSION_GRANTED;
+import static android.permission.PermissionManager.PERMISSION_HARD_DENIED;
+
+import android.annotation.NonNull;
+import android.content.AttributionSource;
+import android.os.PermissionEnforcer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Fake for {@link PermissionEnforcer}. Useful for tests wanting to mock the
+ * permission checks of an AIDL service. FakePermissionEnforcer may be passed
+ * to the constructor of the AIDL-generated Stub class.
+ *
+ */
+public class FakePermissionEnforcer extends PermissionEnforcer {
+ private Set<String> mGranted;
+
+ public FakePermissionEnforcer() {
+ mGranted = new HashSet();
+ }
+
+ public void grant(String permission) {
+ mGranted.add(permission);
+ }
+
+ public void revoke(String permission) {
+ mGranted.remove(permission);
+ }
+
+ private boolean granted(String permission) {
+ return mGranted.contains(permission);
+ }
+
+ @Override
+ protected int checkPermission(@NonNull String permission,
+ @NonNull AttributionSource source) {
+ return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+ }
+
+ @Override
+ protected int checkPermission(@NonNull String permission, int pid, int uid) {
+ return granted(permission) ? PERMISSION_GRANTED : PERMISSION_HARD_DENIED;
+ }
+}
diff --git a/tests/utils/testutils/java/android/os/test/OWNERS b/tests/utils/testutils/java/android/os/test/OWNERS
new file mode 100644
index 000000000000..3a9129e1bb69
--- /dev/null
+++ b/tests/utils/testutils/java/android/os/test/OWNERS
@@ -0,0 +1 @@
+per-file FakePermissionEnforcer.java = file:/tests/EnforcePermission/OWNERS
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index adec05851f23..cc3781a0bfb3 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -147,12 +147,39 @@ public class BroadcastInterceptingContext extends ContextWrapper {
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
- return registerReceiver(receiver, filter, null, null);
+ return registerReceiver(receiver, filter, null, null, 0);
+ }
+
+ /**
+ * Registers the specified {@code receiver} to listen for broadcasts that match the {@code
+ * filter} in the current process.
+ *
+ * <p>Since this method only listens for broadcasts in the current process, the provided {@code
+ * flags} are ignored; this method is primarily intended to allow receivers that register with
+ * flags to register in the current process during tests.
+ */
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ return registerReceiver(receiver, filter, null, null, flags);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
+ return registerReceiver(receiver, filter, broadcastPermission, scheduler, 0);
+ }
+
+ /**
+ * Registers the specified {@code receiver} to listen for broadcasts that match the {@code
+ * filter} to run in the context of the specified {@code scheduler} in the current process.
+ *
+ * <p>Since this method only listens for broadcasts in the current process, the provided {@code
+ * flags} are ignored; this method is primarily intended to allow receivers that register with
+ * flags to register in the current process during tests.
+ */
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
synchronized (mInterceptors) {
mInterceptors.add(new BroadcastInterceptor(receiver, filter, scheduler));
}
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 15a6afc5ff7c..7c5dcf8b95f7 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -50,6 +50,7 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.app.servertransaction.", // all tests under the package.
"android.view.CutoutSpecificationTest",
"android.view.DisplayCutoutTest",
+ "android.view.DisplayShapeTest",
"android.view.InsetsAnimationControlImplTest",
"android.view.InsetsControllerTest",
"android.view.InsetsFlagsTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 2fbcf9d87bd4..156961312323 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -20,6 +20,7 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import org.junit.Test;
@@ -31,8 +32,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
- // Package private for use in VcnGatewayConnectionConfigTest
- static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnCellUnderlyingNetworkTemplate.Builder getTestNetworkTemplateBuilder() {
return new VcnCellUnderlyingNetworkTemplate.Builder()
.setMetered(MATCH_FORBIDDEN)
.setMinUpstreamBandwidthKbps(
@@ -44,13 +45,44 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
.setOperatorPlmnIds(ALLOWED_PLMN_IDS)
.setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
.setRoaming(MATCH_FORBIDDEN)
- .setOpportunistic(MATCH_REQUIRED)
- .build();
+ .setOpportunistic(MATCH_REQUIRED);
+ }
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
+ return getTestNetworkTemplateBuilder().build();
+ }
+
+ private void setAllCapabilities(
+ VcnCellUnderlyingNetworkTemplate.Builder builder, int matchCriteria) {
+ builder.setCbs(matchCriteria);
+ builder.setDun(matchCriteria);
+ builder.setIms(matchCriteria);
+ builder.setInternet(matchCriteria);
+ builder.setMms(matchCriteria);
+ builder.setRcs(matchCriteria);
+ }
+
+ private void verifyAllCapabilities(
+ VcnCellUnderlyingNetworkTemplate template,
+ int expectMatchCriteriaforNonInternet,
+ int expectMatchCriteriaforInternet) {
+ assertEquals(expectMatchCriteriaforNonInternet, template.getCbs());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getDun());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getIms());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getMms());
+ assertEquals(expectMatchCriteriaforNonInternet, template.getRcs());
+
+ assertEquals(expectMatchCriteriaforInternet, template.getInternet());
}
@Test
public void testBuilderAndGetters() {
- final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder();
+ setAllCapabilities(builder, MATCH_REQUIRED);
+
+ final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build();
+
assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
assertEquals(
TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
@@ -68,6 +100,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
+
+ verifyAllCapabilities(networkPriority, MATCH_REQUIRED, MATCH_REQUIRED);
}
@Test
@@ -86,6 +120,8 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
assertEquals(MATCH_ANY, networkPriority.getRoaming());
assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
+
+ verifyAllCapabilities(networkPriority, MATCH_ANY, MATCH_REQUIRED);
}
@Test
@@ -112,6 +148,25 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
}
@Test
+ public void testBuildFailWithoutRequiredCapabilities() {
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder().setInternet(MATCH_ANY).build();
+
+ fail("Expected IAE for missing required capabilities");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testEqualsWithDifferentCapabilities() {
+ final VcnCellUnderlyingNetworkTemplate left =
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build();
+ final VcnCellUnderlyingNetworkTemplate right =
+ new VcnCellUnderlyingNetworkTemplate.Builder().setMms(MATCH_REQUIRED).build();
+ assertNotEquals(left, right);
+ }
+
+ @Test
public void testPersistableBundle() {
final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
assertEquals(
@@ -119,4 +174,16 @@ public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTe
VcnUnderlyingNetworkTemplate.fromPersistableBundle(
networkPriority.toPersistableBundle()));
}
+
+ @Test
+ public void testPersistableBundleWithCapabilities() {
+ final VcnCellUnderlyingNetworkTemplate.Builder builder = getTestNetworkTemplateBuilder();
+ setAllCapabilities(builder, MATCH_REQUIRED);
+
+ final VcnCellUnderlyingNetworkTemplate networkPriority = builder.build();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index 7ac51b7e3342..73a0a6183cb6 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -16,7 +16,13 @@
package android.net.vcn;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -24,6 +30,7 @@ import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Parcel;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -42,19 +49,36 @@ public class VcnConfigTest {
private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
+ private static final Set<Integer> RESTRICTED_TRANSPORTS = new ArraySet<>();
+
+ static {
+ RESTRICTED_TRANSPORTS.add(TRANSPORT_WIFI);
+ RESTRICTED_TRANSPORTS.add(TRANSPORT_CELLULAR);
+ }
+
private final Context mContext = mock(Context.class);
// Public visibility for VcnManagementServiceTest
- public static VcnConfig buildTestConfig(@NonNull Context context) {
+ public static VcnConfig buildTestConfig(
+ @NonNull Context context, Set<Integer> restrictedTransports) {
VcnConfig.Builder builder = new VcnConfig.Builder(context);
for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
builder.addGatewayConnectionConfig(gatewayConnectionConfig);
}
+ if (restrictedTransports != null) {
+ builder.setRestrictedUnderlyingNetworkTransports(restrictedTransports);
+ }
+
return builder.build();
}
+ // Public visibility for VcnManagementServiceTest
+ public static VcnConfig buildTestConfig(@NonNull Context context) {
+ return buildTestConfig(context, null);
+ }
+
@Before
public void setUp() throws Exception {
doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName();
@@ -91,11 +115,25 @@ public class VcnConfigTest {
}
@Test
- public void testBuilderAndGetters() {
+ public void testBuilderAndGettersDefaultValues() {
final VcnConfig config = buildTestConfig(mContext);
assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ assertFalse(config.isTestModeProfile());
+ assertEquals(
+ Collections.singleton(TRANSPORT_WIFI),
+ config.getRestrictedUnderlyingNetworkTransports());
+ }
+
+ @Test
+ public void testBuilderAndGettersConfigRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+
+ assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
+ assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
+ assertFalse(config.isTestModeProfile());
+ assertEquals(RESTRICTED_TRANSPORTS, config.getRestrictedUnderlyingNetworkTransports());
}
@Test
@@ -106,6 +144,55 @@ public class VcnConfigTest {
}
@Test
+ public void testPersistableBundleWithRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+
+ assertEquals(config, new VcnConfig(config.toPersistableBundle()));
+ }
+
+ @Test
+ public void testEqualityWithRestrictedTransports() {
+ final VcnConfig config = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+ final VcnConfig configEqual = buildTestConfig(mContext, RESTRICTED_TRANSPORTS);
+ final VcnConfig configNotEqual =
+ buildTestConfig(mContext, Collections.singleton(TRANSPORT_WIFI));
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
+
+ private VcnConfig buildConfigRestrictTransportTest(boolean isTestMode) throws Exception {
+ VcnConfig.Builder builder =
+ new VcnConfig.Builder(mContext)
+ .setRestrictedUnderlyingNetworkTransports(Set.of(TRANSPORT_TEST));
+ if (isTestMode) {
+ builder.setIsTestModeProfile();
+ }
+
+ for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
+ builder.addGatewayConnectionConfig(gatewayConnectionConfig);
+ }
+
+ return builder.build();
+ }
+
+ @Test
+ public void testRestrictTransportTestInTestModeProfile() throws Exception {
+ final VcnConfig config = buildConfigRestrictTransportTest(true /* isTestMode */);
+ assertEquals(Set.of(TRANSPORT_TEST), config.getRestrictedUnderlyingNetworkTransports());
+ }
+
+ @Test
+ public void testRestrictTransportTestInNonTestModeProfile() throws Exception {
+ try {
+ buildConfigRestrictTransportTest(false /* isTestMode */);
+ fail("Expected exception because the config is not a test mode profile");
+ } catch (Exception expected) {
+
+ }
+ }
+
+ @Test
public void testParceling() {
final VcnConfig config = buildTestConfig(mContext);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 2aef9ae7ca32..a1a39ff173b4 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -19,9 +19,11 @@ package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -42,7 +44,9 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -78,6 +82,10 @@ public class VcnGatewayConnectionConfigTest {
TimeUnit.MINUTES.toMillis(30)
};
public static final int MAX_MTU = 1360;
+ public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
+
+ private static final Set<Integer> GATEWAY_OPTIONS =
+ Collections.singleton(VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY);
public static final IkeTunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
TunnelConnectionParamsUtilsTest.buildTestParams();
@@ -93,14 +101,22 @@ public class VcnGatewayConnectionConfigTest {
EXPOSED_CAPS);
}
- // Public for use in VcnGatewayConnectionTest
- public static VcnGatewayConnectionConfig buildTestConfig() {
+ // Public for use in UnderlyingNetworkControllerTest
+ public static VcnGatewayConnectionConfig buildTestConfig(
+ List<VcnUnderlyingNetworkTemplate> nwTemplates) {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
+ newBuilder()
+ .setVcnUnderlyingNetworkPriorities(nwTemplates)
+ .setMinUdpPort4500NatTimeoutSeconds(MIN_UDP_PORT_4500_NAT_TIMEOUT);
return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfig(UNDERLYING_NETWORK_TEMPLATES);
+ }
+
private static VcnGatewayConnectionConfig.Builder newBuilder() {
// Append a unique identifier to the name prefix to guarantee that all created
// VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
@@ -109,10 +125,16 @@ public class VcnGatewayConnectionConfigTest {
TUNNEL_CONNECTION_PARAMS);
}
- private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
- VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCapsAndOptions(
+ VcnGatewayConnectionConfig.Builder builder,
+ Set<Integer> gatewayOptions,
+ int... exposedCaps) {
builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ for (int option : gatewayOptions) {
+ builder.addGatewayOption(option);
+ }
+
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
@@ -120,11 +142,28 @@ public class VcnGatewayConnectionConfigTest {
return builder.build();
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+ VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ return buildTestConfigWithExposedCapsAndOptions(
+ builder, Collections.emptySet(), exposedCaps);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
}
+ private static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ VcnGatewayConnectionConfig.Builder builder, Set<Integer> gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(builder, gatewayOptions, EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithGatewayOptions(
+ Set<Integer> gatewayOptions) {
+ return buildTestConfigWithExposedCapsAndOptions(newBuilder(), gatewayOptions, EXPOSED_CAPS);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -211,6 +250,15 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testBuilderRequiresValidOption() {
+ try {
+ newBuilder().addGatewayOption(-1);
+ fail("Expected exception due to the invalid VCN gateway option");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
@@ -225,6 +273,20 @@ public class VcnGatewayConnectionConfigTest {
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
+
+ assertFalse(
+ config.hasGatewayOption(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY));
+ }
+
+ @Test
+ public void testBuilderAndGettersWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ for (int option : GATEWAY_OPTIONS) {
+ assertTrue(config.hasGatewayOption(option));
+ }
}
@Test
@@ -235,6 +297,14 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testPersistableBundleWithOptions() {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithGatewayOptions(GATEWAY_OPTIONS);
+
+ assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
+ }
+
+ @Test
public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
@@ -318,4 +388,27 @@ public class VcnGatewayConnectionConfigTest {
assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
assertNotEquals(config, configNotEqual);
}
+
+ private static VcnGatewayConnectionConfig buildConfigWithGatewayOptionsForEqualityTest(
+ Set<Integer> gatewayOptions) {
+ return buildTestConfigWithGatewayOptions(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildConfigWithGatewayOptionsForEqualityTest", TUNNEL_CONNECTION_PARAMS),
+ gatewayOptions);
+ }
+
+ @Test
+ public void testVcnGatewayOptionsEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(GATEWAY_OPTIONS);
+
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildConfigWithGatewayOptionsForEqualityTest(Collections.emptySet());
+
+ assertEquals(config, configEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 19df3c75266c..81814b67f5ee 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -19,6 +19,7 @@ package android.net.vcn;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.net.NetworkCapabilities.REDACT_NONE;
+import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
@@ -37,11 +38,14 @@ import java.util.Arrays;
public class VcnTransportInfoTest {
private static final int SUB_ID = 1;
private static final int NETWORK_ID = 5;
+ private static final int MIN_UDP_PORT_4500_NAT_TIMEOUT = 120;
private static final WifiInfo WIFI_INFO =
new WifiInfo.Builder().setNetworkId(NETWORK_ID).build();
- private static final VcnTransportInfo CELL_UNDERLYING_INFO = new VcnTransportInfo(SUB_ID);
- private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
+ private static final VcnTransportInfo CELL_UNDERLYING_INFO =
+ new VcnTransportInfo(SUB_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT);
+ private static final VcnTransportInfo WIFI_UNDERLYING_INFO =
+ new VcnTransportInfo(WIFI_INFO, MIN_UDP_PORT_4500_NAT_TIMEOUT);
@Test
public void testGetWifiInfo() {
@@ -58,6 +62,16 @@ public class VcnTransportInfoTest {
}
@Test
+ public void testGetMinUdpPort4500NatTimeoutSeconds() {
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT,
+ CELL_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds());
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT,
+ WIFI_UNDERLYING_INFO.getMinUdpPort4500NatTimeoutSeconds());
+ }
+
+ @Test
public void testMakeCopyRedactForNetworkSettings() {
for (VcnTransportInfo info : Arrays.asList(CELL_UNDERLYING_INFO, WIFI_UNDERLYING_INFO)) {
assertEquals(
@@ -67,6 +81,10 @@ public class VcnTransportInfoTest {
assertNull(
((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
.getWifiInfo());
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET,
+ ((VcnTransportInfo) info.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+ .getMinUdpPort4500NatTimeoutSeconds());
}
}
@@ -77,9 +95,17 @@ public class VcnTransportInfoTest {
((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
.getSubId());
assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT,
+ ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getMinUdpPort4500NatTimeoutSeconds());
+ assertEquals(
WifiConfiguration.INVALID_NETWORK_ID,
((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
.getWifiInfo().getNetworkId());
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT,
+ ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_ACCESS_FINE_LOCATION))
+ .getMinUdpPort4500NatTimeoutSeconds());
}
@Test
@@ -110,8 +136,12 @@ public class VcnTransportInfoTest {
public void testParcelNotRedactedForSysUi() {
VcnTransportInfo cellRedacted = parcelForSysUi(CELL_UNDERLYING_INFO);
assertEquals(SUB_ID, cellRedacted.getSubId());
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT, cellRedacted.getMinUdpPort4500NatTimeoutSeconds());
VcnTransportInfo wifiRedacted = parcelForSysUi(WIFI_UNDERLYING_INFO);
assertEquals(NETWORK_ID, wifiRedacted.getWifiInfo().getNetworkId());
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT, wifiRedacted.getMinUdpPort4500NatTimeoutSeconds());
}
private VcnTransportInfo parcelForSysUi(VcnTransportInfo vcnTransportInfo) {
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 3b201f9d20dd..9f7d2390938f 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -16,6 +16,9 @@
package android.net.vcn.persistablebundleutils;
+import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF;
+import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION;
+import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static android.telephony.TelephonyManager.APPTYPE_USIM;
@@ -134,15 +137,35 @@ public class IkeSessionParamsUtilsTest {
verifyPersistableBundleEncodeDecodeIsLossless(params);
}
+ private static IkeSessionParams.Builder createBuilderMinimumWithEap() throws Exception {
+ final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
+
+ final byte[] eapId = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+ final int subId = 1;
+ final EapSessionConfig eapConfig =
+ new EapSessionConfig.Builder()
+ .setEapIdentity(eapId)
+ .setEapSimConfig(subId, APPTYPE_USIM)
+ .setEapAkaConfig(subId, APPTYPE_USIM)
+ .build();
+ return createBuilderMinimum().setAuthEap(serverCaCert, eapConfig);
+ }
+
@Test
public void testEncodeDecodeParamsWithIkeOptions() throws Exception {
- final IkeSessionParams params =
- createBuilderMinimum()
+ final IkeSessionParams.Builder builder =
+ createBuilderMinimumWithEap()
.addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH)
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
.addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT)
- .build();
- verifyPersistableBundleEncodeDecodeIsLossless(params);
+ .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY)
+ .addIkeOption(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION)
+ .addIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES)
+ .addIkeOption(IKE_OPTION_AUTOMATIC_KEEPALIVE_ON_OFF);
+
+ verifyPersistableBundleEncodeDecodeIsLossless(builder.build());
}
private static InputStream openAssetsFile(String fileName) throws Exception {
@@ -176,19 +199,7 @@ public class IkeSessionParamsUtilsTest {
@Test
public void testEncodeRecodeParamsWithEapAuth() throws Exception {
- final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
-
- final byte[] eapId = "test@android.net".getBytes(StandardCharsets.US_ASCII);
- final int subId = 1;
- final EapSessionConfig eapConfig =
- new EapSessionConfig.Builder()
- .setEapIdentity(eapId)
- .setEapSimConfig(subId, APPTYPE_USIM)
- .setEapAkaConfig(subId, APPTYPE_USIM)
- .build();
-
- final IkeSessionParams params =
- createBuilderMinimum().setAuthEap(serverCaCert, eapConfig).build();
+ final IkeSessionParams params = createBuilderMinimumWithEap().build();
verifyPersistableBundleEncodeDecodeIsLossless(params);
}
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index f924b2e9b932..960b57cb632a 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -17,10 +17,14 @@
package com.android.server;
import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -65,7 +69,6 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -95,6 +98,7 @@ import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
+import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import org.junit.Before;
import org.junit.Test;
@@ -114,6 +118,7 @@ import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
+ private static final String CONTEXT_ATTRIBUTION_TAG = "VCN";
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
@@ -125,6 +130,15 @@ public class VcnManagementServiceTest {
private static final VcnConfig TEST_VCN_CONFIG;
private static final VcnConfig TEST_VCN_CONFIG_PKG_2;
private static final int TEST_UID = Process.FIRST_APPLICATION_UID;
+ private static final String TEST_IFACE_NAME = "TEST_IFACE";
+ private static final String TEST_IFACE_NAME_2 = "TEST_IFACE2";
+ private static final LinkProperties TEST_LP_1 = new LinkProperties();
+ private static final LinkProperties TEST_LP_2 = new LinkProperties();
+
+ static {
+ TEST_LP_1.setInterfaceName(TEST_IFACE_NAME);
+ TEST_LP_2.setInterfaceName(TEST_IFACE_NAME_2);
+ }
static {
final Context mockConfigContext = mock(Context.class);
@@ -164,6 +178,7 @@ public class VcnManagementServiceTest {
0 /* carrierId */,
0 /* profileClass */);
+ private final Context mMockContextWithoutAttributionTag = mock(Context.class);
private final Context mMockContext = mock(Context.class);
private final VcnManagementService.Dependencies mMockDeps =
mock(VcnManagementService.Dependencies.class);
@@ -189,6 +204,10 @@ public class VcnManagementServiceTest {
private final IBinder mMockIBinder = mock(IBinder.class);
public VcnManagementServiceTest() throws Exception {
+ doReturn(mMockContext)
+ .when(mMockContextWithoutAttributionTag)
+ .createAttributionContext(CONTEXT_ATTRIBUTION_TAG);
+
setupSystemService(
mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
setupSystemService(
@@ -236,7 +255,7 @@ public class VcnManagementServiceTest {
doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk();
setupMockedCarrierPrivilege(true);
- mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+ mVcnMgmtSvc = new VcnManagementService(mMockContextWithoutAttributionTag, mMockDeps);
setupActiveSubscription(TEST_UUID_1);
doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
@@ -252,6 +271,10 @@ public class VcnManagementServiceTest {
.when(mMockContext)
.enforceCallingOrSelfPermission(
eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ doReturn(Collections.singleton(TRANSPORT_WIFI))
+ .when(mMockDeps)
+ .getRestrictedTransports(any(), any(), any());
}
@@ -637,8 +660,7 @@ public class VcnManagementServiceTest {
final BroadcastReceiver receiver = getPackageChangeReceiver();
verify(mMockContext).registerReceiver(any(), argThat(filter -> {
- return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
- && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
+ return filter.hasAction(Intent.ACTION_PACKAGE_REMOVED);
}), any(), any());
receiver.onReceive(mMockContext, new Intent(Intent.ACTION_PACKAGE_REMOVED));
@@ -1028,70 +1050,232 @@ public class VcnManagementServiceTest {
setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
return mVcnMgmtSvc.getUnderlyingNetworkPolicy(
- getNetworkCapabilitiesBuilderForTransport(subId, transport).build(),
- new LinkProperties());
+ getNetworkCapabilitiesBuilderForTransport(subId, transport).build(), TEST_LP_1);
+ }
+
+ private void checkGetRestrictedTransportsFromCarrierConfig(
+ ParcelUuid subGrp,
+ TelephonySubscriptionSnapshot lastSnapshot,
+ Set<Integer> expectedTransports) {
+ Set<Integer> result =
+ new VcnManagementService.Dependencies()
+ .getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot);
+ assertEquals(expectedTransports, result);
}
@Test
- public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
+ public void testGetRestrictedTransportsFromCarrierConfig() {
+ final Set<Integer> restrictedTransports = new ArraySet<>();
+ restrictedTransports.add(TRANSPORT_CELLULAR);
+ restrictedTransports.add(TRANSPORT_WIFI);
+
+ PersistableBundle carrierConfigBundle = new PersistableBundle();
+ carrierConfigBundle.putIntArray(
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ restrictedTransports.stream().mapToInt(i -> i).toArray());
+ final PersistableBundleWrapper carrierConfig =
+ new PersistableBundleWrapper(carrierConfigBundle);
+
+ final TelephonySubscriptionSnapshot lastSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
+
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
+ }
+
+ @Test
+ public void testGetRestrictedTransportsFromCarrierConfig_noRestrictPolicyConfigured() {
+ final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI);
+
+ final PersistableBundleWrapper carrierConfig =
+ new PersistableBundleWrapper(new PersistableBundle());
+ final TelephonySubscriptionSnapshot lastSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
+
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
+ }
+
+ @Test
+ public void testGetRestrictedTransportsFromCarrierConfig_noCarrierConfig() {
+ final Set<Integer> restrictedTransports = Collections.singleton(TRANSPORT_WIFI);
+
+ final TelephonySubscriptionSnapshot lastSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+
+ checkGetRestrictedTransportsFromCarrierConfig(
+ TEST_UUID_2, lastSnapshot, restrictedTransports);
+ }
+
+ @Test
+ public void testGetRestrictedTransportsFromCarrierConfigAndVcnConfig() {
+ // Configure restricted transport in CarrierConfig
+ final Set<Integer> restrictedTransportInCarrierConfig =
+ Collections.singleton(TRANSPORT_WIFI);
+
+ PersistableBundle carrierConfigBundle = new PersistableBundle();
+ carrierConfigBundle.putIntArray(
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ restrictedTransportInCarrierConfig.stream().mapToInt(i -> i).toArray());
+ final PersistableBundleWrapper carrierConfig =
+ new PersistableBundleWrapper(carrierConfigBundle);
+
+ final TelephonySubscriptionSnapshot lastSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ doReturn(carrierConfig).when(lastSnapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
+
+ // Configure restricted transport in VcnConfig
+ final Context mockContext = mock(Context.class);
+ doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName();
+ final VcnConfig vcnConfig =
+ VcnConfigTest.buildTestConfig(
+ mockContext, Collections.singleton(TRANSPORT_CELLULAR));
+
+ // Verifications
+ final Set<Integer> expectedTransports = new ArraySet<>();
+ expectedTransports.add(TRANSPORT_CELLULAR);
+ expectedTransports.add(TRANSPORT_WIFI);
+
+ Set<Integer> result =
+ new VcnManagementService.Dependencies()
+ .getRestrictedTransports(TEST_UUID_2, lastSnapshot, vcnConfig);
+ assertEquals(expectedTransports, result);
+ }
+
+ private void checkGetUnderlyingNetworkPolicy(
+ int transportType,
+ boolean isTransportRestricted,
+ boolean isActive,
+ boolean expectVcnManaged,
+ boolean expectRestricted)
+ throws Exception {
+
+ final Set<Integer> restrictedTransports = new ArraySet();
+ if (isTransportRestricted) {
+ restrictedTransports.add(transportType);
+ }
+ doReturn(restrictedTransports).when(mMockDeps).getRestrictedTransports(any(), any(), any());
+
final VcnUnderlyingNetworkPolicy policy =
startVcnAndGetPolicyForTransport(
- TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR);
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, isActive, transportType);
assertFalse(policy.isTeardownRequested());
verifyMergedNetworkCapabilities(
policy.getMergedNetworkCapabilities(),
+ transportType,
+ expectVcnManaged,
+ expectRestricted);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy_unrestrictCell() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
TRANSPORT_CELLULAR,
- true /* isVcnManaged */,
- false /* isRestricted */);
+ false /* isTransportRestricted */,
+ true /* isActive */,
+ true /* expectVcnManaged */,
+ false /* expectRestricted */);
}
@Test
- public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception {
- final VcnUnderlyingNetworkPolicy policy =
- startVcnAndGetPolicyForTransport(
- TEST_SUBSCRIPTION_ID,
- TEST_UUID_2,
- false /* isActive */,
- TRANSPORT_CELLULAR);
+ public void testGetUnderlyingNetworkPolicy_unrestrictCellSafeMode() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_CELLULAR,
+ false /* isTransportRestricted */,
+ false /* isActive */,
+ false /* expectVcnManaged */,
+ false /* expectRestricted */);
+ }
- assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilities(
- policy.getMergedNetworkCapabilities(),
- NetworkCapabilities.TRANSPORT_CELLULAR,
- false /* isVcnManaged */,
- false /* isRestricted */);
+ @Test
+ public void testGetUnderlyingNetworkPolicy_restrictCell() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_CELLULAR,
+ true /* isTransportRestricted */,
+ true /* isActive */,
+ true /* expectVcnManaged */,
+ true /* expectRestricted */);
}
@Test
- public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
- final VcnUnderlyingNetworkPolicy policy =
- startVcnAndGetPolicyForTransport(
- TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI);
+ public void testGetUnderlyingNetworkPolicy_restrictCellSafeMode() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_CELLULAR,
+ true /* isTransportRestricted */,
+ false /* isActive */,
+ false /* expectVcnManaged */,
+ false /* expectRestricted */);
+ }
- assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilities(
- policy.getMergedNetworkCapabilities(),
- NetworkCapabilities.TRANSPORT_WIFI,
- true /* isVcnManaged */,
- true /* isRestricted */);
+ @Test
+ public void testGetUnderlyingNetworkPolicy_unrestrictWifi() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_WIFI,
+ false /* isTransportRestricted */,
+ true /* isActive */,
+ true /* expectVcnManaged */,
+ false /* expectRestricted */);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy_unrestrictWifiSafeMode() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_WIFI,
+ false /* isTransportRestricted */,
+ false /* isActive */,
+ false /* expectVcnManaged */,
+ false /* expectRestricted */);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy_restrictWifi() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_WIFI,
+ true /* isTransportRestricted */,
+ true /* isActive */,
+ true /* expectVcnManaged */,
+ true /* expectRestricted */);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicy_restrictWifiSafeMode() throws Exception {
+ checkGetUnderlyingNetworkPolicy(
+ TRANSPORT_WIFI,
+ true /* isTransportRestricted */,
+ false /* isActive */,
+ false /* expectVcnManaged */,
+ true /* expectRestricted */);
}
@Test
- public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception {
+ public void testGetUnderlyingNetworkPolicyCell_restrictWifi() throws Exception {
+ doReturn(Collections.singleton(TRANSPORT_WIFI))
+ .when(mMockDeps)
+ .getRestrictedTransports(any(), any(), any());
+
+ setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isVcnActive */);
+
+ // Get the policy for a cellular network and expect it won't be affected by the wifi
+ // restriction policy
final VcnUnderlyingNetworkPolicy policy =
- startVcnAndGetPolicyForTransport(
- TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+ getNetworkCapabilitiesBuilderForTransport(
+ TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR)
+ .build(),
+ new LinkProperties());
assertFalse(policy.isTeardownRequested());
verifyMergedNetworkCapabilities(
policy.getMergedNetworkCapabilities(),
- NetworkCapabilities.TRANSPORT_WIFI,
- false /* isVcnManaged */,
- true /* isRestricted */);
+ TRANSPORT_CELLULAR,
+ true /* expectVcnManaged */,
+ false /* expectRestricted */);
}
- private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
+ private void setupTrackedNetwork(NetworkCapabilities caps, LinkProperties lp) {
mVcnMgmtSvc.systemReady();
final ArgumentCaptor<NetworkCallback> captor =
@@ -1100,7 +1284,10 @@ public class VcnManagementServiceTest {
.registerNetworkCallback(
eq(new NetworkRequest.Builder().clearCapabilities().build()),
captor.capture());
- captor.getValue().onCapabilitiesChanged(mock(Network.class, CALLS_REAL_METHODS), caps);
+
+ Network mockNetwork = mock(Network.class, CALLS_REAL_METHODS);
+ captor.getValue().onCapabilitiesChanged(mockNetwork, caps);
+ captor.getValue().onLinkPropertiesChanged(mockNetwork, lp);
}
@Test
@@ -1110,7 +1297,7 @@ public class VcnManagementServiceTest {
getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
.removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
.build();
- setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+ setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
// Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED
// immutable capability
@@ -1119,7 +1306,7 @@ public class VcnManagementServiceTest {
getNetworkCapabilitiesBuilderForTransport(
TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
.build(),
- new LinkProperties());
+ TEST_LP_1);
assertTrue(policy.isTeardownRequested());
}
@@ -1129,7 +1316,7 @@ public class VcnManagementServiceTest {
final NetworkCapabilities existingNetworkCaps =
getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
.build();
- setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+ setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
final VcnUnderlyingNetworkPolicy policy =
startVcnAndGetPolicyForTransport(
@@ -1139,6 +1326,27 @@ public class VcnManagementServiceTest {
}
@Test
+ public void testGetUnderlyingNetworkPolicyForRestrictedImsWhenUnrestrictingCell()
+ throws Exception {
+ final NetworkCapabilities existingNetworkCaps =
+ getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NET_CAPABILITY_IMS)
+ .build();
+ setupTrackedNetwork(existingNetworkCaps, TEST_LP_1);
+
+ final VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+ getNetworkCapabilitiesBuilderForTransport(
+ TEST_SUBSCRIPTION_ID, TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_IMS)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build(),
+ new LinkProperties());
+ assertFalse(policy.isTeardownRequested());
+ }
+
+ @Test
public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */);
@@ -1146,7 +1354,7 @@ public class VcnManagementServiceTest {
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_2))
+ .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2))
.build();
VcnUnderlyingNetworkPolicy policy =
@@ -1156,6 +1364,38 @@ public class VcnManagementServiceTest {
assertEquals(nc, policy.getMergedNetworkCapabilities());
}
+ /**
+ * Checks that networks with similar capabilities do not clobber each other.
+ *
+ * <p>In previous iterations, the VcnMgmtSvc used capability-matching to check if a network
+ * undergoing policy checks were the same as an existing networks. However, this meant that if
+ * there were newly added capabilities that the VCN did not check, two networks differing only
+ * by that capability would restart each other constantly.
+ */
+ @Test
+ public void testGetUnderlyingNetworkPolicySimilarNetworks() throws Exception {
+ NetworkCapabilities nc1 =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .setSubscriptionIds(Collections.singleton(TEST_SUBSCRIPTION_ID_2))
+ .build();
+
+ NetworkCapabilities nc2 =
+ new NetworkCapabilities.Builder(nc1)
+ .addCapability(NET_CAPABILITY_ENTERPRISE)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build();
+
+ setupTrackedNetwork(nc1, TEST_LP_1);
+
+ VcnUnderlyingNetworkPolicy policy = mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc2, TEST_LP_2);
+
+ assertFalse(policy.isTeardownRequested());
+ assertEquals(nc2, policy.getMergedNetworkCapabilities());
+ }
+
@Test(expected = SecurityException.class)
public void testGetUnderlyingNetworkPolicyInvalidPermission() {
doReturn(PackageManager.PERMISSION_DENIED)
@@ -1192,6 +1432,23 @@ public class VcnManagementServiceTest {
}
@Test
+ public void testVcnConfigChangeUpdatesPolicyListener() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ final Context mockContext = mock(Context.class);
+ doReturn(TEST_PACKAGE_NAME).when(mockContext).getOpPackageName();
+ final VcnConfig vcnConfig =
+ VcnConfigTest.buildTestConfig(
+ mockContext, Collections.singleton(TRANSPORT_CELLULAR));
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, vcnConfig, TEST_PACKAGE_NAME);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
public void testRemoveVcnUpdatesPolicyListener() throws Exception {
setupActiveSubscription(TEST_UUID_2);
@@ -1218,6 +1475,30 @@ public class VcnManagementServiceTest {
verify(mMockPolicyListener).onPolicyChanged();
}
+ @Test
+ public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ final TelephonySubscriptionSnapshot snapshot =
+ buildSubscriptionSnapshot(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ Collections.singleton(TEST_UUID_2),
+ Collections.emptyMap(),
+ true /* hasCarrierPrivileges */);
+
+ final PersistableBundleWrapper mockCarrierConfig = mock(PersistableBundleWrapper.class);
+ doReturn(mockCarrierConfig).when(snapshot).getCarrierConfigForSubGrp(eq(TEST_UUID_2));
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ cb.onNewSnapshot(snapshot);
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
private void triggerVcnSafeMode(
@NonNull ParcelUuid subGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 09080be9ee41..34f884b94296 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -16,9 +16,9 @@
package com.android.server.vcn;
-import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX;
-import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
@@ -39,6 +39,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -139,6 +140,8 @@ public class TelephonySubscriptionTrackerTest {
@NonNull private TelephonySubscriptionTrackerCallback mCallback;
@NonNull private TelephonySubscriptionTracker mTelephonySubscriptionTracker;
+ @NonNull private CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener;
+
public TelephonySubscriptionTrackerTest() {
mContext = mock(Context.class);
mTestLooper = new TestLooper();
@@ -169,7 +172,7 @@ public class TelephonySubscriptionTrackerTest {
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
doReturn(TEST_CARRIER_CONFIG)
.when(mCarrierConfigManager)
- .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
// subId 1, 2 are in same subGrp, only subId 1 is active
doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -185,9 +188,15 @@ public class TelephonySubscriptionTrackerTest {
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
+ // Capture CarrierConfigChangeListener to emulate the carrier config change notification
+ ArgumentCaptor<CarrierConfigManager.CarrierConfigChangeListener> listenerArgumentCaptor =
+ ArgumentCaptor.forClass(CarrierConfigManager.CarrierConfigChangeListener.class);
mTelephonySubscriptionTracker =
new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
mTelephonySubscriptionTracker.register();
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(),
+ listenerArgumentCaptor.capture());
+ mCarrierConfigChangeListener = listenerArgumentCaptor.getAllValues().get(0);
doReturn(true).when(mDeps).isConfigForIdentifiedCarrier(any());
doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
@@ -235,14 +244,11 @@ public class TelephonySubscriptionTrackerTest {
return intent;
}
- private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
- Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
- intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
- intent.putExtra(
- EXTRA_SUBSCRIPTION_INDEX,
- hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID);
-
- return intent;
+ private void sendCarrierConfigChange(boolean hasValidSubscription) {
+ mCarrierConfigChangeListener.onCarrierConfigChanged(
+ TEST_SIM_SLOT_INDEX,
+ hasValidSubscription ? TEST_SUBSCRIPTION_ID_1 : INVALID_SUBSCRIPTION_ID,
+ TelephonyManager.UNKNOWN_CARRIER_ID, TelephonyManager.UNKNOWN_CARRIER_ID);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
@@ -298,14 +304,15 @@ public class TelephonySubscriptionTrackerTest {
any(),
eq(mHandler));
final IntentFilter filter = getIntentFilter();
- assertEquals(2, filter.countActions());
- assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+ assertEquals(1, filter.countActions());
assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
verify(mSubscriptionManager)
.addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
assertNotNull(getOnSubscriptionsChangedListener());
+ verify(mCarrierConfigManager).registerCarrierConfigChangeListener(any(), any());
+
verify(mTelephonyManager, times(2))
.registerCarrierPrivilegesCallback(anyInt(), any(HandlerExecutor.class), any());
verify(mTelephonyManager)
@@ -438,7 +445,7 @@ public class TelephonySubscriptionTrackerTest {
@Test
public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
@@ -450,7 +457,7 @@ public class TelephonySubscriptionTrackerTest {
.when(mSubscriptionManager)
.getAllSubscriptionInfoList();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
// Expect an empty snapshot
@@ -461,7 +468,7 @@ public class TelephonySubscriptionTrackerTest {
public void testReceiveBroadcast_SlotCleared() throws Exception {
setupReadySubIds();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ sendCarrierConfigChange(false /* hasValidSubscription */);
mTestLooper.dispatchAll();
verifyNoActiveSubscriptions();
@@ -472,7 +479,7 @@ public class TelephonySubscriptionTrackerTest {
public void testReceiveBroadcast_ConfigNotReady() throws Exception {
doReturn(false).when(mDeps).isConfigForIdentifiedCarrier(any());
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
// No interactions expected; config was not loaded
@@ -481,27 +488,58 @@ public class TelephonySubscriptionTrackerTest {
@Test
public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
assertNotNull(
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
}
@Test
+ public void testCarrierConfigUpdatedAfterValidTriggersCallbacks() throws Exception {
+ sendCarrierConfigChange(true /* hasValidSubscription */);
+ mTestLooper.dispatchAll();
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
+ reset(mCallback);
+
+ final PersistableBundle updatedConfig = new PersistableBundle();
+ updatedConfig.putIntArray(
+ VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY,
+ new int[] {TRANSPORT_WIFI, TRANSPORT_CELLULAR});
+ doReturn(updatedConfig)
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1), any());
+
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
+ subIdToCarrierConfigMap.put(
+ TEST_SUBSCRIPTION_ID_1, new PersistableBundleWrapper(updatedConfig));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
+ mTestLooper.dispatchAll();
+
+ verify(mCallback)
+ .onNewSnapshot(
+ eq(
+ buildExpectedSnapshot(
+ 0,
+ TEST_SUBID_TO_INFO_MAP,
+ subIdToCarrierConfigMap,
+ TEST_PRIVILEGED_PACKAGES)));
+ }
+
+ @Test
public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
+ sendCarrierConfigChange(true /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
assertNotNull(
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
- mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
+ sendCarrierConfigChange(false /* hasValidSubscription */);
mTestLooper.dispatchAll();
verify(mCallback)
.onNewSnapshot(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 15d4f1097108..89271e1218d8 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -25,6 +25,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
+import static android.net.vcn.VcnGatewayConnectionConfigTest.MIN_UDP_PORT_4500_NAT_TIMEOUT;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
@@ -50,23 +51,30 @@ import static org.mockito.Mockito.when;
import static java.util.Collections.singletonList;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeSessionConnectionInfo;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager.VcnErrorCode;
+import android.net.vcn.VcnTransportInfo;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import com.android.server.vcn.util.MtuUtils;
import org.junit.Before;
@@ -86,8 +94,11 @@ import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
+ private static final int PARALLEL_SA_COUNT = 4;
+
private VcnIkeSession mIkeSession;
private VcnNetworkAgent mNetworkAgent;
+ private Network mVcnNetwork;
@Before
public void setUp() throws Exception {
@@ -98,6 +109,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
.when(mDeps)
.newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any());
+ mVcnNetwork = mock(Network.class);
+ doReturn(mVcnNetwork).when(mNetworkAgent).getNetwork();
+
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
@@ -166,19 +180,82 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
+ private void verifyDataStallTriggersMigration(
+ UnderlyingNetworkRecord networkRecord,
+ Network networkWithDataStall,
+ boolean expectMobilityUpdate)
+ throws Exception {
+ mGatewayConnection.setUnderlyingNetwork(networkRecord);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ final DataStallReport report =
+ new DataStallReport(
+ networkWithDataStall,
+ 1234 /* reportTimestamp */,
+ 1 /* detectionMethod */,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ new PersistableBundle());
+
+ mGatewayConnection.getConnectivityDiagnosticsCallback().onDataStallSuspected(report);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ if (expectMobilityUpdate) {
+ verify(mIkeSession).setNetwork(networkRecord.network);
+ } else {
+ verify(mIkeSession, never()).setNetwork(any(Network.class));
+ }
+ }
+
+ @Test
+ public void testDataStallTriggersMigration() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1, mVcnNetwork, true /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenOnOtherNetwork() throws Exception {
+ verifyDataStallTriggersMigration(
+ TEST_UNDERLYING_NETWORK_RECORD_1,
+ mock(Network.class),
+ false /* expectMobilityUpdate */);
+ }
+
+ @Test
+ public void testDataStallWontTriggerMigrationWhenUnderlyingNetworkLost() throws Exception {
+ verifyDataStallTriggersMigration(
+ null /* networkRecord */, mock(Network.class), false /* expectMobilityUpdate */);
+ }
+
private void verifyVcnTransformsApplied(
VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform)
throws Exception {
+ verifyVcnTransformsApplied(
+ vcnGatewayConnection,
+ expectForwardTransform,
+ Collections.singletonList(getChildSessionCallback()));
+ }
+
+ private void verifyVcnTransformsApplied(
+ VcnGatewayConnection vcnGatewayConnection,
+ boolean expectForwardTransform,
+ List<VcnChildSessionCallback> callbacks)
+ throws Exception {
for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) {
- getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onIpSecTransformCreated(makeDummyIpSecTransform(), direction);
+ }
mTestLooper.dispatchAll();
- verify(mIpSecSvc)
+ verify(mIpSecSvc, times(callbacks.size()))
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any());
}
- verify(mIpSecSvc, expectForwardTransform ? times(1) : never())
+ verify(mIpSecSvc, expectForwardTransform ? times(callbacks.size()) : never())
.applyTunnelModeTransform(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any());
@@ -350,6 +427,12 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
assertTrue(nc.hasCapability(cap));
}
+ assertTrue(nc.getTransportInfo() instanceof VcnTransportInfo);
+ final VcnTransportInfo vcnTransportInfo = (VcnTransportInfo) nc.getTransportInfo();
+ assertEquals(
+ MIN_UDP_PORT_4500_NAT_TIMEOUT,
+ vcnTransportInfo.getMinUdpPort4500NatTimeoutSeconds());
+
// Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
// canceled
triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
@@ -358,6 +441,89 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
verifySafeModeStateAndCallbackFired(1 /* invocationCount */, false /* isInSafeMode */);
}
+ private List<VcnChildSessionCallback> openChildAndVerifyParallelSasRequested()
+ throws Exception {
+ doReturn(PARALLEL_SA_COUNT)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
+
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ // Verify new child sessions requested
+ final ArgumentCaptor<VcnChildSessionCallback> captor =
+ ArgumentCaptor.forClass(VcnChildSessionCallback.class);
+ verify(mIkeSession, times(PARALLEL_SA_COUNT - 1))
+ .openChildSession(any(TunnelModeChildSessionParams.class), captor.capture());
+
+ return captor.getAllValues();
+ }
+
+ private List<VcnChildSessionCallback> verifyChildOpenedRequestsAndAppliesParallelSas()
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ verifyVcnTransformsApplied(mGatewayConnection, false, callbacks);
+
+ // Mock IKE calling of onOpened()
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onOpened(mock(VcnChildSessionConfiguration.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ return callbacks;
+ }
+
+ @Test
+ public void testChildOpenedWithParallelSas() throws Exception {
+ verifyChildOpenedRequestsAndAppliesParallelSas();
+ }
+
+ @Test
+ public void testOpportunisticSa_ignoresPreOpenFailures() throws Exception {
+ List<VcnChildSessionCallback> callbacks = openChildAndVerifyParallelSasRequested();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ cb.onClosed();
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+ }
+
+ private void verifyPostOpenFailuresCloseSession(boolean shouldCloseWithException)
+ throws Exception {
+ List<VcnChildSessionCallback> callbacks = verifyChildOpenedRequestsAndAppliesParallelSas();
+
+ for (VcnChildSessionCallback cb : callbacks) {
+ if (shouldCloseWithException) {
+ cb.onClosed();
+ } else {
+ cb.onClosedExceptionally(mock(IkeException.class));
+ }
+ }
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosed() throws Exception {
+ verifyPostOpenFailuresCloseSession(false /* shouldCloseWithException */);
+ }
+
+ @Test
+ public void testOpportunisticSa_handlesPostOpenFailures_onClosedExceptionally()
+ throws Exception {
+ verifyPostOpenFailuresCloseSession(true /* shouldCloseWithException */);
+ }
+
@Test
public void testInternalAndDnsAddressesChanged() throws Exception {
final List<LinkAddress> startingInternalAddrs =
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 6a9a1e22cab1..692c8a8f0898 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -24,6 +24,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
@@ -34,20 +35,25 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
import android.net.IpSecManager;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
@@ -64,6 +70,7 @@ import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
import java.util.Arrays;
@@ -71,7 +78,9 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.Executor;
/** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class)
@@ -134,9 +143,9 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH);
capBuilder.setAdministratorUids(new int[] {TEST_UID});
final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS);
- UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
- underlyingNetwork,
- capBuilder.build(), new LinkProperties(), false);
+ UnderlyingNetworkRecord record =
+ getTestNetworkRecord(
+ underlyingNetwork, capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
VcnGatewayConnectionConfigTest.buildTestConfig(),
@@ -202,7 +211,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
doReturn(TEST_DNS_ADDRESSES).when(childSessionConfig).getInternalDnsServers();
UnderlyingNetworkRecord record =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities.Builder().build(),
underlyingLp,
@@ -287,5 +296,60 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
verify(vcnNetworkAgent).unregister();
verifyWakeLockReleased();
+
+ verify(mConnDiagMgr)
+ .unregisterConnectivityDiagnosticsCallback(
+ mGatewayConnection.getConnectivityDiagnosticsCallback());
+ }
+
+ private VcnGatewayConnection buildConnectionWithDataStallHandling(
+ boolean datatStallHandlingEnabled) throws Exception {
+ Set<Integer> options =
+ datatStallHandlingEnabled
+ ? Collections.singleton(
+ VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY)
+ : Collections.emptySet();
+ final VcnGatewayConnectionConfig gatewayConfig =
+ VcnGatewayConnectionConfigTest.buildTestConfigWithGatewayOptions(options);
+ final VcnGatewayConnection gatewayConnection =
+ new VcnGatewayConnection(
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ gatewayConfig,
+ mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
+ mDeps);
+ return gatewayConnection;
+ }
+
+ @Test
+ public void testDataStallHandlingEnabled() throws Exception {
+ final VcnGatewayConnection gatewayConnection =
+ buildConnectionWithDataStallHandling(true /* datatStallHandlingEnabled */);
+
+ final ArgumentCaptor<NetworkRequest> networkRequestCaptor =
+ ArgumentCaptor.forClass(NetworkRequest.class);
+ verify(mConnDiagMgr)
+ .registerConnectivityDiagnosticsCallback(
+ networkRequestCaptor.capture(),
+ any(Executor.class),
+ eq(gatewayConnection.getConnectivityDiagnosticsCallback()));
+
+ final NetworkRequest nr = networkRequestCaptor.getValue();
+ final NetworkRequest expected =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_CELLULAR).build();
+ assertEquals(expected, nr);
+ }
+
+ @Test
+ public void testDataStallHandlingDisabled() throws Exception {
+ buildConnectionWithDataStallHandling(false /* datatStallHandlingEnabled */);
+
+ verify(mConnDiagMgr, never())
+ .registerConnectivityDiagnosticsCallback(
+ any(NetworkRequest.class),
+ any(Executor.class),
+ any(ConnectivityDiagnosticsCallback.class));
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 785bff167ad2..5efbf598f941 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
@@ -35,6 +37,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpSecConfig;
@@ -45,6 +48,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
@@ -108,11 +112,28 @@ public class VcnGatewayConnectionTestBase {
protected static final long ELAPSED_REAL_TIME = 123456789L;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
protected static final String TEST_TCP_BUFFER_SIZES_1 = "1,2,3,4";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
- new NetworkCapabilities(),
+ new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUB_ID))
+ .build(),
new LinkProperties(),
false /* blocked */);
@@ -123,7 +144,7 @@ public class VcnGatewayConnectionTestBase {
protected static final String TEST_TCP_BUFFER_SIZES_2 = "2,3,4,5";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(),
new LinkProperties(),
@@ -157,6 +178,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final ConnectivityDiagnosticsManager mConnDiagMgr;
@NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
@NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
@@ -186,6 +208,13 @@ public class VcnGatewayConnectionTestBase {
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mConnDiagMgr = mock(ConnectivityDiagnosticsManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext,
+ mConnDiagMgr,
+ Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class);
+
mIkeConnectionInfo =
new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
@@ -200,6 +229,9 @@ public class VcnGatewayConnectionTestBase {
doReturn(mWakeLock)
.when(mDeps)
.newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
+ doReturn(1)
+ .when(mDeps)
+ .getParallelTunnelCount(eq(TEST_SUBSCRIPTION_SNAPSHOT), eq(TEST_SUB_GRP));
setUpWakeupMessage(mTeardownTimeoutAlarm, VcnGatewayConnection.TEARDOWN_TIMEOUT_ALARM);
setUpWakeupMessage(mDisconnectRequestAlarm, VcnGatewayConnection.DISCONNECT_REQUEST_ALARM);
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index b0d68952c39d..226604108522 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,6 +16,7 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
@@ -24,8 +25,8 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.calculatePriorityClass;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
@@ -48,6 +49,7 @@ import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -64,6 +66,8 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -91,6 +95,7 @@ public class NetworkPriorityClassifierTest {
private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSignalStrength(WIFI_RSSI)
.setSsid(SSID)
.setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
@@ -102,6 +107,7 @@ public class NetworkPriorityClassifierTest {
private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setSubscriptionIds(Set.of(SUB_ID))
.setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
@@ -135,25 +141,35 @@ public class NetworkPriorityClassifierTest {
false /* isInTestMode */));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
- mWifiNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- WIFI_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
- mCellNetworkRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- CELL_NETWORK_CAPABILITIES,
- LINK_PROPERTIES,
- false /* isBlocked */);
-
setupSystemService(
mockContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
when(mTelephonyManager.getNetworkOperator()).thenReturn(PLMN_ID);
when(mTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+
+ mWifiNetworkRecord =
+ getTestNetworkRecord(
+ WIFI_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ mCellNetworkRecord =
+ getTestNetworkRecord(
+ CELL_NETWORK_CAPABILITIES,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
+ }
+
+ private UnderlyingNetworkRecord getTestNetworkRecord(
+ NetworkCapabilities nc, List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+ return new UnderlyingNetworkRecord(
+ mNetwork,
+ nc,
+ LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null /* currentlySelected */,
+ null /* carrierConfig */);
}
@Test
@@ -490,37 +506,74 @@ public class NetworkPriorityClassifierTest {
mSubscriptionSnapshot));
}
- private void verifyCalculatePriorityClass(
- UnderlyingNetworkRecord networkRecord, int expectedIndex) {
- final int priorityIndex =
- calculatePriorityClass(
+ private void verifyMatchCellWithRequiredCapabilities(
+ VcnCellUnderlyingNetworkTemplate template, boolean expectMatch) {
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
mVcnContext,
- networkRecord,
- VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ template,
+ mCellNetworkRecord,
SUB_GROUP,
mSubscriptionSnapshot,
null /* currentlySelected */,
- null /* carrierConfig */);
+ null /* carrierConfig */));
+ }
- assertEquals(expectedIndex, priorityIndex);
+ @Test
+ public void testMatchCell() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setInternet(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, true /* expectMatch */);
}
@Test
- public void testCalculatePriorityClass() throws Exception {
- verifyCalculatePriorityClass(mCellNetworkRecord, 2);
+ public void testMatchCellFail_RequiredCapabilitiesMissing() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setCbs(MATCH_REQUIRED).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
}
@Test
- public void testCalculatePriorityClassFailToMatchAny() throws Exception {
- final NetworkCapabilities nc =
+ public void testMatchCellFail_ForbiddenCapabilitiesFound() {
+ final VcnCellUnderlyingNetworkTemplate template =
+ getCellNetworkPriorityBuilder().setDun(MATCH_FORBIDDEN).build();
+ verifyMatchCellWithRequiredCapabilities(template, false /* expectMatch */);
+ }
+
+ @Test
+ public void testCalculatePriorityClass() throws Exception {
+ assertEquals(2, mCellNetworkRecord.priorityClass);
+ }
+
+ private void checkCalculatePriorityClassFailToMatchAny(
+ boolean hasInternet, int expectedPriorityClass) throws Exception {
+ final List<VcnUnderlyingNetworkTemplate> templatesRequireDun =
+ Collections.singletonList(
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setDun(MATCH_REQUIRED)
+ .build());
+
+ final NetworkCapabilities.Builder ncBuilder =
new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .setSignalStrength(WIFI_RSSI_LOW)
- .setSsid(SSID)
- .build();
- final UnderlyingNetworkRecord wifiNetworkRecord =
- new UnderlyingNetworkRecord(mNetwork, nc, LINK_PROPERTIES, false /* isBlocked */);
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ if (hasInternet) {
+ ncBuilder.addCapability(NET_CAPABILITY_INTERNET);
+ }
+
+ final UnderlyingNetworkRecord nonDunNetworkRecord =
+ getTestNetworkRecord(ncBuilder.build(), templatesRequireDun);
+
+ assertEquals(expectedPriorityClass, nonDunNetworkRecord.priorityClass);
+ }
- verifyCalculatePriorityClass(wifiNetworkRecord, PRIORITY_ANY);
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny_InternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(true /* hasInternet */, PRIORITY_FALLBACK);
+ }
+
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny_NonInternetNetwork() throws Exception {
+ checkCalculatePriorityClassFailToMatchAny(false /* hasInternet */, PRIORITY_INVALID);
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index fad9669911bb..2941fdea20bb 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -16,18 +16,29 @@
package com.android.server.vcn.routeselection;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnCellUnderlyingNetworkTemplate.MATCH_REQUIRED;
+
import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
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.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,7 +53,10 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -64,7 +78,10 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -95,11 +112,39 @@ public class UnderlyingNetworkControllerTest {
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
+ private static final NetworkCapabilities DUN_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
+ private static final NetworkCapabilities CBS_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+
private static final LinkProperties INITIAL_LINK_PROPERTIES =
getLinkPropertiesWithName("initial_iface");
private static final LinkProperties UPDATED_LINK_PROPERTIES =
getLinkPropertiesWithName("updated_iface");
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_DUN =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setDun(MATCH_REQUIRED)
+ .build();
+
+ private static final VcnCellUnderlyingNetworkTemplate CELL_TEMPLATE_CBS =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setInternet(MATCH_ANY)
+ .setCbs(MATCH_REQUIRED)
+ .build();
+
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
@@ -201,6 +246,107 @@ public class UnderlyingNetworkControllerTest {
any());
}
+ private void verifyRequestBackgroundNetwork(
+ ConnectivityManager cm,
+ int expectedSubId,
+ Set<Integer> expectedRequiredCaps,
+ Set<Integer> expectedForbiddenCaps) {
+ verify(cm)
+ .requestBackgroundNetwork(
+ eq(
+ getCellRequestForSubId(
+ expectedSubId,
+ expectedRequiredCaps,
+ expectedForbiddenCaps)),
+ any(NetworkBringupCallback.class),
+ any());
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupForNonInternetCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setDun(MATCH_REQUIRED)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ networkTemplates.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplateBuilder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY)
+ .build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm, subId, Collections.singleton(NET_CAPABILITY_DUN), Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
+ @Test
+ public void testNetworkCallbacksRegisteredOnStartupWithDedupedtCapabilities() {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ // Build network templates
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ final VcnCellUnderlyingNetworkTemplate.Builder builder =
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMms(MATCH_REQUIRED)
+ .setCbs(MATCH_FORBIDDEN)
+ .setInternet(MATCH_ANY);
+
+ networkTemplates.add(builder.setMetered(MATCH_REQUIRED).build());
+ networkTemplates.add(builder.setMetered(MATCH_FORBIDDEN).build());
+
+ // Start UnderlyingNetworkController
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ // Verifications
+ for (final int subId : INITIAL_SUB_IDS) {
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet());
+ verifyRequestBackgroundNetwork(
+ cm,
+ subId,
+ Collections.singleton(NET_CAPABILITY_MMS),
+ Collections.singleton(NET_CAPABILITY_CBS));
+ }
+ }
+
private void verifyNetworkRequestsRegistered(Set<Integer> expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
@@ -210,8 +356,13 @@ public class UnderlyingNetworkControllerTest {
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
- eq(getCellRequestForSubId(subId)),
- any(NetworkBringupCallback.class), any());
+ eq(
+ getCellRequestForSubId(
+ subId,
+ Collections.singleton(NET_CAPABILITY_INTERNET),
+ Collections.emptySet())),
+ any(NetworkBringupCallback.class),
+ any());
}
verify(mConnectivityManager)
@@ -253,6 +404,7 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.build();
}
@@ -261,6 +413,7 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
@@ -270,16 +423,27 @@ public class UnderlyingNetworkControllerTest {
// TODO (b/187991063): Add tests for carrier-config based thresholds
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.setSubscriptionIds(netCapsSubIds)
.setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
- private NetworkRequest getCellRequestForSubId(int subId) {
- return getExpectedRequestBase()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
- .build();
+ private NetworkRequest getCellRequestForSubId(
+ int subId, Set<Integer> requiredCaps, Set<Integer> forbiddenCaps) {
+ final NetworkRequest.Builder nqBuilder =
+ getExpectedRequestBase()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setNetworkSpecifier(new TelephonyNetworkSpecifier(subId));
+
+ for (int cap : requiredCaps) {
+ nqBuilder.addCapability(cap);
+ }
+ for (int cap : forbiddenCaps) {
+ nqBuilder.addForbiddenCapability(cap);
+ }
+
+ return nqBuilder.build();
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
@@ -301,7 +465,6 @@ public class UnderlyingNetworkControllerTest {
private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -321,16 +484,30 @@ public class UnderlyingNetworkControllerTest {
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
}
+ private static UnderlyingNetworkRecord getTestNetworkRecord(
+ Network network,
+ NetworkCapabilities networkCapabilities,
+ LinkProperties linkProperties,
+ boolean isBlocked) {
+ return new UnderlyingNetworkRecord(
+ network,
+ networkCapabilities,
+ linkProperties,
+ isBlocked,
+ false /* isSelected */,
+ 0 /* priorityClass */);
+ }
+
@Test
public void testUnderlyingNetworkRecordEquals() {
UnderlyingNetworkRecord recordA =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
UnderlyingNetworkRecord recordB =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
@@ -338,12 +515,24 @@ public class UnderlyingNetworkControllerTest {
UnderlyingNetworkRecord recordC =
new UnderlyingNetworkRecord(
mNetwork,
+ INITIAL_NETWORK_CAPABILITIES,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ true /* isSelected */,
+ -1 /* priorityClass */);
+ UnderlyingNetworkRecord recordD =
+ getTestNetworkRecord(
+ mNetwork,
UPDATED_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
assertEquals(recordA, recordB);
- assertNotEquals(recordA, recordC);
+ assertEquals(recordA, recordC);
+ assertNotEquals(recordA, recordD);
+
+ assertTrue(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordB));
+ assertFalse(UnderlyingNetworkRecord.isEqualIncludingPriorities(recordA, recordC));
}
@Test
@@ -366,6 +555,10 @@ public class UnderlyingNetworkControllerTest {
.build();
}
+ private void verifyOnSelectedUnderlyingNetworkChanged(UnderlyingNetworkRecord expectedRecord) {
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ }
+
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
@@ -384,12 +577,12 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
return cb;
}
@@ -402,12 +595,12 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -417,12 +610,12 @@ public class UnderlyingNetworkControllerTest {
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -434,18 +627,16 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -458,18 +649,16 @@ public class UnderlyingNetworkControllerTest {
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
+ getTestNetworkRecord(
mNetwork,
responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
- verify(mNetworkControllerCb, times(1))
- .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(expectedRecord);
}
@Test
@@ -478,13 +667,7 @@ public class UnderlyingNetworkControllerTest {
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
- UnderlyingNetworkRecord expectedRecord =
- new UnderlyingNetworkRecord(
- mNetwork,
- buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
- INITIAL_LINK_PROPERTIES,
- true /* isBlocked */);
- verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verifyOnSelectedUnderlyingNetworkChanged(null);
}
@Test
@@ -520,5 +703,132 @@ public class UnderlyingNetworkControllerTest {
verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
- // TODO (b/187991063): Add tests for network prioritization
+ private UnderlyingNetworkListener setupControllerAndGetNetworkListener(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
+ final ConnectivityManager cm = mock(ConnectivityManager.class);
+ setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
+ new UnderlyingNetworkController(
+ mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(networkTemplates),
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ mNetworkControllerCb);
+
+ verify(cm)
+ .registerNetworkCallback(
+ eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
+ mUnderlyingNetworkListenerCaptor.capture(),
+ any());
+
+ return mUnderlyingNetworkListenerCaptor.getValue();
+ }
+
+ private UnderlyingNetworkRecord bringupNetworkAndGetRecord(
+ UnderlyingNetworkListener cb,
+ NetworkCapabilities requestNetworkCaps,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ UnderlyingNetworkRecord currentlySelected) {
+ final Network network = mock(Network.class);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(requestNetworkCaps, INITIAL_SUB_IDS);
+
+ cb.onAvailable(network);
+ cb.onCapabilitiesChanged(network, responseNetworkCaps);
+ cb.onLinkPropertiesChanged(network, INITIAL_LINK_PROPERTIES);
+ cb.onBlockedStatusChanged(network, false /* isFalse */);
+ return new UnderlyingNetworkRecord(
+ network,
+ responseNetworkCaps,
+ INITIAL_LINK_PROPERTIES,
+ false /* isBlocked */,
+ mVcnContext,
+ underlyingNetworkTemplates,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */);
+ }
+
+ @Test
+ public void testSelectMorePreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ CBS_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, DUN_NETWORK_CAPABILITIES, networkTemplates, cbsNetworkRecord);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+ }
+
+ @Test
+ public void testNeverSelectLessPreferredNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+ networkTemplates.add(CELL_TEMPLATE_DUN);
+ networkTemplates.add(CELL_TEMPLATE_CBS);
+
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up DUN network
+ final UnderlyingNetworkRecord dunNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ DUN_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(dunNetworkRecord));
+
+ // Bring up CBS network
+ final UnderlyingNetworkRecord cbsNetworkRecord =
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, dunNetworkRecord);
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(eq(cbsNetworkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndFallBackToInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ // Bring up an Internet network without DUN capability
+ final UnderlyingNetworkRecord networkRecord =
+ bringupNetworkAndGetRecord(
+ cb,
+ INITIAL_NETWORK_CAPABILITIES,
+ networkTemplates,
+ null /* currentlySelected */);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(networkRecord));
+ }
+
+ @Test
+ public void testFailtoMatchTemplateAndNeverFallBackToNonInternetNetwork() {
+ final List<VcnUnderlyingNetworkTemplate> networkTemplates = new ArrayList();
+
+ networkTemplates.add(
+ new VcnCellUnderlyingNetworkTemplate.Builder().setDun(MATCH_REQUIRED).build());
+ UnderlyingNetworkListener cb = setupControllerAndGetNetworkListener(networkTemplates);
+
+ bringupNetworkAndGetRecord(
+ cb, CBS_NETWORK_CAPABILITIES, networkTemplates, null /* currentlySelected */);
+
+ verify(mNetworkControllerCb, never())
+ .onSelectedUnderlyingNetworkChanged(any(UnderlyingNetworkRecord.class));
+ }
}