summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java2
-rw-r--r--tests/ApkVerityTest/Android.bp15
-rw-r--r--tests/ApkVerityTest/AndroidTest.xml10
-rw-r--r--tests/ApkVerityTest/block_device_writer/Android.bp35
-rw-r--r--tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java5
-rw-r--r--tests/AppLaunchWear/AndroidManifest.xml21
-rw-r--r--tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java864
-rw-r--r--tests/AttestationVerificationTest/Android.bp45
-rwxr-xr-xtests/AttestationVerificationTest/AndroidManifest.xml34
-rw-r--r--tests/AttestationVerificationTest/AndroidTest.xml27
-rw-r--r--tests/AttestationVerificationTest/OWNERS1
-rw-r--r--tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem81
-rw-r--r--tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem30
-rw-r--r--tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem33
-rw-r--r--tests/AttestationVerificationTest/assets/test_root_certs.pem61
-rw-r--r--tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem50
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt161
-rw-r--r--tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt222
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java297
-rw-r--r--tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt175
-rw-r--r--tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java6
-rw-r--r--tests/BatteryStatsPerfTest/AndroidManifest.xml2
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java135
-rw-r--r--tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java129
-rw-r--r--tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java17
-rw-r--r--tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java22
-rw-r--r--tests/BootImageProfileTest/OWNERS2
-rw-r--r--tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java17
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/.project33
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp34
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk45
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java5
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp36
-rw-r--r--tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk38
-rw-r--r--tests/CanvasCompare/Android.bp63
-rw-r--r--tests/CanvasCompare/Android.mk33
-rw-r--r--tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java74
-rw-r--r--tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java3
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/Android.bp60
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/Android.mk95
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/OWNERS1
-rw-r--r--tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java6
-rw-r--r--tests/FlickerTests/AndroidTest.xml4
-rw-r--r--tests/FlickerTests/OWNERS5
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt70
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt31
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt66
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt1
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt60
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt53
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt34
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt24
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt9
-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/CloseImeWindowToAppTest.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt6
-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.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS2
-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.kt6
-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.kt55
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt49
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt52
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt49
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt32
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt71
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt102
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt128
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt126
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt126
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt84
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt197
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt92
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt167
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt99
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt46
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt35
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt74
-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.kt159
-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.kt37
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt61
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt23
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt52
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml72
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_notification.xml24
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml5
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml27
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml49
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java31
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java75
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivityAutoFocus.java6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java46
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java50
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java80
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java33
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java33
-rw-r--r--tests/HandwritingIme/Android.bp (renamed from tests/benchmarks/Android.bp)23
-rw-r--r--tests/HandwritingIme/AndroidManifest.xml35
-rw-r--r--tests/HandwritingIme/res/xml/ime.xml20
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java121
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java168
-rw-r--r--tests/HwAccelerationTest/.project33
-rw-r--r--tests/HwAccelerationTest/Android.bp12
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml18
-rw-r--r--tests/HwAccelerationTest/jni/Android.bp44
-rw-r--r--tests/HwAccelerationTest/jni/native-lib.cpp62
-rw-r--r--tests/HwAccelerationTest/res/drawable-nodpi/scratches.pngbin0 -> 248848 bytes
-rw-r--r--tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpgbin0 -> 706520 bytes
-rw-r--r--tests/HwAccelerationTest/res/layout/pen_stylus.xml20
-rw-r--r--tests/HwAccelerationTest/res/layout/view_runtime_shader.xml205
-rw-r--r--tests/HwAccelerationTest/res/values/styles.xml1
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt138
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java8
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt132
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java2
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt85
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt109
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java6
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java28
-rw-r--r--tests/Input/Android.bp2
-rw-r--r--tests/Input/src/com/android/test/input/AnrTest.kt119
-rw-r--r--tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt110
-rw-r--r--tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt93
-rw-r--r--tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt75
-rw-r--r--tests/InputMethodStressTest/Android.bp36
-rw-r--r--tests/InputMethodStressTest/AndroidManifest.xml29
-rw-r--r--tests/InputMethodStressTest/AndroidTest.xml40
-rw-r--r--tests/InputMethodStressTest/OWNERS3
-rw-r--r--tests/InputMethodStressTest/TEST_MAPPING7
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java92
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java239
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java80
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java160
-rw-r--r--tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenCaptureRule.java69
-rw-r--r--tests/Internal/src/com/android/internal/util/ParcellingTests.java77
-rw-r--r--tests/Internal/src/com/android/internal/util/UserIconsTest.java47
-rw-r--r--tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java11
-rw-r--r--tests/LockTaskTests/Android.bp32
-rw-r--r--tests/LockTaskTests/Android.mk19
-rw-r--r--tests/MultiUser/Android.bp (renamed from tests/AppLaunchWear/Android.bp)17
-rw-r--r--tests/MultiUser/AndroidManifest.xml18
-rw-r--r--tests/MultiUser/TEST_MAPPING7
-rw-r--r--tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java164
-rw-r--r--tests/RollbackTest/Android.bp8
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java935
-rw-r--r--tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java345
-rw-r--r--tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java540
-rw-r--r--tests/SharedLibraryLoadingTest/Android.bp37
-rw-r--r--tests/SharedLibraryLoadingTest/AndroidTest.xml45
-rw-r--r--tests/SharedLibraryLoadingTest/OWNERS2
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp29
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml23
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp35
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml29
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml20
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java90
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp30
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml23
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml20
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java32
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp30
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml23
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml20
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java24
-rw-r--r--tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java32
-rw-r--r--tests/SilkFX/AndroidManifest.xml10
-rw-r--r--tests/SilkFX/res/drawable/background_blur_drawable.xml20
-rw-r--r--tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml20
-rw-r--r--tests/SilkFX/res/layout/activity_background_blur.xml173
-rw-r--r--tests/SilkFX/res/values/style.xml31
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/Main.kt8
-rw-r--r--tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt189
-rw-r--r--tests/SoundTriggerTestApp/OWNERS1
-rw-r--r--tests/SoundTriggerTestApp/res/layout/main.xml8
-rw-r--r--tests/SoundTriggerTestApp/res/values/strings.xml1
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java40
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java70
-rw-r--r--tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java33
-rw-r--r--tests/StagedInstallTest/Android.bp7
-rw-r--r--tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java158
-rw-r--r--tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java70
-rw-r--r--tests/SurfaceViewSyncTest/Android.bp31
-rw-r--r--tests/SurfaceViewSyncTest/AndroidManifest.xml27
-rw-r--r--tests/SurfaceViewSyncTest/OWNERS1
-rw-r--r--tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml47
-rw-r--r--tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java198
-rw-r--r--tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java3
-rw-r--r--tests/TrustTests/Android.bp41
-rw-r--r--tests/TrustTests/AndroidManifest.xml87
-rw-r--r--tests/TrustTests/AndroidTest.xml35
-rw-r--r--tests/TrustTests/OWNERS1
-rw-r--r--tests/TrustTests/README.md40
-rw-r--r--tests/TrustTests/TEST_MAPPING28
-rw-r--r--tests/TrustTests/src/android/trust/BaseTrustAgentService.kt47
-rw-r--r--tests/TrustTests/src/android/trust/TrustTestActivity.kt30
-rw-r--r--tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt104
-rw-r--r--tests/TrustTests/src/android/trust/test/LockUserTest.kt63
-rw-r--r--tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt158
-rw-r--r--tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt110
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt88
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt125
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt110
-rw-r--r--tests/TrustTests/src/android/trust/test/lib/utils.kt51
-rw-r--r--tests/UpdatableSystemFontTest/Android.bp20
-rw-r--r--tests/UpdatableSystemFontTest/AndroidTest.xml20
-rw-r--r--tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java13
-rw-r--r--tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java176
-rw-r--r--tests/UpdatableSystemFontTest/testdata/Android.bp76
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf (renamed from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf)bin1820 -> 1820 bytes
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx (renamed from tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx)0
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttfbin0 -> 1776 bytes
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx196
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttfbin0 -> 1772 bytes
-rw-r--r--tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx196
-rw-r--r--tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java3
-rw-r--r--tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java2
-rw-r--r--tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java60
-rw-r--r--tests/benchmarks/src/com/android/server/net/OWNERS1
-rw-r--r--tests/componentalias/Android.bp87
-rwxr-xr-xtests/componentalias/AndroidManifest.xml25
-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/OWNERS2
-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/ComponentAliasTestCommon.java28
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java44
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/Target00.java21
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/Target01.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/Target02.java19
-rw-r--r--tests/componentalias/src/android/content/componentalias/tests/b/Target03.java19
-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/notification/src/com/android/frameworks/tests/notification/NotificationTests.java10
-rw-r--r--tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java16
-rw-r--r--tests/utils/testutils/Android.bp2
-rw-r--r--tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java1
-rw-r--r--tests/vcn/Android.bp2
-rw-r--r--tests/vcn/AndroidManifest.xml3
-rw-r--r--tests/vcn/OWNERS2
-rw-r--r--tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java122
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java71
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java24
-rw-r--r--tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java111
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java3
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java107
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java145
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java34
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java4
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java6
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java20
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java28
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java526
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java (renamed from tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java)123
-rw-r--r--tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java91
-rw-r--r--tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java91
290 files changed, 14054 insertions, 4159 deletions
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
index fa5724ea64bc..ca909a4c9b77 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
@@ -30,7 +30,7 @@ public class DisableScreenshotsActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setDisablePreviewScreenshots(true);
+ setRecentsScreenshotEnabled(false);
getWindow().getDecorView().setBackgroundColor(Color.RED);
}
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index 4e98f4264e11..f026bea80470 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -24,14 +24,21 @@ package {
java_test_host {
name: "ApkVerityTest",
srcs: ["src/**/*.java"],
- libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
+ libs: [
+ "tradefed",
+ "compatibility-tradefed",
+ "compatibility-host-util",
+ ],
static_libs: [
"block_device_writer_jar",
"frameworks-base-hostutils",
],
- test_suites: ["general-tests", "vts"],
- target_required: [
- "block_device_writer_module",
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ data_device_bins_both: [
+ "block_device_writer",
],
data: [
":ApkVerityTestCertDer",
diff --git a/tests/ApkVerityTest/AndroidTest.xml b/tests/ApkVerityTest/AndroidTest.xml
index 55704eda905e..39b75cc27acb 100644
--- a/tests/ApkVerityTest/AndroidTest.xml
+++ b/tests/ApkVerityTest/AndroidTest.xml
@@ -31,10 +31,18 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
- <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
<option name="push" value="ApkVerityTestCert.der->/data/local/tmp/ApkVerityTestCert.der" />
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <!-- The build system produces both 32 and 64 bit variants with bitness suffix. Let
+ FilePusher find the filename with bitness and push to a remote name without bitness.
+ -->
+ <option name="append-bitness" value="true" />
+ <option name="cleanup" value="true" />
+ <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
+ </target_preparer>
+
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="ApkVerityTest.jar" />
</test>
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index 0b5f0f611916..0002447d17f2 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -24,12 +24,7 @@ package {
}
cc_test {
- // Depending on how the test runs, the executable may be uploaded to different location.
- // Before the bug in the file pusher is fixed, workaround by making the name unique.
- // See b/124718249#comment12.
- name: "block_device_writer_module",
- stem: "block_device_writer",
-
+ name: "block_device_writer",
srcs: ["block_device_writer.cpp"],
cflags: [
"-D_FILE_OFFSET_BITS=64",
@@ -38,31 +33,33 @@ cc_test {
"-Wextra",
"-g",
],
- shared_libs: ["libbase", "libutils"],
- // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
- // the uploader does not pick up the executable from correct output location. The following
- // workaround allows the test to:
- // * upload the 32-bit exectuable for both 32 and 64 bits devices to use
- // * refer to the same executable name in Java
- // * no need to force the Java test to be archiecture specific.
- //
- // See b/145573317 for details.
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+ compile_multilib: "both",
multilib: {
lib32: {
- suffix: "",
+ suffix: "32",
},
lib64: {
- suffix: "64", // not really used
+ suffix: "64",
},
},
auto_gen_config: false,
- test_suites: ["general-tests", "pts", "vts"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
gtest: false,
}
java_library_host {
name: "block_device_writer_jar",
srcs: ["src/**/*.java"],
- libs: ["tradefed", "junit"],
+ libs: [
+ "tradefed",
+ "junit",
+ ],
}
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 5c2c15b22bb0..9be02ec3be86 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -32,11 +32,12 @@ import java.util.ArrayList;
* <p>To use this class, please push block_device_writer binary to /data/local/tmp.
* 1. In Android.bp, add:
* <pre>
- * target_required: ["block_device_writer_module"],
+ * data_device_bins_both: ["block_device_writer"],
* </pre>
* 2. In AndroidText.xml, add:
* <pre>
- * <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ * <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ * <option name="append-bitness" value="true" />
* <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
* </target_preparer>
* </pre>
diff --git a/tests/AppLaunchWear/AndroidManifest.xml b/tests/AppLaunchWear/AndroidManifest.xml
deleted file mode 100644
index 7dfd7bafbaaa..000000000000
--- a/tests/AppLaunchWear/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.applaunch"
- android:sharedUserId="android.uid.system" >
-
- <uses-permission android:name="android.permission.REAL_GET_TASKS" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
- <uses-sdk
- android:minSdkVersion="22"
- android:targetSdkVersion="24" />
-
- <instrumentation android:label="Measure app start up time"
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.tests.applaunch" />
-
- <application android:label="App Launch Test">
- <uses-library android:name="android.test.runner" />
- </application>
-</manifest>
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
deleted file mode 100644
index 97701c61011e..000000000000
--- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.tests.applaunch;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.ActivityManager;
-import android.app.ActivityManager.ProcessErrorStateInfo;
-import android.app.IActivityManager;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.test.InstrumentationTestCase;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
-
-import androidx.test.rule.logging.AtraceLogger;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This test is intended to measure the time it takes for the apps to start.
- * Names of the applications are passed in command line, and the
- * test starts each application, and reports the start up time in milliseconds.
- * The instrumentation expects the following key to be passed on the command line:
- * apps - A list of applications to start and their corresponding result keys
- * in the following format:
- * -e apps <app name>^<result key>|<app name>^<result key>
- */
-public class AppLaunch extends InstrumentationTestCase {
-
- private static final int JOIN_TIMEOUT = 10000;
- private static final String TAG = AppLaunch.class.getSimpleName();
-
- // optional parameter: comma separated list of required account types before proceeding
- // with the app launch
- private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
- private static final String KEY_APPS = "apps";
- private static final String KEY_TRIAL_LAUNCH = "trial_launch";
- private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
- private static final String KEY_LAUNCH_ORDER = "launch_order";
- private static final String KEY_DROP_CACHE = "drop_cache";
- private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
- private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
- private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
- private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
- private static final String KEY_TRACE_DIRECTORY = "trace_directory";
- private static final String KEY_TRACE_CATEGORY = "trace_categories";
- private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
- private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
- private static final String KEY_COMPILER_FILTERS = "compiler_filters";
-
- private static final String SIMPLEPERF_APP_CMD =
- "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
- private static final String WEARABLE_ACTION_GOOGLE =
- "com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
- private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
- private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
- private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
- private static final String LAUNCH_FILE = "applaunch.txt";
- private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
- private static final String DEFAULT_TRACE_CATEGORIES =
- "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
- private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
- private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
- private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
- private static final String DELIMITER = ",";
- private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
- private static final String APP_LAUNCH_CMD = "am start -W -n";
- private static final String SUCCESS_MESSAGE = "Status: ok";
- private static final String WARNING_MESSAGE = "Warning: Activity not started";
- private static final String COMPILE_SUCCESS = "Success";
- private static final String THIS_TIME = "ThisTime:";
- private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
- private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
- private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
- private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
- private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
- private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
- private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
- private static final String SPEED_PROFILE_FILTER = "speed-profile";
- private static final String VERIFY_FILTER = "verify";
- private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
- private static final String WEARABLE_HOME_PACKAGE = "com.google.android.wearable.app";
-
- private Map<String, Intent> mNameToIntent;
- private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
- private Map<String, String> mNameToResultKey;
- private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
- private IActivityManager mAm;
- private String mSimplePerfCmd = null;
- private String mLaunchOrder = null;
- private boolean mDropCache = false;
- private int mLaunchIterations = 10;
- private int mTraceLaunchCount = 0;
- private String mTraceDirectoryStr = null;
- private Bundle mResult = new Bundle();
- private Set<String> mRequiredAccounts;
- private boolean mTrialLaunch = false;
- private BufferedWriter mBufferedWriter = null;
- private boolean mSimplePerfAppOnly = false;
- private String[] mCompilerFilters = null;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
- }
-
- @Override
- protected void tearDown() throws Exception {
- getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
- super.tearDown();
- }
-
- private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
- mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
- }
-
- private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
- List<AppLaunchResult> results =
- mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
- return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
- }
-
- public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
- IOException, InterruptedException {
- InstrumentationTestRunner instrumentation =
- (InstrumentationTestRunner)getInstrumentation();
- Bundle args = instrumentation.getArguments();
- mAm = ActivityManager.getService();
- String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
-
- createMappings();
- parseArgs(args);
- checkAccountSignIn();
-
- // Root directory for applaunch file to log the app launch output
- // Will be useful in case of simpleperf command is used
- File launchRootDir = null;
- if (null != launchDirectory && !launchDirectory.isEmpty()) {
- launchRootDir = new File(launchDirectory);
- if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
- throw new IOException("Unable to create the destination directory");
- }
- }
-
- try {
- File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
-
- if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
- throw new IOException("Unable to create the lauch file sub directory");
- }
- File file = new File(launchSubDir, LAUNCH_FILE);
- FileOutputStream outputStream = new FileOutputStream(file);
- mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
- outputStream));
-
- // Root directory for trace file during the launches
- File rootTrace = null;
- File rootTraceSubDir = null;
- int traceBufferSize = 0;
- int traceDumpInterval = 0;
- Set<String> traceCategoriesSet = null;
- if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) {
- rootTrace = new File(mTraceDirectoryStr);
- if (!rootTrace.exists() && !rootTrace.mkdirs()) {
- throw new IOException("Unable to create the trace directory");
- }
- rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
- if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
- throw new IOException("Unable to create the trace sub directory");
- }
- assertNotNull("Trace iteration parameter is mandatory",
- args.getString(KEY_TRACE_ITERATIONS));
- mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS));
- String traceCategoriesStr = args
- .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES);
- traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE,
- DEFAULT_TRACE_BUFFER_SIZE));
- traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL,
- DEFAULT_TRACE_DUMP_INTERVAL));
- traceCategoriesSet = new HashSet<String>();
- if (!traceCategoriesStr.isEmpty()) {
- String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER);
- for (int i = 0; i < traceCategoriesSplit.length; i++) {
- traceCategoriesSet.add(traceCategoriesSplit[i]);
- }
- }
- }
-
- // Get the app launch order based on launch order, trial launch,
- // launch iterations and trace iterations
- setLaunchOrder();
-
- for (LaunchOrder launch : mLaunchOrderList) {
- if (mNameToIntent.get(launch.getApp()) == null) {
- continue;
- }
- dropCache();
- String appPkgName = mNameToIntent.get(launch.getApp())
- .getComponent().getPackageName();
- Log.v(TAG, String.format("\nApp name: %s", launch.getApp()));
- Log.v(TAG, String.format("Adding app package name: %s", appPkgName));
- // App launch times for trial launch will not be used for final
- // launch time calculations.
- if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
- // In the "applaunch.txt" file, trail launches is referenced using
- // "TRIAL_LAUNCH"
- Log.v(TAG, "Trial Launch");
- if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
- assertTrue(String.format("Not able to compile the app : %s", appPkgName),
- compileApp(VERIFY_FILTER, appPkgName));
- } else if (launch.getCompilerFilter() != null) {
- assertTrue(String.format("Not able to compile the app : %s", appPkgName),
- compileApp(launch.getCompilerFilter(), appPkgName));
- }
- // We only need to run a trial for the speed-profile filter, but we always
- // run one for "applaunch.txt" consistency.
- AppLaunchResult launchResult = null;
- if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
- Log.v(TAG, "Home package detected. Not killing app");
- launchResult = startApp(launch.getApp(), false, launch.getLaunchReason());
- } else {
- Log.v(TAG, "Will kill app before launch");
- launchResult = startApp(launch.getApp(), true, launch.getLaunchReason());
- }
- if (launchResult.mLaunchTime < 0) {
- addLaunchResult(launch, new AppLaunchResult());
- // simply pass the app if launch isn't successful
- // error should have already been logged by startApp
- continue;
- }
- sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
- if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
- // Send SIGUSR1 to force dumping a profile.
- String sendSignalCommand =
- String.format("killall -s SIGUSR1 %s", appPkgName);
- getInstrumentation().getUiAutomation().executeShellCommand(
- sendSignalCommand);
- assertTrue(String.format("Not able to compile the app : %s", appPkgName),
- compileApp(launch.getCompilerFilter(), appPkgName));
- }
- }
-
- // App launch times used for final calculation
- else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
- Log.v(TAG, "Launch iteration prefix.");
- AppLaunchResult launchResults = null;
- if (hasFailureOnFirstLaunch(launch)) {
- // skip if the app has failures while launched first
- continue;
- }
- // In the "applaunch.txt" file app launches are referenced using
- // "LAUNCH_ITERATION - ITERATION NUM"
- if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
- Log.v(TAG, "Home package detected. Not killing app");
- launchResults = startApp(launch.getApp(), false, launch.getLaunchReason());
- } else {
- Log.v(TAG, "Will kill app before launch");
- launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
- }
- if (launchResults.mLaunchTime < 0) {
- addLaunchResult(launch, new AppLaunchResult());
- // if it fails once, skip the rest of the launches
- continue;
- } else {
- addLaunchResult(launch, launchResults);
- }
- sleep(POST_LAUNCH_IDLE_TIMEOUT);
- }
-
- // App launch times for trace launch will not be used for final
- // launch time calculations.
- else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
- Log.v(TAG, "Trace iteration prefix");
- AtraceLogger atraceLogger = AtraceLogger
- .getAtraceLoggerInstance(getInstrumentation());
- // Start the trace
- try {
- atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
- traceDumpInterval, rootTraceSubDir,
- String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
- if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
- Log.v(TAG, "Home package detected. Not killing app");
- startApp(launch.getApp(), false, launch.getLaunchReason());
- } else {
- Log.v(TAG, "Will kill app before launch");
- startApp(launch.getApp(), true, launch.getLaunchReason());
- }
- sleep(POST_LAUNCH_IDLE_TIMEOUT);
- } finally {
- // Stop the trace
- atraceLogger.atraceStop();
- }
- }
- closeApp(launch.getApp(), true);
- sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
- }
- } finally {
- if (null != mBufferedWriter) {
- mBufferedWriter.close();
- }
- }
-
- for (String app : mNameToResultKey.keySet()) {
- for (String compilerFilter : mCompilerFilters) {
- StringBuilder launchTimes = new StringBuilder();
- StringBuilder cpuCycles = new StringBuilder();
- StringBuilder majorFaults = new StringBuilder();
- for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
- launchTimes.append(result.mLaunchTime);
- launchTimes.append(",");
- if (mSimplePerfAppOnly) {
- cpuCycles.append(result.mCpuCycles);
- cpuCycles.append(",");
- majorFaults.append(result.mMajorFaults);
- majorFaults.append(",");
- }
- }
- String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
- mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
- if (mSimplePerfAppOnly) {
- mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
- cpuCycles.toString());
- mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
- majorFaults.toString());
- }
- }
- }
- instrumentation.sendStatus(0, mResult);
- }
-
- /**
- * Compile the app package using compilerFilter and return true or false
- * based on status of the compilation command.
- */
- private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
- try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
- executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
- new FileInputStream(result.getFileDescriptor())))) {
- String line;
- while ((line = bufferedReader.readLine()) != null) {
- if (line.contains(COMPILE_SUCCESS)) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * If launch order is "cyclic" then apps will be launched one after the
- * other for each iteration count.
- * If launch order is "sequential" then each app will be launched for given number
- * iterations at once before launching the other apps.
- */
- private void setLaunchOrder() {
- if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
- for (String compilerFilter : mCompilerFilters) {
- if (mTrialLaunch) {
- for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
- }
- }
- for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
- for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
- String.format(LAUNCH_ITERATION, launchCount)));
- }
- }
- if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
- for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
- for (String app : mNameToResultKey.keySet()) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
- String.format(TRACE_ITERATION, traceCount)));
- }
- }
- }
- }
- } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
- for (String compilerFilter : mCompilerFilters) {
- for (String app : mNameToResultKey.keySet()) {
- if (mTrialLaunch) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
- }
- for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
- String.format(LAUNCH_ITERATION, launchCount)));
- }
- if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
- for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
- mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
- String.format(TRACE_ITERATION, traceCount)));
- }
- }
- }
- }
- } else {
- assertTrue("Launch order is not valid parameter", false);
- }
- }
-
- private void dropCache() {
- if (mDropCache) {
- assertNotNull("Issue in dropping the cache",
- getInstrumentation().getUiAutomation()
- .executeShellCommand(DROP_CACHE_SCRIPT));
- }
- }
-
- private void parseArgs(Bundle args) {
- mNameToResultKey = new LinkedHashMap<String, String>();
- mNameToLaunchTime = new HashMap<>();
- String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
- if (launchIterations != null) {
- mLaunchIterations = Integer.parseInt(launchIterations);
- }
- String appList = args.getString(KEY_APPS);
- if (appList == null)
- return;
-
- String appNames[] = appList.split("\\|");
- for (String pair : appNames) {
- String[] parts = pair.split("\\^");
- if (parts.length != 2) {
- Log.e(TAG, "The apps key is incorrectly formatted");
- fail();
- }
-
- mNameToResultKey.put(parts[0], parts[1]);
- mNameToLaunchTime.put(parts[0], null);
- }
- String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
- if (requiredAccounts != null) {
- mRequiredAccounts = new HashSet<String>();
- for (String accountType : requiredAccounts.split(",")) {
- mRequiredAccounts.add(accountType);
- }
- }
-
- String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
- if (compilerFilterList != null) {
- // If a compiler filter is passed, we make a trial launch to force compilation
- // of the apps.
- mTrialLaunch = true;
- mCompilerFilters = compilerFilterList.split("\\|");
- } else {
- // Just pass a null compiler filter to use the current state of the app.
- mCompilerFilters = new String[1];
- }
-
- // Pre-populate the results map to avoid null checks.
- for (String app : mNameToLaunchTime.keySet()) {
- HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
- mNameToLaunchTime.put(app, map);
- for (String compilerFilter : mCompilerFilters) {
- map.put(compilerFilter, new ArrayList<>());
- }
- }
-
- mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
- mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
- mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
- mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
- mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
- mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
-
- if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
- Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
- KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
- }
- }
-
- private boolean hasLeanback(Context context) {
- return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
- }
-
- private void createMappings() {
- mNameToIntent = new LinkedHashMap<String, Intent>();
-
- PackageManager pm = getInstrumentation().getContext()
- .getPackageManager();
- Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
- intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ?
- Intent.CATEGORY_LEANBACK_LAUNCHER :
- Intent.CATEGORY_LAUNCHER);
- List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
- resolveLoop(ris, intentToResolve, pm);
- // For Wear
- intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
- ris = pm.queryIntentActivities(intentToResolve, 0);
- resolveLoop(ris, intentToResolve, pm);
- }
-
- private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
- if (ris == null || ris.isEmpty()) {
- Log.i(TAG, "Could not find any apps");
- } else {
- for (ResolveInfo ri : ris) {
- Intent startIntent = new Intent(intentToResolve);
- startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- startIntent.setClassName(ri.activityInfo.packageName,
- ri.activityInfo.name);
- String appName = ri.loadLabel(pm).toString();
- if (appName != null) {
- // Support launching intent using package name or app name
- mNameToIntent.put(ri.activityInfo.packageName, startIntent);
- mNameToIntent.put(appName, startIntent);
- }
- }
- }
- }
-
- private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
- String launchReason) throws NameNotFoundException, RemoteException {
- Log.i(TAG, "Starting " + appName);
-
- Intent startIntent = mNameToIntent.get(appName);
- if (startIntent == null) {
- Log.w(TAG, "App does not exist: " + appName);
- mResult.putString(mNameToResultKey.get(appName), "App does not exist");
- return new AppLaunchResult();
- }
- AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
- launchReason);
- Thread t = new Thread(runnable);
- t.start();
- try {
- t.join(JOIN_TIMEOUT);
- } catch (InterruptedException e) {
- // ignore
- }
- return runnable.getResult();
- }
-
- private void checkAccountSignIn() {
- // ensure that the device has the required account types before starting test
- // e.g. device must have a valid Google account sign in to measure a meaningful launch time
- // for Gmail
- if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
- return;
- }
- final AccountManager am =
- (AccountManager) getInstrumentation().getTargetContext().getSystemService(
- Context.ACCOUNT_SERVICE);
- Account[] accounts = am.getAccounts();
- // use set here in case device has multiple accounts of the same type
- Set<String> foundAccounts = new HashSet<String>();
- for (Account account : accounts) {
- if (mRequiredAccounts.contains(account.type)) {
- foundAccounts.add(account.type);
- }
- }
- // check if account type matches, if not, fail test with message on what account types
- // are missing
- if (mRequiredAccounts.size() != foundAccounts.size()) {
- mRequiredAccounts.removeAll(foundAccounts);
- StringBuilder sb = new StringBuilder("Device missing these accounts:");
- for (String account : mRequiredAccounts) {
- sb.append(' ');
- sb.append(account);
- }
- fail(sb.toString());
- }
- }
-
- private void closeApp(String appName, boolean forceStopApp) {
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- getInstrumentation().getContext().startActivity(homeIntent);
- sleep(POST_LAUNCH_IDLE_TIMEOUT);
- if (forceStopApp) {
- Intent startIntent = mNameToIntent.get(appName);
- if (startIntent != null) {
- String packageName = startIntent.getComponent().getPackageName();
- try {
- mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
- } catch (RemoteException e) {
- Log.w(TAG, "Error closing app", e);
- }
- }
- }
- }
-
- private void sleep(int time) {
- try {
- Thread.sleep(time);
- } catch (InterruptedException e) {
- // ignore
- }
- }
-
- private void reportError(String appName, String processName) {
- ActivityManager am = (ActivityManager) getInstrumentation()
- .getContext().getSystemService(Context.ACTIVITY_SERVICE);
- List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
- if (crashes != null) {
- for (ProcessErrorStateInfo crash : crashes) {
- if (!crash.processName.equals(processName))
- continue;
-
- Log.w(TAG, appName + " crashed: " + crash.shortMsg);
- mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
- return;
- }
- }
-
- mResult.putString(mNameToResultKey.get(appName),
- "Crashed for unknown reason");
- Log.w(TAG, appName
- + " not found in process list, most likely it is crashed");
- }
-
- private class LaunchOrder {
- private String mApp;
- private String mCompilerFilter;
- private String mLaunchReason;
-
- LaunchOrder(String app, String compilerFilter, String launchReason){
- mApp = app;
- mCompilerFilter = compilerFilter;
- mLaunchReason = launchReason;
- }
-
- public String getApp() {
- return mApp;
- }
-
- public void setApp(String app) {
- mApp = app;
- }
-
- public String getCompilerFilter() {
- return mCompilerFilter;
- }
-
- public String getLaunchReason() {
- return mLaunchReason;
- }
-
- public void setLaunchReason(String launchReason) {
- mLaunchReason = launchReason;
- }
- }
-
- private class AppLaunchResult {
- long mLaunchTime;
- long mCpuCycles;
- long mMajorFaults;
-
- AppLaunchResult() {
- mLaunchTime = -1L;
- mCpuCycles = -1L;
- mMajorFaults = -1L;
- }
-
- AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
- try {
- mLaunchTime = Long.parseLong(launchTime, 10);
- mCpuCycles = Long.parseLong(cpuCycles, 10);
- mMajorFaults = Long.parseLong(majorFaults, 10);
- } catch (NumberFormatException e) {
- Log.e(TAG, "Error parsing result", e);
- }
- }
- }
-
- private class AppLaunchRunnable implements Runnable {
- private Intent mLaunchIntent;
- private AppLaunchResult mLaunchResult;
- private boolean mForceStopBeforeLaunch;
- private String mLaunchReason;
-
- public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
- String launchReason) {
- mLaunchIntent = intent;
- mForceStopBeforeLaunch = forceStopBeforeLaunch;
- mLaunchReason = launchReason;
- mLaunchResult = new AppLaunchResult();
- }
-
- public AppLaunchResult getResult() {
- return mLaunchResult;
- }
-
- public void run() {
- File launchFile = null;
- try {
- String packageName = mLaunchIntent.getComponent().getPackageName();
- String componentName = mLaunchIntent.getComponent().flattenToShortString();
- if (mForceStopBeforeLaunch) {
- Log.v(TAG, "Stopping app before launch");
- mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
- } else {
- Log.v(TAG, "Not killing app. Going to Home Screen.");
- ParcelFileDescriptor goHome = getInstrumentation().getUiAutomation()
- .executeShellCommand("input keyevent 3");
- }
- String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
- if (mSimplePerfAppOnly) {
- try {
- // executeShellCommand cannot handle shell specific actions, like '&'.
- // Therefore, we create a file containing the command and make that
- // the command to launch.
- launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
- launchFile.setExecutable(true);
- try (FileOutputStream stream = new FileOutputStream(launchFile);
- BufferedWriter writer =
- new BufferedWriter(new OutputStreamWriter(stream))) {
- String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
- writer.write(cmd);
- }
- launchCmd = launchFile.getAbsolutePath();
- } catch (IOException e) {
- Log.w(TAG, "Error writing the launch command", e);
- return;
- }
- } else if (null != mSimplePerfCmd) {
- launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
- }
- Log.v(TAG, "Final launch cmd:" + launchCmd);
- ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
- .executeShellCommand(launchCmd);
- mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
- ("App Launch :%s %s", componentName, mLaunchReason));
- } catch (RemoteException e) {
- Log.w(TAG, "Error launching app", e);
- } finally {
- if (launchFile != null) {
- launchFile.delete();
- }
- }
- }
-
- /**
- * Method to parse the launch time info and write the result to file
- *
- * @param parcelDesc
- * @return
- */
- private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
- String headerInfo) {
- String launchTime = "-1";
- String cpuCycles = "-1";
- String majorFaults = "-1";
- boolean launchSuccess = false;
- try {
- InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
- /* SAMPLE OUTPUT :
- Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
- Status: ok
- Activity: com.google.android.calculator/com.android.calculator2.Calculator
- ThisTime: 357
- TotalTime: 357
- WaitTime: 377
- Complete*/
- /* WHEN NOT KILLING HOME :
- Starting: Intent { cmp=com.google.android.wearable.app/
- com.google.android.clockwork.home.calendar.AgendaActivity }
- Warning: Activity not started, its current task has been brought to the front
- Status: ok
- Activity: com.google.android.wearable.app/
- com.google.android.clockwork.home.calendar.AgendaActivity
- ThisTime: 209
- TotalTime: 209
- WaitTime: 285
- Complete*/
- /* WITH SIMPLEPERF :
- Performance counter statistics,
- 6595722690,cpu-cycles,4.511040,GHz,(100%),
- 0,major-faults,0.000,/sec,(100%),
- Total test time,1.462129,seconds,*/
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
- inputStream));
- String line = null;
- int lineCount = 1;
- int addLineForWarning = 0;
- mBufferedWriter.newLine();
- mBufferedWriter.write(headerInfo);
- mBufferedWriter.newLine();
- while ((line = bufferedReader.readLine()) != null) {
- if (lineCount == 2 && line.contains(WARNING_MESSAGE)) {
- addLineForWarning = 1;
- }
- if (lineCount == (2 + addLineForWarning) && line.contains(SUCCESS_MESSAGE)) {
- launchSuccess = true;
- }
- // Parse TotalTime which is the launch time
- if (launchSuccess && lineCount == (5 + addLineForWarning)) {
- String launchSplit[] = line.split(":");
- launchTime = launchSplit[1].trim();
- }
-
- if (mSimplePerfAppOnly) {
- // Parse simpleperf output.
- if (lineCount == (9 + addLineForWarning)) {
- if (!line.contains("cpu-cycles")) {
- Log.e(TAG, "Error in simpleperf output");
- } else {
- cpuCycles = line.split(",")[0].trim();
- }
- } else if (lineCount == (10 + addLineForWarning)) {
- if (!line.contains("major-faults")) {
- Log.e(TAG, "Error in simpleperf output");
- } else {
- majorFaults = line.split(",")[0].trim();
- }
- }
- }
- mBufferedWriter.write(line);
- mBufferedWriter.newLine();
- lineCount++;
- }
- mBufferedWriter.flush();
- inputStream.close();
- } catch (IOException e) {
- Log.w(TAG, "Error writing the launch file", e);
- }
- return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
- }
-
- }
-}
diff --git a/tests/AttestationVerificationTest/Android.bp b/tests/AttestationVerificationTest/Android.bp
new file mode 100644
index 000000000000..b98f8cb0c21d
--- /dev/null
+++ b/tests/AttestationVerificationTest/Android.bp
@@ -0,0 +1,45 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "AttestationVerificationTest",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+ defaults: ["cts_defaults"],
+ manifest: "AndroidManifest.xml",
+ test_config: "AndroidTest.xml",
+ platform_apis: true,
+ certificate: "platform",
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "platform-test-annotations",
+ "services.core",
+ ],
+}
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
new file mode 100755
index 000000000000..37321ad80b0f
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.security.attestationverification">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+ <uses-permission android:name="android.permission.USE_ATTESTATION_VERIFICATION_SERVICE" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".SystemAttestationVerificationTest$TestActivity" />
+ <activity android:name=".PeerDeviceSystemAttestationVerificationTest$TestActivity" />
+ </application>
+
+ <!-- self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.attestationverification">
+ </instrumentation>
+</manifest>
diff --git a/tests/AttestationVerificationTest/AndroidTest.xml b/tests/AttestationVerificationTest/AndroidTest.xml
new file mode 100644
index 000000000000..132576035952
--- /dev/null
+++ b/tests/AttestationVerificationTest/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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 description="Platform tests for Attestation Verification Framework">
+ <option name="test-tag" value="AttestationVerificationTest" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="AttestationVerificationTest.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.security.attestationverification" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/AttestationVerificationTest/OWNERS b/tests/AttestationVerificationTest/OWNERS
new file mode 100644
index 000000000000..a7a6ef156eda
--- /dev/null
+++ b/tests/AttestationVerificationTest/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/security/attestationverification/OWNERS
diff --git a/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
new file mode 100644
index 000000000000..e29ff487806e
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIICkjCCAjmgAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQMDANURUUxKTAn
+BgNVBAUTIDg2ZTQ0MjRhMjY2NDlhZDcyZWZhNWM0MWEwM2IyN2QxMCAXDTcwMDEw
+MTAwMDAwMFoYDzIxMDYwMjA3MDYyODE1WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtl
+eXN0b3JlIEtleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIlTwcvhe+DLV45X
+RCTO7HoN20Ib7IbCEhV5+YdMiYOp/0AdKk8oYvsri1XODeC4zcoPfHNdQGt/68i0
+ADbilJmjggFIMIIBRDAOBgNVHQ8BAf8EBAMCB4AwggEwBgorBgEEAdZ5AgERBIIB
+IDCCARwCAQMKAQECAQQKAQEECXBsYXllcjQ1NgQAMFe/hT0IAgYBfvkgVei/hUVH
+BEUwQzEdMBsEFmNvbS5nb29nbGUuYXR0ZXN0YXRpb24CAQExIgQgOqyVXRJUdAGY
+/XVx8y/uRPiebqlyELt1EpqIz29h5tUwgaehCDEGAgECAgEDogMCAQOjBAICAQCl
+CDEGAgEEAgEGqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCCEZx8qY8Ys0HC2TqPq
+74eYPzh5L/agxD7Bn7zVBQHoNAEB/woBAAQguJwoDfWBjRaedzQ6TJPFJJKs+ytr
++8Vu2CSmqifFBHW/hUEFAgMB1MC/hUIFAgMDFdm/hU4GAgQBNIjJv4VPBgIEATSI
+yTAKBggqhkjOPQQDAgNHADBEAiBdGxfMEx59k5+zo+hV3Q9kgjbGi0zU3WH355P5
+JZttBwIgY4FZsSreUJL8RY3JvfvD8BRw8GuXcB1OQ600hwaYYC4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8zCCAXqgAwIBAgIRAOuuukN0OHbNQvKngECkewEwCgYIKoZIzj0EAwIwOTEM
+MAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYxMDRmYWFlOTQ3ODY0ZTU4
+MDRmMWY4ZDAeFw0yMDA5MjgyMDI3NTZaFw0zMDA5MjYyMDI3NTZaMDkxDDAKBgNV
+BAwMA1RFRTEpMCcGA1UEBRMgODZlNDQyNGEyNjY0OWFkNzJlZmE1YzQxYTAzYjI3
+ZDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT3Mjl05ewv6G8zAR4fXJy2iadU
+yK7rNvzlECy2+nhEieL8BFXDvo0tx5fYs8qr67j/KvluFBfp2r9s+ckWz3Kzo2Mw
+YTAdBgNVHQ4EFgQUsVKBzAs1lMXAauQ3mGAJZJqK5tAwHwYDVR0jBBgwFoAUEsQA
+i8d2oLULSi5Ix4BTGGbvUEkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AgQwCgYIKoZIzj0EAwIDZwAwZAIwfFziBCwuM1thLUSUNI61Xx/vnDnNkSv/aX5D
+yLjxbLlgnFSzIrc+6vf6h6L/+TjYAjAq6h9GKtMn4R0286MoqYqzp/rHn6JD2sqH
+iM8KZ0oA+Ut242EcmGjAoNfGZGZGddQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAXugAwIBAgIQNTAX5z3CBac6nD3hQiMDcDANBgkqhkiG9w0BAQsFADAb
+MRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MB4XDTIwMDkyODIwMjUwMloXDTMw
+MDkyNjIwMjUwMlowOTEMMAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYx
+MDRmYWFlOTQ3ODY0ZTU4MDRmMWY4ZDB2MBAGByqGSM49AgEGBSuBBAAiA2IABA/7
+xZFlFtTjdy2B3p7E+FsrBjyhBSqY4a9FywawXMJRSja3HAK36ruzJjWlEkD+D0vq
+HI2joY39FHmWoZWwm2cq9gOleFGYOSCpMr4ib7xtq/6nefvKTP5rutxudF97t6Nj
+MGEwHQYDVR0OBBYEFBLEAIvHdqC1C0ouSMeAUxhm71BJMB8GA1UdIwQYMBaAFDZh
+4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgIEMA0GCSqGSIb3DQEBCwUAA4ICAQAaMONDQxJz3PRn9gHQW5KP+TIoBPJZyGa1
+QFuEBcMDTtIxBxEh5Pj3ivPBc76PrdYu5U47Ve5YYCPsTpUTj7dOxbzGSZjfjvHF
+fNwy24g1Lah2iAdQRVErhWKBlpnQhBnnRrrNmTTmzhl8NvSExqAPP746dqwm1kQ7
+YesC5yoEAHpxamhlZpIKAjSxSZeHWace2qV00M8qWd/7lIpqttJjFFrhCjzR0dtr
+oIIpC5EtmqIWdLeg6yZjJkX+Cjv4F8mRfBtwuNuxFsfALQ3D5l8WKw3iwPebmCy1
+kEby8Eoq88FxzXQp/XgAaljlrKXyuxptrc1noRuob4g42Oh6wetueYRSCtO6Bkym
+0UMnld/kG77aeiHOMVVb86wrhNuAGir1vgDGOBsclITVyuu9ka0YVQjjDm3phTpd
+O8JV16gbei2Phn+FfRV1MSDsZo/wu0i2KVzgs27bfJocMHXv+GzvwfefYgMJ/rYq
+Bg27lpsWzmFEPv2cyhA5PwwbG8ceswa3RZE/2eS9o7STkz93jr/KsKLcMBY6cX2C
+q4CBJByKFJtVANOVj+neFNxc2sQgeTT33yYNKbe4b5bm7Ki1FbrhFVckpzUGDnKs
+gL+AxvALWOoryDGwNbJiW8PRiD3HHByiMvSEQ7e7BSc2KjbsaWbCfYZAMZJEhEsc
+P1l8lcUVuA==
+-----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/assets/test_attestation_wrong_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
new file mode 100644
index 000000000000..3d6410af042a
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx
+MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb
+BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c
+NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU
+xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo
+/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+
+tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD
+AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB
+ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj
+aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl
+cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp
+Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv
+Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h
+bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y
+AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy
+b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB
+rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+
+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP
+HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC
+AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S
+OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+
+qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa
+u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3
+Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk
+DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW
+OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i
+3i9VM6yOLIrP
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
new file mode 100644
index 000000000000..6d261fae47cf
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoDCCA4igAwIBAgIQTfpKgAsLZJhp2V4xUriMADANBgkqhkiG9w0BAQ0FADBp
+MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFu
+ZHJvaWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlv
+biBSb290IENBMCAXDTE3MDYyMTIwMjQzN1oYDzIwNTcwNjExMjAyNDM3WjBpMQsw
+CQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFuZHJv
+aWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlvbiBS
+b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuO82oerGivb9
+G9bWyM8Pg0y6SOnAC8/8b92dp1v4Npnc+QpjPRUKgn8lzjQ9Jo6IGY3OShRBiQYl
+bbZYkfJnC5HtqbOETdPLZclErVE/G6Oda1IeZWvQVMjNImEYOLL5ct2RxiPttd8v
+SLyOSNFPf5/SeFqX/La0NcmXMOvPSrTW3qO34brnC+ih7mlpJFLz6Up93N3Umxsl
+IElz2wCG72t6k3+caWLyIPVgIPmsQrfTeBK/hN5dAJgAN65BsTevLHRP9J610wj3
+RagSIK1NdTuJRnr5ZyTQrwE2nA8H3IJ7/eo6IlGhXPwLKDhbdxYygPxdlCq6Rl96
+aVLjfpqDPtJ9ow+QKZuEDbYJ4z4olNXC6O5G7vqnCuULA/2E7y7DZObjzXOrdx2z
+9YKd8BrIDMTN/5mmw2us8tywiaQhbl8vOtjU+A+iBBnkj/wt9TYyLKopdrDlo5mz
+wy5l750HOkVZXC3VkeECnp+9duSHdS4qeUf/W1j9nPM7kY0HFLPUVX9AFVp2JXnC
+iKZC32GQAVsDc1iyAZWAVTqA7E0fBHhk9jUnA0W9b5Lq06oW95ngNR1MIFY871i8
+aLHCBpIix8DuMe8NB9spCIP6WCQqGiWQQpzbeuBPtoi424xwZTO4oectTd77bs9V
+Rvunn49fz308KnoWjk/by1N7gWyTb38CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMDQ1I0RKwFCI+Fy9uIIJ/HrXuqu
+MA0GCSqGSIb3DQEBDQUAA4ICAQB09qkyEpEDocdN5pPeXqtjj9d0AXREUGH2LhnC
+z9KZcUFR+JskwEMHCaOENOmKI3zWRmxT7d8cVywwGk+ExE7EBQoeHlh3Yo44M8aj
+ZL7RHCvHRYsePhAJkYpJ02IMR60TV+1jhMqE8+BnqFivS7kft4t295EyrnLRZE3b
+Nfc0t011j02RwUrioR57mdvS9EZBRnMQkobhn+jWt9O+V3mtplW+1A2n4ec6uni1
+2MMgAWHuO1sKVYd5Sp4JMUpNnfmQAMnNiOMF6VxkpaoF1lZWo4TrLxuDKJG3O8h1
+fByjCpNVY8kOvvYEadbldzh6Agy/3ppb9yfG7X7FtHr1ghNjuNT6w5VgvbRtoRja
+/ZSKuJMaKm5emMWNkls/cwVSPJIvTOzPTeYK1BKSyAL2LDJ93HI7x8h79/Q7gKRi
+kL8qT7GW2FqpWTK0253sJHqCJJP4A5Rxtf2+Afwqadfc6Ga4jJHb7rPXngz4j1ZB
+gl5yjXgWF9wHGxqrjKWe2EA3d47BC4HG3Rf5L56KQiRPhTqTk5vtZwtwLRLFDLt7
+Hdff13O1oLhn+2z9xkASUL3rFE/qWajZP7fk3CvzcuXwKDTZomIC4nNaglx4nLdj
+lHhOq+6ON8MZC46sLStD+D4a9A1HOoihJgI/yGGkwdrp4KQIveRkEBO/x9v3NNBE
+bMwG9w==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_root_certs.pem b/tests/AttestationVerificationTest/assets/test_root_certs.pem
new file mode 100644
index 000000000000..c51851fe3da5
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_root_certs.pem
@@ -0,0 +1,61 @@
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
+ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+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
+AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
+VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
+Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
+ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
+Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
+qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
+DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
+QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
+JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
+CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
+ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
+qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
+MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
+wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
+-----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/assets/test_virtual_device_attestation_certs.pem b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
new file mode 100644
index 000000000000..282771000bf7
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
@@ -0,0 +1,50 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCApGgAwIBAgIBATAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCVVMxEzAR
+BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UE
+CwwHQW5kcm9pZDE7MDkGA1UEAwwyQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBB
+dHRlc3RhdGlvbiBJbnRlcm1lZGlhdGUwHhcNNzAwMTAxMDAwMDAwWhcNNjkxMjMx
+MjM1OTU5WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtleXN0b3JlIEtleTBZMBMGByqG
+SM49AgEGCCqGSM49AwEHA0IABEYtCH28qu+St0F0TixVsQz0L/Y7DcRHgYAU98E6
+edwOpACFmmseYxMjvmZv/4jURSG2/Z0J1s3A/qFzIY96/tyjggFSMIIBTjALBgNV
+HQ8EBAMCB4AwggEcBgorBgEEAdZ5AgERBIIBDDCCAQgCAQQKAQACASkKAQAECXBs
+YXllcjQ1NgQAMIHqoQgxBgIBAgIBA6IDAgEDowQCAgEApQgxBgIBBAIBBqoDAgEB
+v4N3AgUAv4U9CAIGAX8DoY9Qv4U+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAv4VBBQIDAa2wv4VCBQIDAxUbv4VFRwRFMEMxHTAbBBZjb20uZ29v
+Z2xlLmF0dGVzdGF0aW9uAgEBMSIEIDqslV0SVHQBmP11cfMv7kT4nm6pchC7dRKa
+iM9vYebVMAAwHwYDVR0jBBgwFoAUP/ys1hqxOp6BILjVJRzFZbsekakwCgYIKoZI
+zj0EAwIDSQAwRgIhAMzs7gWWBIITpeLeEEx9B8ihdhkFqpMGlsYLRO01ZIOeAiEA
+uKs9xfK3fIOpVAhDmsrp+zE8KUwyvqCU/IS13tXz7Ng=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
+VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
+ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
+MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
+bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
+dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
+efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
+U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
+qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
+wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
+Xvsiu+f+uXc/WT/7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
+dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
+VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
+HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
+QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
+dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
+BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
+EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
+SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
+C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
new file mode 100644
index 000000000000..32c2230e4880
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
@@ -0,0 +1,161 @@
+package android.security.attestationverification
+
+import android.app.Activity
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_UNKNOWN
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.ByteArrayOutputStream
+import java.security.cert.CertificateFactory
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PeerDeviceSystemAttestationVerificationTest {
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ private val certifcateFactory = CertificateFactory.getInstance("X.509")
+ private lateinit var activity: Activity
+ private lateinit var avm: AttestationVerificationManager
+ private lateinit var invalidAttestationByteArray: ByteArray
+
+ @Before
+ fun setup() {
+ rule.getScenario().onActivity {
+ avm = it.getSystemService(AttestationVerificationManager::class.java)
+ activity = it
+ }
+ invalidAttestationByteArray = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToByteArray()
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_UNKNOWN, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyRequirements() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureMismatchBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val publicKeyRequirements = Bundle()
+ publicKeyRequirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, publicKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+
+ val future2 = CompletableFuture<Int>()
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "challengeStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future2.complete(result)
+ }
+
+ assertThat(future2.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongResourceKey() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val wrongKeyRequirements = Bundle()
+ wrongKeyRequirements.putByteArray("wrongReqKey", "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, wrongKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyAttestation() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val requirements = Bundle()
+ requirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, requirements, ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustAnchorMismatch() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun <T> CompletableFuture<T>.getSoon(): T {
+ return this.get(1, TimeUnit.SECONDS)
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = certifcateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
new file mode 100644
index 000000000000..169effaa45ca
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -0,0 +1,222 @@
+package android.security.attestationverification
+
+import android.os.Bundle
+import android.app.Activity
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
+import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import java.lang.IllegalArgumentException
+import java.io.ByteArrayOutputStream
+import java.security.KeyPairGenerator
+import java.security.KeyStore
+import java.time.Duration
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SystemAttestationVerificationTest {
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ private lateinit var activity: Activity
+ private lateinit var avm: AttestationVerificationManager
+ private lateinit var androidKeystore: KeyStore
+
+ @Before
+ fun setup() {
+ rule.getScenario().onActivity {
+ avm = it.getSystemService(AttestationVerificationManager::class.java)
+ activity = it
+ androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
+ }
+ }
+
+ @Test
+ fun verifyAttestation_returnsUnknown() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_UNKNOWN)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithEmptyAttestation() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithEmptyRequirements() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongBindingType() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY,
+ selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongRequirements() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ val wrongKeyRequirements = Bundle()
+ wrongKeyRequirements.putByteArray(
+ "wrongBindingKey", "challengeStr".encodeToByteArray())
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongChallenge() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ val wrongChallengeRequirements = Bundle()
+ wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) {
+ result, _ -> future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED.
+ @Test
+ fun verifyAttestation_returnsSuccess() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyToken_returnsUnknown() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { _, token ->
+ val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_UNKNOWN)
+ }
+
+ @Test
+ fun verifyToken_tooBigMaxAgeThrows() {
+ val future = CompletableFuture<VerificationToken>()
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
+ activity.mainExecutor) { _, token ->
+ future.complete(token)
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), future.getSoon(),
+ Duration.ofSeconds(3601))
+ }
+ }
+
+ private fun <T> CompletableFuture<T>.getSoon(): T {
+ return this.get(1, TimeUnit.SECONDS)
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) {
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ val localBindingType = TYPE_CHALLENGE
+ val requirements: Bundle
+ val attestation: ByteArray
+
+ init {
+ val challengeByteArray = challenge.encodeToByteArray()
+ generateAndStoreKey(alias, challengeByteArray)
+ attestation = generateCertificatesByteArray(alias)
+ requirements = Bundle()
+ requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray)
+ }
+
+ private fun generateAndStoreKey(alias: String, challenge: ByteArray) {
+ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_EC,
+ ANDROID_KEYSTORE
+ )
+ val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
+ alias,
+ KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
+ ).run {
+ // a challenge results in a generated attestation
+ setAttestationChallenge(challenge)
+ setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ build()
+ }
+ kpg.initialize(parameterSpec)
+ kpg.generateKeyPair()
+ }
+
+ private fun generateCertificatesByteArray(alias: String): ByteArray {
+ val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry
+ val certs = pkEntry.certificateChain
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+ }
+
+ companion object {
+ private const val TAG = "AVFTEST"
+ private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
new file mode 100644
index 000000000000..0d15fe72920a
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
@@ -0,0 +1,297 @@
+/*
+ * 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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/** Test for data class holding parsed X509Certificate attestation attributes. */
+@RunWith(AndroidJUnit4.class)
+public class AndroidKeystoreAttestationVerificationAttributesTest {
+ @Rule public ExpectedException mException = ExpectedException.none();
+ private static final String TEST_PHYSCIAL_DEVICE_CERTS =
+ "test_attestation_wrong_root_certs.pem";
+ private static final String TEST_PHYSICAL_DEVICE_CERTS_2 =
+ "test_attestation_with_root_certs.pem";
+ private static final String TEST_VIRTUAL_DEVICE_CERTS =
+ "test_virtual_device_attestation_certs.pem";
+ private static final String TEST_CERT_NO_ATTESTATION_EXTENSION =
+ "test_no_attestation_ext_certs.pem";
+ private static final String TEST_CERTS_NO_ATTESTATION_EXTENSION_2 =
+ "test_root_certs.pem";
+
+
+ private CertificateFactory mFactory;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes2;
+ private AndroidKeystoreAttestationVerificationAttributes mVirtualDeviceAttributes;
+
+ @Before
+ public void setUp() throws Exception {
+ mFactory = CertificateFactory.getInstance("X.509");
+ mPhysicalDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificate(TEST_PHYSCIAL_DEVICE_CERTS));
+ mPhysicalDeviceAttributes2 =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_PHYSICAL_DEVICE_CERTS_2).get(0));
+ mVirtualDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_VIRTUAL_DEVICE_CERTS).get(0));
+ }
+
+ @Test
+ public void parseCertificate_noAttestationExtension() throws Exception {
+ List<X509Certificate> certsNoAttestation =
+ generateCertificates(TEST_CERTS_NO_ATTESTATION_EXTENSION_2);
+ certsNoAttestation.add(generateCertificate(TEST_CERT_NO_ATTESTATION_EXTENSION));
+ for (X509Certificate cert: certsNoAttestation) {
+ mException.expect(CertificateEncodingException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("No attestation extension found in certificate."));
+
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(cert);
+ }
+ }
+
+ @Test
+ public void parseCertificate_attestationLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationVersion()).isEqualTo(3);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationVersion()).isEqualTo(3);
+ assertThat(mVirtualDeviceAttributes.getAttestationVersion()).isEqualTo(4);
+ }
+
+ @Test
+ public void parseCertificate_attestationSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isAttestationHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isAttestationHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isAttestationHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isAttestationHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keymasterLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mVirtualDeviceAttributes.getKeymasterVersion()).isEqualTo(41);
+ }
+
+ @Test
+ public void parseCertificate_keymasterSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isKeymasterHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isKeymasterHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_attestationChallenge() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "abc".getBytes(UTF_8));
+ assertThat(mPhysicalDeviceAttributes2.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ assertThat(mVirtualDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootState() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.UNVERIFIED);
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED);
+ assertThat(mVirtualDeviceAttributes.getVerifiedBootState()).isNull();
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyBootPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyBootPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyBootPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyBootPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsPatchLevel()).isEqualTo(202201);
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyVendorPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyVendorPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyVendorPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyVendorPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyAuthenticatorType() {
+ assertThat(mPhysicalDeviceAttributes.getKeyAuthenticatorType()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyAuthenticatorType()).isEqualTo(0);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersion() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsVersion()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsVersion()).isEqualTo(120000);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersionNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsVersion is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsVersion();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootHash() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootHash()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootHash()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootKey() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootKey()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootKey()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLocked() {
+ assertThat(mPhysicalDeviceAttributes.isVerifiedBootLocked()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.isVerifiedBootLocked()).isTrue();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLockedNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("VerifiedBootLocked is not set."));
+
+ mVirtualDeviceAttributes.isVerifiedBootLocked();
+ }
+
+ @Test
+ public void parseCertificate_applicationPackageNameVersion() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationPackageNameVersion()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_applicationCertificateDigests() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationCertificateDigests()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_valuesNotSet() {
+ assertThat(mPhysicalDeviceAttributes.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.isKeyAllowedForAllApplications()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.isKeyAllowedForAllApplications()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keyRequiresUnlockedDeviceNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyRequiresUnlockedDevice is not set."));
+
+ mPhysicalDeviceAttributes.isKeyRequiresUnlockedDevice();
+ }
+
+ private X509Certificate generateCertificate(String certificateString)
+ throws Exception {
+ return generateCertificates(certificateString).get(0);
+ }
+
+ private List<X509Certificate> generateCertificates(String certificateString)
+ throws Exception {
+ Collection<? extends Certificate> certificates = mFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(certificateString));
+
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
+ for (Certificate cert : certificates) {
+ x509Certs.add((X509Certificate) cert);
+ }
+ return x509Certs;
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
new file mode 100644
index 000000000000..45f2e5c6fdf7
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -0,0 +1,175 @@
+package com.android.server.security
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import java.io.ByteArrayOutputStream
+import java.security.cert.Certificate
+import java.security.cert.CertificateFactory
+import java.security.cert.TrustAnchor
+import java.security.cert.X509Certificate
+import java.time.LocalDate
+
+/** Test for Peer Device attestation verifier. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AttestationVerificationPeerDeviceVerifierTest {
+ private val certificateFactory = CertificateFactory.getInstance("X.509")
+ @Mock private lateinit var context: Context
+ private lateinit var trustAnchors: HashSet<TrustAnchor>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts()
+ trustAnchors = HashSet<TrustAnchor>()
+ rootCerts.forEach {
+ trustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypeChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypePublicKey() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+
+ val leafCert =
+ (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
+ as X509Certificate
+ val pkRequirements = Bundle()
+ pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded)
+
+ val result = verifier.verifyAttestation(
+ TYPE_PUBLIC_KEY, pkRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2023, 3, 1),
+ LocalDate.of(2023, 2, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, HashSet(), false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorMismatch() {
+ val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts()
+ val badTrustAnchors = HashSet<TrustAnchor>()
+ badTrustAnchorsCerts.forEach {
+ badTrustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ fun verifyAttestation_returnsFailureChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun String.fromPEMFileToCerts(): Collection<Certificate> {
+ return certificateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = this.fromPEMFileToCerts()
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
+ private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
+ "test_attestation_with_root_certs.pem"
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
index 35f1e585931b..644d450a7a88 100644
--- a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
+++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java
@@ -24,6 +24,8 @@ import android.net.SntpClient;
import android.os.Environment;
import android.util.Log;
+import libcore.io.Streams;
+
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -37,8 +39,6 @@ import java.net.InetAddress;
import java.net.URL;
import java.util.Random;
-import libcore.io.Streams;
-
/*
* Test Service that tries to connect to the web via different methods and outputs the results to
* the log and a output file.
@@ -146,7 +146,7 @@ public class BandwidthEnforcementTestService extends IntentService {
final ConnectivityManager mCM = context.getSystemService(ConnectivityManager.class);
final Network network = mCM.getActiveNetwork();
- if (client.requestTime("0.pool.ntp.org", 10000, network)) {
+ if (client.requestTime("0.pool.ntp.org", SntpClient.STANDARD_NTP_PORT, 10000, network)) {
return true;
}
return false;
diff --git a/tests/BatteryStatsPerfTest/AndroidManifest.xml b/tests/BatteryStatsPerfTest/AndroidManifest.xml
index 7633d5283f5e..ab5728e75b9f 100644
--- a/tests/BatteryStatsPerfTest/AndroidManifest.xml
+++ b/tests/BatteryStatsPerfTest/AndroidManifest.xml
@@ -20,6 +20,8 @@
<application>
<uses-library android:name="android.test.runner" />
+ <service android:name="com.android.internal.os.BatteryUsageStatsPerfTest$BatteryUsageStatsService"
+ android:process=":BatteryUsageStatsService" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
deleted file mode 100644
index 6266cda204b0..000000000000
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryStatsHelperPerfTest.java
+++ /dev/null
@@ -1,135 +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.internal.os;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.os.BatteryStats;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class BatteryStatsHelperPerfTest {
-
- @Rule
- public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
- /**
- * Measures the performance of {@link BatteryStatsHelper#getStats()}, which triggers
- * a battery stats sync on every iteration.
- */
- @Test
- public void testGetStats_forceUpdate() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- state.pauseTiming();
- statsHelper.clearStats();
- state.resumeTiming();
-
- statsHelper.getStats();
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- /**
- * Measures performance of the {@link BatteryStatsHelper#getStats(boolean)}, which does
- * not trigger a sync and just returns current values.
- */
- @Test
- public void testGetStats_cached() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- state.pauseTiming();
- statsHelper.clearStats();
- state.resumeTiming();
-
- statsHelper.getStats(false /* forceUpdate */);
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- @Test
- public void testPowerCalculation() {
- final Context context = InstrumentationRegistry.getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.getStats();
-
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- // This will use the cached BatteryStatsObject
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- assertThat(statsHelper.getUsageList()).isNotEmpty();
- }
- }
-
- @Test
- public void testEndToEnd() {
- final Context context = InstrumentationRegistry.getContext();
- final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
- while (state.keepRunning()) {
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- statsHelper.create((Bundle) null);
- statsHelper.clearStats();
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.myUserId());
-
- state.pauseTiming();
-
- List<BatterySipper> usageList = statsHelper.getUsageList();
- double power = 0;
- for (int i = 0; i < usageList.size(); i++) {
- BatterySipper sipper = usageList.get(i);
- power += sipper.sumPower();
- }
-
- assertThat(power).isGreaterThan(0.0);
-
- state.resumeTiming();
- }
- }
-}
diff --git a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
index 54d70478f762..fe2fe0b40891 100644
--- a/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
+++ b/tests/BatteryStatsPerfTest/src/com/android/internal/os/BatteryUsageStatsPerfTest.java
@@ -18,13 +18,25 @@ package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
+import android.app.Service;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.BatteryConsumer;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Binder;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -54,7 +66,8 @@ public class BatteryUsageStatsPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats();
+ BatteryUsageStats batteryUsageStats = batteryStatsManager.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder().setMaxStatsAgeMs(0).build());
state.pauseTiming();
@@ -71,4 +84,118 @@ public class BatteryUsageStatsPerfTest {
state.resumeTiming();
}
}
+
+ private final ConditionVariable mServiceConnected = new ConditionVariable();
+ private IBinder mService;
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = service;
+ mServiceConnected.open();
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ /**
+ * Measures the performance of transferring BatteryUsageStats over a Binder.
+ */
+ @Test
+ public void testBatteryUsageStatsTransferOverBinder() throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ context.bindService(
+ new Intent(context, BatteryUsageStatsService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ mServiceConnected.block(30000);
+ assertThat(mService).isNotNull();
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final Parcel data = Parcel.obtain();
+ final Parcel reply = Parcel.obtain();
+ mService.transact(42, data, reply, 0);
+ final BatteryUsageStats batteryUsageStats =
+ BatteryUsageStats.CREATOR.createFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+
+ state.pauseTiming();
+
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getUidBatteryConsumers()).hasSize(1000);
+ final UidBatteryConsumer uidBatteryConsumer =
+ batteryUsageStats.getUidBatteryConsumers().get(0);
+ assertThat(uidBatteryConsumer.getConsumedPower(1)).isEqualTo(123);
+
+ state.resumeTiming();
+ }
+
+ context.unbindService(mConnection);
+ }
+
+ /* This service runs in a separate process */
+ public static class BatteryUsageStatsService extends Service {
+ private final BatteryUsageStats mBatteryUsageStats;
+
+ public BatteryUsageStatsService() {
+ mBatteryUsageStats = buildBatteryUsageStats();
+ }
+
+ @Nullable
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new Binder() {
+ @Override
+ protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
+ int flags) {
+ mBatteryUsageStats.writeToParcel(reply, 0);
+ return true;
+ }
+ };
+ }
+ }
+
+ private static BatteryUsageStats buildBatteryUsageStats() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[]{"FOO"}, true, false)
+ .setBatteryCapacity(4000)
+ .setDischargePercentage(20)
+ .setDischargedPowerRange(1000, 2000)
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
+
+ builder.getAggregateBatteryConsumerBuilder(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
+ .setConsumedPower(123)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10100)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10300)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
+
+ for (int i = 0; i < 1000; i++) {
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(i)
+ .setPackageWithHighestDrain("example.packagename" + i)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, i * 2000)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, i * 1000);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ consumerBuilder.setConsumedPower(componentId, componentId * 123.0,
+ BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+ consumerBuilder.setUsageDurationMillis(componentId, componentId * 1000);
+ }
+
+ consumerBuilder.setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 1234)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 4321);
+ }
+ return builder.build();
+ }
}
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
index 56db4f98e160..f4692983675d 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/FakeBlobData.java
@@ -17,6 +17,7 @@ package com.android.utils.blob;
import static com.android.utils.blob.Utils.BUFFER_SIZE_BYTES;
import static com.android.utils.blob.Utils.copy;
+import static com.android.utils.blob.Utils.writeRandomData;
import static com.google.common.truth.Truth.assertThat;
@@ -123,7 +124,7 @@ public class FakeBlobData {
public void prepare() throws Exception {
try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) {
- writeRandomData(file, mFileSize);
+ writeRandomData(file, mRandom, mFileSize);
}
mFileDigest = FileUtils.digest(mFile, "SHA-256");
mExpiryTimeMs = System.currentTimeMillis() + mExpiryDurationMs;
@@ -239,18 +240,4 @@ public class FakeBlobData {
}
return digest.digest();
}
-
- private void writeRandomData(RandomAccessFile file, long fileSize)
- throws Exception {
- long bytesWritten = 0;
- final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
- while (bytesWritten < fileSize) {
- mRandom.nextBytes(buffer);
- final int toWrite = (bytesWritten + buffer.length <= fileSize)
- ? buffer.length : (int) (fileSize - bytesWritten);
- file.seek(bytesWritten);
- file.write(buffer, 0, toWrite);
- bytesWritten += toWrite;
- }
- }
}
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
index 2d230a74a477..f6c0e6dbcf22 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/Utils.java
@@ -30,11 +30,14 @@ import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.util.Random;
public class Utils {
public static final String TAG = "BlobStoreTest";
@@ -164,6 +167,25 @@ public class Utils {
runShellCmd("cmd blob_store idle-maintenance");
}
+ public static void writeRandomData(File file, long fileSizeBytes)
+ throws Exception {
+ writeRandomData(new RandomAccessFile(file, "rw"), new Random(0), fileSizeBytes);
+ }
+
+ public static void writeRandomData(RandomAccessFile file, Random random, long fileSizeBytes)
+ throws Exception {
+ long bytesWritten = 0;
+ final byte[] buffer = new byte[BUFFER_SIZE_BYTES];
+ while (bytesWritten < fileSizeBytes) {
+ random.nextBytes(buffer);
+ final int toWrite = (bytesWritten + buffer.length <= fileSizeBytes)
+ ? buffer.length : (int) (fileSizeBytes - bytesWritten);
+ file.seek(bytesWritten);
+ file.write(buffer, 0, toWrite);
+ bytesWritten += toWrite;
+ }
+ }
+
public static String runShellCmd(String cmd) throws IOException {
final UiDevice uiDevice = UiDevice.getInstance(
InstrumentationRegistry.getInstrumentation());
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
index 7ee0d9a5e77e..57303e748738 100644
--- a/tests/BootImageProfileTest/OWNERS
+++ b/tests/BootImageProfileTest/OWNERS
@@ -1,4 +1,4 @@
calin@google.com
-mathieuc@google.com
ngeoffray@google.com
+vmarko@google.com
yawanng@google.com
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 4ecca2dc4c39..cf5658644a61 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -31,6 +31,8 @@ public class BootImageProfileTest implements IDeviceTest {
private static final String SYSTEM_SERVER_PROFILE =
"/data/misc/profiles/cur/0/android/primary.prof";
private static final boolean USE_PHENOTYPE = false;
+ private static final String DALVIK_VM_EXTRA_OPTS =
+ "-Xusejit:false -Xint -Xjitsaveprofilinginfo";
@Override
public void setDevice(ITestDevice testDevice) {
@@ -54,10 +56,10 @@ public class BootImageProfileTest implements IDeviceTest {
private String setProperty(String property, String value) throws Exception {
if (USE_PHENOTYPE) {
return mTestDevice.executeShellCommand(
- "device_config put runtime_native_boot " + property + " " + value);
+ String.format("device_config put runtime_native_boot %s '%s'", property, value));
} else {
return mTestDevice.executeShellCommand(
- "setprop dalvik.vm." + property + " " + value);
+ String.format("setprop dalvik.vm.%s '%s'", property, value));
}
}
@@ -69,6 +71,8 @@ public class BootImageProfileTest implements IDeviceTest {
assertTrue("profile boot class path not enabled: " + res, "true".equals(res));
res = getProperty("profilesystemserver");
assertTrue("profile system server not enabled: " + res, "true".equals(res));
+ res = getProperty("extra-opts");
+ assertTrue("extra options not set: " + res, DALVIK_VM_EXTRA_OPTS.equals(res));
}
private boolean forceSaveProfile(String pkg) throws Exception {
@@ -91,16 +95,20 @@ public class BootImageProfileTest implements IDeviceTest {
boolean profileBootClassPath = "true".equals(pbcp);
String pss = getProperty("profilesystemserver");
boolean profileSystemServer = "true".equals(pss);
- if (profileBootClassPath && profileSystemServer) {
+ String extraOpts = getProperty("extra-opts");
+ boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts);
+ if (profileBootClassPath && profileSystemServer && extraOptsOk) {
break;
}
if (i == numIterations) {
assertTrue("profile system server not enabled: " + pss, profileSystemServer);
assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ assertTrue("extra options not set: " + extraOpts, extraOptsOk);
}
setProperty("profilebootclasspath", "true");
setProperty("profilesystemserver", "true");
+ setProperty("extra-opts", DALVIK_VM_EXTRA_OPTS);
Thread.sleep(1000);
}
@@ -114,12 +122,15 @@ public class BootImageProfileTest implements IDeviceTest {
boolean profileBootClassPath = "true".equals(pbcp);
String pss = getProperty("profilesystemserver");
boolean profileSystemServer = "true".equals(pss);
+ String extraOpts = getProperty("extra-opts");
+ boolean extraOptsOk = DALVIK_VM_EXTRA_OPTS.equals(extraOpts);
if (profileBootClassPath && profileSystemServer) {
break;
}
if (i == numIterations) {
assertTrue("profile system server not enabled: " + pss, profileSystemServer);
assertTrue("profile boot class path not enabled: " + pbcp, profileBootClassPath);
+ assertTrue("extra options not set: " + extraOpts, extraOptsOk);
}
Thread.sleep(1000);
}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/.project b/tests/Camera2Tests/SmartCamera/SimpleCamera/.project
deleted file mode 100644
index 2517e2d4f93d..000000000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>CameraShoot</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
new file mode 100644
index 000000000000..4fff969359c8
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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: "SmartCamera",
+ optimize: {
+ enabled: false,
+ },
+ // comment it out for now since we need use some hidden APIs
+ sdk_version: "current",
+ static_libs: ["android-ex-camera2"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ jni_libs: ["libsmartcamera_jni"],
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
deleted file mode 100644
index 6003628ffb0d..000000000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-ifneq ($(TARGET_BUILD_JAVA_SUPPORT_LEVEL),)
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-# comment it out for now since we need use some hidden APIs
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := SmartCamera
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE
-LOCAL_JNI_SHARED_LIBRARIES := libsmartcamera_jni
-
-include $(BUILD_PACKAGE)
-
-# Include packages in subdirectories
-include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java
index aa5739414df4..00fe6f2f866e 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/MediaDecoder.java
@@ -24,10 +24,12 @@ import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
+
import androidx.media.filterfw.FrameImage2D;
import androidx.media.filterfw.FrameValue;
import androidx.media.filterfw.RenderTarget;
+import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
@TargetApi(16)
@@ -276,12 +278,13 @@ public class MediaDecoder implements
}
@TargetApi(17)
- private void retrieveDefaultRotation() {
+ private void retrieveDefaultRotation() throws IOException {
MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
metadataRetriever.setDataSource(mContext, mUri);
String rotationString = metadataRetriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
mDefaultRotation = rotationString == null ? 0 : Integer.parseInt(rotationString);
+ metadataRetriever.release();
}
private void onStop(boolean notifyListener) {
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
new file mode 100644
index 000000000000..5edb1de9586e
--- /dev/null
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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: "SmartCamera-tests",
+ platform_apis: true,
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.base"],
+ static_libs: [
+ "guava",
+ "junit",
+ ],
+ optimize: {
+ enabled: false,
+ },
+ instrumentation_for: "SmartCamera",
+}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
deleted file mode 100644
index c23d593d4f86..000000000000
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_PACKAGE_NAME := SmartCamera-tests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../../NOTICE
-
-LOCAL_SRC_FILES += $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := guava junit
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_INSTRUMENTATION_FOR := SmartCamera
-
-include $(BUILD_PACKAGE)
diff --git a/tests/CanvasCompare/Android.bp b/tests/CanvasCompare/Android.bp
new file mode 100644
index 000000000000..98831154ddc2
--- /dev/null
+++ b/tests/CanvasCompare/Android.bp
@@ -0,0 +1,63 @@
+//
+// 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/Android.mk b/tests/CanvasCompare/Android.mk
deleted file mode 100644
index b82ae65b4356..000000000000
--- a/tests/CanvasCompare/Android.mk
+++ /dev/null
@@ -1,33 +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.
-#
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := CanvasCompare
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-include $(BUILD_PACKAGE)
diff --git a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
index 4ec86b186285..56848b89be6f 100644
--- a/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
+++ b/tests/DataIdleTest/src/com/android/tests/dataidle/DataIdleTest.java
@@ -15,20 +15,19 @@
*/
package com.android.tests.dataidle;
+import static android.net.NetworkStats.METERED_YES;
+
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStats;
-import android.net.NetworkStats.Entry;
import android.net.NetworkTemplate;
-import android.net.TrafficStats;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.telephony.TelephonyManager;
import android.test.InstrumentationTestCase;
import android.util.Log;
+import java.util.Set;
+
/**
* A test that dumps data usage to instrumentation out, used for measuring data usage for idle
* devices.
@@ -36,7 +35,7 @@ import android.util.Log;
public class DataIdleTest extends InstrumentationTestCase {
private TelephonyManager mTelephonyManager;
- private INetworkStatsService mStatsService;
+ private NetworkStatsManager mStatsManager;
private static final String LOG_TAG = "DataIdleTest";
private final static int INSTRUMENTATION_IN_PROGRESS = 2;
@@ -44,8 +43,7 @@ public class DataIdleTest extends InstrumentationTestCase {
protected void setUp() throws Exception {
super.setUp();
Context c = getInstrumentation().getTargetContext();
- mStatsService = INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ mStatsManager = c.getSystemService(NetworkStatsManager.class);
mTelephonyManager = (TelephonyManager) c.getSystemService(Context.TELEPHONY_SERVICE);
}
@@ -53,7 +51,9 @@ public class DataIdleTest extends InstrumentationTestCase {
* Test that dumps all the data usage metrics for wifi to instrumentation out.
*/
public void testWifiIdle() {
- NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
+ final NetworkTemplate template = new NetworkTemplate
+ .Builder(NetworkTemplate.MATCH_WIFI)
+ .build();
fetchStats(template);
}
@@ -61,8 +61,11 @@ public class DataIdleTest extends InstrumentationTestCase {
* Test that dumps all the data usage metrics for all mobile to instrumentation out.
*/
public void testMobile() {
- String subscriberId = mTelephonyManager.getSubscriberId();
- NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+ final String subscriberId = mTelephonyManager.getSubscriberId();
+ NetworkTemplate template = new NetworkTemplate
+ .Builder(NetworkTemplate.MATCH_MOBILE)
+ .setMeteredness(METERED_YES)
+ .setSubscriberIds(Set.of(subscriberId)).build();
fetchStats(template);
}
@@ -72,49 +75,26 @@ public class DataIdleTest extends InstrumentationTestCase {
* @param template {@link NetworkTemplate} to match.
*/
private void fetchStats(NetworkTemplate template) {
- INetworkStatsSession session = null;
try {
- mStatsService.forceUpdate();
- session = mStatsService.openSession();
- final NetworkStats stats = session.getSummaryForAllUid(
- template, Long.MIN_VALUE, Long.MAX_VALUE, false);
- reportStats(stats);
- } catch (RemoteException e) {
+ mStatsManager.forceUpdate();
+ final NetworkStats.Bucket bucket =
+ mStatsManager.querySummaryForDevice(template, Long.MIN_VALUE, Long.MAX_VALUE);
+ reportStats(bucket);
+ } catch (RuntimeException e) {
Log.w(LOG_TAG, "Failed to fetch network stats.");
- } finally {
- TrafficStats.closeQuietly(session);
}
}
/**
* Print network data usage stats to instrumentation out
- * @param stats {@link NetworkorStats} to print
+ * @param bucket {@link NetworkStats} to print
*/
- void reportStats(NetworkStats stats) {
+ void reportStats(NetworkStats.Bucket bucket) {
Bundle result = new Bundle();
- long rxBytes = 0;
- long txBytes = 0;
- long rxPackets = 0;
- long txPackets = 0;
- for (int i = 0; i < stats.size(); ++i) {
- // Label will be iface_uid_tag_set
- Entry statsEntry = stats.getValues(i, null);
- // Debugging use.
- /*
- String labelTemplate = String.format("%s_%d_%d_%d", statsEntry.iface, statsEntry.uid,
- statsEntry.tag, statsEntry.set) + "_%s";
- result.putLong(String.format(labelTemplate, "rxBytes"), statsEntry.rxBytes);
- result.putLong(String.format(labelTemplate, "txBytes"), statsEntry.txBytes);
- */
- rxPackets += statsEntry.rxPackets;
- rxBytes += statsEntry.rxBytes;
- txPackets += statsEntry.txPackets;
- txBytes += statsEntry.txBytes;
- }
- result.putLong("Total rx Bytes", rxBytes);
- result.putLong("Total tx Bytes", txBytes);
- result.putLong("Total rx Packets", rxPackets);
- result.putLong("Total tx Packets", txPackets);
+ result.putLong("Total rx Bytes", bucket.getRxBytes());
+ result.putLong("Total tx Bytes", bucket.getTxBytes());
+ result.putLong("Total rx Packets", bucket.getRxPackets());
+ result.putLong("Total tx Packets", bucket.getTxPackets());
getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, result);
}
diff --git a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
index 6985702c24e6..9a88abdd7b37 100644
--- a/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
+++ b/tests/DozeTest/src/com/android/dreams/dozetest/DozeTestDream.java
@@ -81,7 +81,8 @@ public class DozeTestDream extends DreamService {
intent.setPackage(getPackageName());
IntentFilter filter = new IntentFilter();
filter.addAction(intent.getAction());
- registerReceiver(mAlarmReceiver, filter);
+ registerReceiver(mAlarmReceiver, filter,
+ Context.RECEIVER_EXPORTED_UNAUDITED);
mAlarmIntent = PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
new file mode 100644
index 000000000000..448d46fe5e4e
--- /dev/null
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -0,0 +1,60 @@
+//
+// Copyright 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES 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"],
+}
+
+java_test_helper_library {
+ name: "DynamicCodeLoggerTestLibrary",
+ srcs: ["src/com/android/dcl/**/*.java"],
+
+}
+
+cc_library_shared {
+ name: "DynamicCodeLoggerNativeTestLibrary",
+ srcs: ["src/cpp/com_android_dcl_Jni.cpp"],
+ header_libs: ["jni_headers"],
+ sdk_version: "28",
+ stl: "c++_static",
+}
+
+cc_binary {
+ name: "DynamicCodeLoggerNativeExecutable",
+ srcs: ["src/cpp/test_executable.cpp"],
+}
+
+android_test {
+ name: "DynamicCodeLoggerIntegrationTests",
+
+ sdk_version: "current",
+ test_suites: ["device-tests"],
+ certificate: "shared",
+ srcs: ["src/com/android/server/pm/**/*.java"],
+
+ static_libs: [
+ "androidx.test.rules",
+ "truth-prebuilt",
+ ],
+
+ compile_multilib: "both",
+ jni_libs: ["DynamicCodeLoggerNativeTestLibrary"],
+
+ java_resources: [
+ ":DynamicCodeLoggerTestLibrary",
+ ":DynamicCodeLoggerNativeExecutable",
+ ],
+}
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
deleted file mode 100644
index dab83046c28f..000000000000
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT 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)
-
-# Build a tiny library that the test app can dynamically load
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerTestLibrary
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl)
-
-include $(BUILD_JAVA_LIBRARY)
-
-dynamiccodeloggertest_jar := $(LOCAL_BUILT_MODULE)
-
-
-# Also build a native library that the test app can dynamically load
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
-LOCAL_HEADER_LIBRARIES := jni_headers
-LOCAL_SDK_VERSION := 28
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_SHARED_LIBRARY)
-
-# And a standalone native executable that we can exec.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerNativeExecutable
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := src/cpp/test_executable.cpp
-
-include $(BUILD_EXECUTABLE)
-
-dynamiccodeloggertest_executable := $(LOCAL_BUILT_MODULE)
-
-# Build the test app itself
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := DynamicCodeLoggerIntegrationTests
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := shared
-LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.rules \
- truth-prebuilt \
-
-# Include both versions of the .so if we have 2 arch
-LOCAL_MULTILIB := both
-LOCAL_JNI_SHARED_LIBRARIES := \
- DynamicCodeLoggerNativeTestLibrary \
-
-# This gets us the javalib.jar built by DynamicCodeLoggerTestLibrary above as well as the various
-# native binaries.
-LOCAL_JAVA_RESOURCE_FILES := \
- $(dynamiccodeloggertest_jar) \
- $(dynamiccodeloggertest_executable) \
-
-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/DynamicCodeLoggerIntegrationTests/OWNERS b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
new file mode 100644
index 000000000000..d9eb1413cb1e
--- /dev/null
+++ b/tests/DynamicCodeLoggerIntegrationTests/OWNERS
@@ -0,0 +1 @@
+file:/services/core/java/com/android/server/pm/dex/OWNERS
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 883c172e4990..5430dee5ca31 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
+++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
@@ -114,7 +114,8 @@ public final class DynamicCodeLoggerIntegrationTests {
// Obtained via "echo -n copied.jar | sha256sum"
String expectedNameHash =
"1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
- String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
+ String expectedContentHash = copyAndHashResource(
+ "/DynamicCodeLoggerTestLibrary.jar", privateCopyFile);
// Feed the jar to a class loader and make sure it contains what we expect.
ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
@@ -135,7 +136,8 @@ public final class DynamicCodeLoggerIntegrationTests {
File privateCopyFile = privateFile("copied2.jar");
String expectedNameHash =
"202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
- String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
+ String expectedContentHash = copyAndHashResource(
+ "/DynamicCodeLoggerTestLibrary.jar", privateCopyFile);
// This time make sure an unknown class loader is an ancestor of the class loader we use.
ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 896ec9ae922c..566c725a3414 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -16,10 +16,6 @@
<!-- restart launcher to activate TAPL -->
<option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
- <!-- reboot the device to teardown any crashed tests -->
- <option name="cleanup-action" value="REBOOT" />
- </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="FlickerTests.apk"/>
diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS
index b5561010e7f9..d40ff5659708 100644
--- a/tests/FlickerTests/OWNERS
+++ b/tests/FlickerTests/OWNERS
@@ -1,3 +1,4 @@
-# Bug component: 909476
+# Bug component: 1157642
include /services/core/java/com/android/server/wm/OWNERS
-natanieljr@google.com \ No newline at end of file
+natanieljr@google.com
+pablogamito@google.com
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 64cb790d324b..7f309e1974e1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -102,28 +102,59 @@ fun FlickerTestParameter.statusBarLayerIsVisible() {
}
}
-fun FlickerTestParameter.navBarLayerRotatesAndScales() {
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
+ * of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerPositionStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display))
}
+}
+
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the end
+ * of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerPositionEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.NAV_BAR)
.coversExactly(WindowUtils.getNavigationBarPosition(display))
}
}
-fun FlickerTestParameter.statusBarLayerRotatesScales() {
+/**
+ * Asserts that the [FlickerComponentName.NAV_BAR] layer is at the correct position at the start
+ * and end of the SF trace
+ */
+fun FlickerTestParameter.navBarLayerRotatesAndScales() {
+ navBarLayerPositionStart()
+ navBarLayerPositionEnd()
+}
+
+/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerPositionStart() {
assertLayersStart {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
this.visibleRegion(FlickerComponentName.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
+}
+
+/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the end
+ * of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerPositionEnd() {
assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
?: throw RuntimeException("There is no display!")
@@ -133,6 +164,15 @@ fun FlickerTestParameter.statusBarLayerRotatesScales() {
}
/**
+ * Asserts that the [FlickerComponentName.STATUS_BAR] layer is at the correct position at the start
+ * and end of the SF trace
+ */
+fun FlickerTestParameter.statusBarLayerRotatesScales() {
+ statusBarLayerPositionStart()
+ statusBarLayerPositionEnd()
+}
+
+/**
* Asserts that:
* [originalLayer] is visible at the start of the trace
* [originalLayer] becomes invisible during the trace and (in the same entry) [newLayer]
@@ -141,20 +181,36 @@ fun FlickerTestParameter.statusBarLayerRotatesScales() {
*
* @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
* (useful mostly for app launch)
+ * @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,
- ignoreSnapshot: Boolean = false
+ ignoreEntriesWithRotationLayer: Boolean = false,
+ ignoreSnapshot: Boolean = false,
+ ignoreSplashscreen: Boolean = true
) {
assertLayers {
val assertion = this.isVisible(originalLayer)
+
+ if (ignoreEntriesWithRotationLayer) {
+ assertion.then().isVisible(FlickerComponentName.ROTATION, isOptional = true)
+ }
if (ignoreSnapshot) {
- assertion.then()
- .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ assertion.then().isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ }
+ if (ignoreSplashscreen) {
+ assertion.then().isSplashScreenVisibleFor(newLayer, isOptional = true)
}
+
assertion.then().isVisible(newLayer)
}
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 9f26c31a6d63..20a2e2228d7a 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
@@ -24,6 +24,9 @@ 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
@@ -65,9 +68,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
device.pressBack()
wmHelper.waitForHomeActivityVisible()
@@ -79,6 +82,28 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
@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()
+
companion object {
/**
* Creates the test configurations.
@@ -90,7 +115,7 @@ class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(repetitions = 3)
}
}
} \ No newline at end of file
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 795766fccfbd..d00fd7d7f09b 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,6 +16,7 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
@@ -64,9 +65,9 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
transitions {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
@@ -78,6 +79,33 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
@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)
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
companion object {
/**
* Creates the test configurations.
@@ -89,7 +117,7 @@ class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransitio
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(repetitions = 3)
}
}
} \ No newline at end of file
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 511fc26fdb38..aaa2db768792 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
@@ -30,7 +30,6 @@ 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.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -47,16 +46,16 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
/**
* Specification of the test transition to execute
*/
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
eachRun {
testApp.launchViaIntent(wmHelper)
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
}
@@ -68,7 +67,7 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
@@ -189,4 +188,4 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter)
open fun launcherLayerReplacesApp() {
testSpec.replacesLayer(testApp.component, LAUNCHER_COMPONENT)
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
new file mode 100644
index 000000000000..f7c0a87f5dac
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
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
new file mode 100644
index 000000000000..2dbf304a0f23
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -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.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+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
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
index 0b1748a6bda4..aacc17a49a24 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -16,13 +16,21 @@
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,
@@ -39,6 +47,16 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
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
@@ -47,4 +65,52 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
}
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 7ee6451b2797..5bd365c7eefd 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
@@ -64,7 +64,6 @@ open class ImeAppHelper @JvmOverloads constructor(
device.waitForIdle()
} else {
wmHelper.waitImeShown()
- wmHelper.waitForAppTransitionIdle()
}
}
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
new file mode 100644
index 000000000000..172c4330c3c6
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -0,0 +1,60 @@
+/*
+ * 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 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(
+ 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()
+) : ImeAppHelper(instr, launcherName, component) {
+ override fun openIME(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper?
+ ) {
+ val editText = device.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
+
+ require(editText != null) {
+ "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)
+ }
+
+ fun dismissDialog(wmHelper: WindowManagerStateHelper) {
+ 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()
+ }
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000000..16c4c254f9e3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -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.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+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(
+ 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
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
new file mode 100644
index 000000000000..4e360f98723e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -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.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+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(
+ 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)
+
+ require(button != null) {
+ "Post notification button not found, this usually happens when the device " +
+ "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")
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000000..bd2e5756b4a9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -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.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+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(
+ 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
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 59e8dc826007..a135e0af067b 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
@@ -19,11 +19,13 @@ 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 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
@@ -36,17 +38,29 @@ class TwoActivitiesAppHelper @JvmOverloads constructor(
.getInstance(instr)
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ private val secondActivityComponent =
+ ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
- val button = device.wait(
- Until.findObject(By.res(getPackage(), "launch_second_activity")),
- FIND_TIMEOUT)
+ val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
+ val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
require(button != null) {
"Button not found, this usually happens when the device " +
"was left in an unknown state (e.g. in split screen)"
}
button.click()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(component)
+
+ device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
+ wmHelper.waitFor(
+ WindowManagerStateHelper.isAppFullScreen(secondActivityComponent),
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerConditionsFactory.hasLayersAnimating().negate()
+ )
+ }
+
+ 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
index 5e21aff94769..dd5f33fb8669 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -20,6 +20,7 @@ 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
@@ -33,7 +34,6 @@ 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.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -63,7 +63,7 @@ import org.junit.runners.Parameterized
@Group2
class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -153,7 +153,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -170,7 +170,7 @@ class CloseImeAutoOpenWindowToAppTest(private val testSpec: FlickerTestParameter
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5,
+ .getConfigNonRotationTests(repetitions = 3,
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
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
index 0582685f2c54..5606965a6d40 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -34,7 +34,6 @@ 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.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -64,7 +63,7 @@ import org.junit.runners.Parameterized
@Group2
class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
@@ -104,7 +103,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
}
}
- @FlakyTest(bugId = 190189685)
+ @Presubmit
@Test
fun imeAppWindowBecomesInvisible() {
testSpec.assertWm {
@@ -152,7 +151,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -179,7 +178,7 @@ class CloseImeAutoOpenWindowToHomeTest(private val testSpec: FlickerTestParamete
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 1,
+ .getConfigNonRotationTests(repetitions = 3,
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
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
new file mode 100644
index 000000000000..6257484be9bd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeEditorPopupDialogTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 91b3d3dae3cd..e7a1c50821b7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -35,7 +35,8 @@ 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
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -119,18 +120,18 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun navBarLayerRotatesAndScales() {
- Assume.assumeFalse(testSpec.isRotated)
+ assumeFalse(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
@FlakyTest
@Test
fun navBarLayerRotatesAndScales_Flaky() {
- Assume.assumeTrue(testSpec.isRotated)
+ assumeTrue(testSpec.isLandscapeOrSeascapeAtStart)
testSpec.navBarLayerRotatesAndScales()
}
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -159,7 +160,7 @@ class CloseImeWindowToAppTest(private val testSpec: FlickerTestParameter) {
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .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
index b589969dee14..b454f0155b3e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -104,7 +104,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Test
fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
- @FlakyTest
+ @Presubmit
@Test
fun imeAppWindowBecomesInvisible() {
testSpec.assertWm {
@@ -144,7 +144,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -164,7 +164,7 @@ class CloseImeWindowToHomeTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
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
new file mode 100644
index 000000000000..2f8f9441a7b9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeAndDialogThemeAppTest.kt
@@ -0,0 +1,132 @@
+/*
+ * 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
index a9568b325af2..b897ca2a9c15 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/LaunchAppShowImeOnStartTest.kt
@@ -28,8 +28,8 @@ 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.flicker.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
import org.junit.Test
@@ -71,18 +71,21 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation)
+ private val initializeApp = ImeStateInitializeHelper(instrumentation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
eachRun {
- this.setRotation(testSpec.config.startRotation)
+ initializeApp.launchViaIntent()
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
eachRun {
+ initializeApp.exit()
testApp.exit()
}
}
@@ -141,7 +144,7 @@ class LaunchAppShowImeOnStartTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
new file mode 100644
index 000000000000..301fafa5309e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
@@ -0,0 +1,2 @@
+# ime
+# Bug component: 34867
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
new file mode 100644
index 000000000000..78aea1f1fb17
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt
@@ -0,0 +1,122 @@
+/*
+ * 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
index 7bf0186cd857..8fcb4b7c03f1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -126,7 +126,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -138,7 +138,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
}
- @FlakyTest
+ @Presubmit
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
testSpec.assertWm {
@@ -152,7 +152,7 @@ class OpenImeWindowTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedRotations = listOf(Surface.ROTATION_0),
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY,
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
new file mode 100644
index 000000000000..0454ca0a607e
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToOverViewTest.kt
@@ -0,0 +1,238 @@
+/*
+ * 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
index f6febe9e2234..e7a33543a86b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -17,10 +17,10 @@
package com.android.server.wm.flicker.ime
import android.app.Instrumentation
-import android.os.SystemProperties
import android.platform.test.annotations.Presubmit
+import android.view.Display
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
@@ -37,12 +37,16 @@ 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.startRotation
+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 org.junit.Assume
+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
@@ -58,11 +62,20 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group2
-class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
+open class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- private val testApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
- private val isShellTransitionsEnabled =
- SystemProperties.getBoolean("persist.debug.shell_transit", false)
+ 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 {
@@ -74,9 +87,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
eachRun {
device.pressRecentApps()
- wmHelper.waitImeGone()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(testSpec.config.startRotation)
+ wmHelper.waitFor(waitConditionSetup)
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
@@ -129,7 +141,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun imeAppWindowVisibilityLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
+ 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.
@@ -144,10 +156,10 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
}
- @Presubmit
+ @FlakyTest(bugId = 204570898)
@Test
fun imeAppWindowVisibility() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ 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
@@ -173,7 +185,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Presubmit
@Test
fun imeLayerIsBecomesVisibleLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
+ assumeFalse(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(FlickerComponentName.IME)
.then()
@@ -183,10 +195,10 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
}
}
- @Presubmit
+ @FlakyTest(bugId = 204570898)
@Test
fun imeLayerIsBecomesVisible() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ assumeTrue(isShellTransitionsEnabled)
testSpec.assertLayers {
this.isVisible(FlickerComponentName.IME)
}
@@ -208,7 +220,7 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
@Test
fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -232,11 +244,8 @@ class ReOpenImeWindowTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 1,
- supportedRotations = listOf(Surface.ROTATION_0),
- supportedNavigationModes = listOf(
- WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
- )
+ repetitions = 3,
+ supportedRotations = listOf(Surface.ROTATION_0)
)
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt
new file mode 100644
index 000000000000..7ffa51320487
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest_ShellTransit.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.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 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)
+ }
+}
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
index 4c506b0fea4d..4b268a871fa0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest.kt
@@ -18,6 +18,7 @@ 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
@@ -31,11 +32,15 @@ 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.startRotation
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
@@ -53,24 +58,39 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
@Presubmit
-class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
+open class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
- private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.config.startRotation)
+ 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.config.startRotation)
+ this.setRotation(testSpec.startRotation)
testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
+ 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)
- wmHelper.waitForFullScreenApp(testApp.component)
- wmHelper.waitForAppTransitionIdle()
+ 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)
}
@@ -86,9 +106,9 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
transitions {
// [Step1]: Swipe right from imeTestApp to testApp task
createTag(TAG_IME_VISIBLE)
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- device.swipe(0, displayBounds.bounds.height(),
- displayBounds.bounds.width(), displayBounds.bounds.height(), 50)
+ 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()
@@ -96,9 +116,9 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
}
transitions {
// [Step2]: Swipe left to back to imeTestApp task
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
- device.swipe(displayBounds.bounds.width(), displayBounds.bounds.height(),
- 0, displayBounds.bounds.height(), 50)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
+ device.swipe(displayBounds.bounds.width, displayBounds.bounds.height,
+ 0, displayBounds.bounds.height, 50)
wmHelper.waitForFullScreenApp(imeTestApp.component)
}
}
@@ -109,8 +129,12 @@ class SwitchImeWindowsFromGestureNavTest(private val testSpec: FlickerTestParame
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)
}
}
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/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
new file mode 100644
index 000000000000..edd52b76cd5c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/SwitchImeWindowsFromGestureNavTest_ShellTransit.kt
@@ -0,0 +1,49 @@
+/*
+ * 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 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 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)
+ }
+}
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 f74a7718461f..cc808a0ce871 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
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.launch
import android.app.Instrumentation
import android.platform.test.annotations.Presubmit
+import android.view.Display
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.entireScreenCovered
@@ -26,12 +27,13 @@ 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.repetitions
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
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
@@ -60,7 +62,7 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
- val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/**
@@ -70,24 +72,24 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
- eachRun {
+ test {
testApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
transitions {
testApp.openSecondActivity(device, wmHelper)
device.pressBack()
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(testApp.component)
+ val firstActivityVisible = wmHelper.waitFor(
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerStateHelper.isAppFullScreen(testApp.component))
+ require(firstActivityVisible) { "Expected ${testApp.component} to be visible" }
}
}
}
@@ -155,7 +157,7 @@ class ActivitiesTransitionTest(val testSpec: FlickerTestParameter) {
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(repetitions = 3)
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
new file mode 100644
index 000000000000..2c414a27cacb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
@@ -0,0 +1,4 @@
+# System UI > ... > Overview (recent apps) > UI
+# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview*
+# window manager > animations/transitions
+# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index be919cd67c1e..2f6b8f008119 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,17 +16,15 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Postsubmit
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+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.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+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
@@ -56,17 +54,18 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+open class OpenAppColdTest(testSpec: FlickerTestParameter)
+ : OpenAppFromLauncherTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
eachRun {
removeAllTasksButHome()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -81,6 +80,11 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
}
/** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ /** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() {
@@ -88,12 +92,12 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
}
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
- @Postsubmit
+ @Presubmit
@Test
override fun appWindowReplacesLauncherAsTopWindow() =
super.appWindowReplacesLauncherAsTopWindow()
@@ -101,17 +105,17 @@ class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
/**
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
new file mode 100644
index 000000000000..c6e92adce8c7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -0,0 +1,71 @@
+/*
+ * 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.launch
+
+import android.platform.test.annotations.Presubmit
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.LAUNCHER_COMPONENT
+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) {
+
+ /**
+ * Checks that the focus changes from the launcher to [testApp]
+ */
+ @Presubmit
+ @Test
+ open fun focusChanges() {
+ testSpec.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
+ */
+ open fun appLayerReplacesLauncher() {
+ testSpec.replacesLayer(LAUNCHER_COMPONENT, testApp.component,
+ 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
+ */
+ @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)
+ }
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000000..a8cbc5dc922c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -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.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 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 a notification from the lock screen.
+ *
+ * This test assumes the device doesn't have AOD enabled
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@Postsubmit
+open class OpenAppFromLockNotificationCold(testSpec: FlickerTestParameter)
+ : OpenAppFromNotificationCold(testSpec) {
+
+ override val openingNotificationsFromLockScreen = true
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ // Needs to run at start of transition,
+ // so before the transition defined in super.transition
+ transitions {
+ device.wakeUp()
+ }
+
+ super.transition(this)
+
+ // 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()
+ }
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+ 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 com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 3)
+ }
+ }
+} \ 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
new file mode 100644
index 000000000000..cd8dea012db5
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.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.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 org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test warm launching an app from a notification from the lock screen.
+ *
+ * This test assumes the device doesn't have AOD enabled
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@Postsubmit
+open class OpenAppFromLockNotificationWarm(testSpec: FlickerTestParameter)
+ : OpenAppFromNotificationWarm(testSpec) {
+
+ override val openingNotificationsFromLockScreen = true
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ // Needs to run at start of transition,
+ // so before the transition defined in super.transition
+ transitions {
+ device.wakeUp()
+ }
+
+ super.transition(this)
+
+ // 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()
+ }
+ }
+ }
+ }
+
+ /**
+ * 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 {
+ this.hasNoVisibleAppWindow()
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the screen is locked.
+ */
+ @Test
+ @Postsubmit
+ fun screenLockedStart() {
+ testSpec.assertLayersStart {
+ isEmpty()
+ }
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ 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 com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 3)
+ }
+ }
+} \ 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
new file mode 100644
index 000000000000..bc637f8f9a63
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.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.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
+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.
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
+ */
+@RequiresDevice
+@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)
+
+ // Although we are technically still locked here, the overlay app means we should open the
+ // notification shade as if we were unlocked.
+ override val openingNotificationsFromLockScreen = false
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+
+ 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()
+ }
+ }
+ }
+
+ teardown {
+ test {
+ showWhenLockedApp.exit(wmHelper)
+ }
+ }
+ }
+
+ @Test
+ @Postsubmit
+ fun showWhenLockedAppWindowBecomesVisible() {
+ testSpec.assertWm {
+ this.hasNoVisibleAppWindow()
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(showWhenLockedApp.component)
+ }
+ }
+
+ @Test
+ @Postsubmit
+ fun showWhenLockedAppLayerBecomesVisible() {
+ testSpec.assertLayers {
+ this.isInvisible(showWhenLockedApp.component)
+ .then()
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(showWhenLockedApp.component)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @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.
+ */
+ @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/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
new file mode 100644
index 000000000000..fe80162b5b81
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.launch
+
+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 org.junit.Test
+
+/**
+ * Base class for app launch tests from lock screen
+ */
+abstract class OpenAppFromLockTransition(testSpec: FlickerTestParameter)
+ : OpenAppTransition(testSpec) {
+
+ /**
+ * 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)
+ }
+ }
+
+ /**
+ * Check that we go from no focus to focus on the [testApp]
+ */
+ @Presubmit
+ @Test
+ open fun focusChanges() {
+ testSpec.assertEventLog {
+ this.focusChanges("", testApp.`package`)
+ }
+ }
+
+ /**
+ * 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.
+ */
+ @FlakyTest(bugId = 203538234)
+ @Test
+ open fun appWindowBecomesFirstAndOnlyTopWindow() {
+ testSpec.assertWm {
+ this.hasNoVisibleAppWindow()
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowOnTop(testApp.component)
+ }
+ }
+
+ /**
+ * Checks that the screen is locked at the start of the transition ([colorFadComponent])
+ * layer is visible
+ */
+ @Presubmit
+ @Test
+ fun screenLockedStart() {
+ testSpec.assertLayersStart {
+ isEmpty()
+ }
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 203538234)
+ @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
+ */
+ @Presubmit
+ @Test
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
+
+ /**
+ * Checks that the 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)
+ }
+ }
+}
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
new file mode 100644
index 000000000000..5022dd8f9bff
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
@@ -0,0 +1,84 @@
+/*
+ * 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.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 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 a notification.
+ *
+ * This test assumes the device doesn't have AOD enabled
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@Postsubmit
+open class OpenAppFromNotificationCold(testSpec: FlickerTestParameter)
+ : OpenAppFromNotificationWarm(testSpec) {
+ 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()
+ }
+ }
+ }
+
+ @Test
+ @Postsubmit
+ override fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+
+ @Test
+ @Postsubmit
+ override fun appLayerBecomesVisible() = appLayerBecomesVisible_coldStart()
+
+ 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 com.android.server.wm.flicker.FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests(repetitions = 3)
+ }
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000000..812f6859c7b0
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -0,0 +1,197 @@
+/*
+ * 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.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.view.WindowInsets
+import android.view.WindowManager
+import androidx.test.filters.FlakyTest
+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 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 cold launching an app from a notification.
+ *
+ * This test assumes the device doesn't have AOD enabled
+ *
+ * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+@Postsubmit
+open class OpenAppFromNotificationWarm(testSpec: FlickerTestParameter)
+ : OpenAppTransition(testSpec) {
+ protected val taplInstrumentation = LauncherInstrumentation()
+
+ override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
+
+ open val openingNotificationsFromLockScreen = false
+
+ 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()
+ }
+ }
+
+ transitions {
+ var startY = 10
+ var endY = 3 * device.displayHeight / 4
+ var steps = 25
+ if (openingNotificationsFromLockScreen) {
+ val wm = instrumentation.context.getSystemService(WindowManager::class.java)
+ val metricInsets = wm.currentWindowMetrics.windowInsets
+ val insets = metricInsets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.statusBars()
+ or WindowInsets.Type.displayCutout())
+
+ startY = insets.top + 100
+ endY = device.displayHeight / 2
+ steps = 4
+ }
+
+ // Swipe down to show the notification shade
+ val x = device.displayWidth / 2
+ device.swipe(x, startY, x, endY, steps)
+ device.waitForIdle(2000)
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Launch the activity by clicking the notification
+ val notification = device.wait(Until.findObject(
+ By.text("Flicker Test Notification")), 2000L)
+ notification?.click() ?: error("Notification not found")
+ instrumentation.uiAutomation.syncInputTransactions()
+
+ // Wait for the app to launch
+ wmHelper.waitForFullScreenApp(testApp.component)
+ }
+
+ teardown {
+ test {
+ testApp.exit(wmHelper)
+ }
+ }
+ }
+
+ @Test
+ @Postsubmit
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+ @Test
+ @Postsubmit
+ override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
+
+ @Test
+ @Postsubmit
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+ @Test
+ @Postsubmit
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ @Test
+ @Postsubmit
+ override fun appWindowBecomesVisible() = appWindowBecomesVisible_warmStart()
+
+ @Test
+ @Postsubmit
+ override fun appLayerBecomesVisible() = appLayerBecomesVisible_warmStart()
+
+ @Test
+ @Postsubmit
+ fun notificationAppWindowVisibleAtEnd() {
+ testSpec.assertWmEnd {
+ this.isAppWindowVisible(testApp.component)
+ }
+ }
+
+ @Test
+ @Postsubmit
+ fun notificationAppWindowOnTopAtEnd() {
+ testSpec.assertWmEnd {
+ this.isAppWindowOnTop(testApp.component)
+ }
+ }
+
+ @Test
+ @Postsubmit
+ fun notificationAppLayerVisibleAtEnd() {
+ testSpec.assertLayersEnd {
+ this.isVisible(testApp.component)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @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.
+ */
+ @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/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 663af703f76d..2226fd1d2155 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,18 +16,21 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
+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 com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.dsl.FlickerBuilder
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
@@ -58,13 +61,15 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+open class OpenAppFromOverviewTest(testSpec: FlickerTestParameter)
+ : OpenAppFromLauncherTransition(testSpec) {
+
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
@@ -73,25 +78,31 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
device.pressHome()
wmHelper.waitForAppTransitionIdle()
device.pressRecentApps()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(testSpec.config.startRotation)
+ wmHelper.waitFor(
+ WindowManagerConditionsFactory
+ .isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerConditionsFactory.isActivityVisible(LAUNCHER_COMPONENT),
+ WindowManagerConditionsFactory.hasLayersAnimating().negate()
+ )
+ this.setRotation(testSpec.startRotation)
}
}
transitions {
device.reopenAppFromOverview(wmHelper)
wmHelper.waitFor(
- WindowManagerConditionsFactory.hasLayersAnimating().negate(),
- WindowManagerConditionsFactory.isWMStateComplete(),
- WindowManagerConditionsFactory.isHomeActivityVisible().negate()
+ WindowManagerConditionsFactory.hasLayersAnimating().negate(),
+ WindowManagerConditionsFactory.isWMStateComplete(),
+ WindowManagerConditionsFactory.isLayerVisible(LAUNCHER_COMPONENT).negate(),
+ WindowManagerConditionsFactory.isActivityVisible(LAUNCHER_COMPONENT).negate()
)
wmHelper.waitForFullScreenApp(testApp.component)
}
}
/** {@inheritDoc} */
- @FlakyTest
+ @FlakyTest(bugId = 206753786)
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
/** {@inheritDoc} */
@Presubmit
@@ -99,9 +110,9 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest
@Test
- override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+ override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
/** {@inheritDoc} */
@Presubmit
@@ -113,6 +124,51 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible_warmStart()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @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.
@@ -124,7 +180,7 @@ class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransitio
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(repetitions = 3)
}
}
-} \ No newline at end of file
+}
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 08aaea70762f..55eb3c3d1a0a 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
@@ -17,20 +17,19 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Postsubmit
+import androidx.test.filters.FlakyTest
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.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.Group1
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
-import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.navBarLayerPositionEnd
import com.android.server.wm.traces.common.FlickerComponentName
-import com.google.common.truth.Truth
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -40,6 +39,8 @@ import org.junit.runners.Parameterized
/**
* Test launching an app while the device is locked
*
+ * This test assumes the device doesn't have AOD enabled
+ *
* To run this test: `atest FlickerTests:OpenAppNonResizeableTest`
*
* Actions:
@@ -59,87 +60,26 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+open class OpenAppNonResizeableTest(testSpec: FlickerTestParameter)
+ : OpenAppFromLockTransition(testSpec) {
override val testApp = NonResizeableAppHelper(instrumentation)
private val colorFadComponent = FlickerComponentName("", "ColorFade BLAST#")
/**
- * Defines the transition used to run the test
+ * Checks that the nav bar layer starts invisible, becomes visible during unlocking animation
+ * and remains visible at the end
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
- get() = { args ->
- super.transition(this, args)
- setup {
- eachRun {
- device.sleep()
- wmHelper.waitFor("noAppWindowsOnTop") {
- it.wmState.topVisibleAppWindow.isEmpty()
- }
- }
- }
- teardown {
- eachRun {
- testApp.exit(wmHelper)
- }
- }
- transitions {
- testApp.launchViaIntent(wmHelper)
- wmHelper.waitForFullScreenApp(testApp.component)
- }
- }
-
- /**
- * Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
- * and becomes visible at the end
- */
- @Postsubmit
+ @FlakyTest(bugId = 227083463)
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
- this.isVisible(FlickerComponentName.NAV_BAR)
- .then()
- .isInvisible(FlickerComponentName.NAV_BAR)
+ this.isInvisible(FlickerComponentName.NAV_BAR)
.then()
.isVisible(FlickerComponentName.NAV_BAR)
}
}
/**
- * Checks that the app layer doesn't exist at the start of the transition, that it is
- * created (invisible) and becomes visible during the transition
- */
- @FlakyTest
- @Test
- fun appLayerBecomesVisible() {
- testSpec.assertLayers {
- this.notContains(testApp.component)
- .then()
- .isInvisible(testApp.component)
- .then()
- .isVisible(testApp.component)
- }
- }
-
- /**
- * Checks that the app 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.
- */
- @Presubmit
- @Test
- fun appWindowBecomesVisible() {
- testSpec.assertWm {
- this.notContains(testApp.component)
- .then()
- .isAppWindowInvisible(testApp.component, isOptional = true)
- .then()
- .isAppWindowVisible(testApp.component)
- }
- }
-
- /**
* Checks if [testApp] is visible at the end of the transition
*/
@Presubmit
@@ -151,31 +91,23 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
}
/**
- * Checks that the nav bar starts the transition visible, then becomes invisible during
- * then unlocking animation and becomes visible at the end of the transition
+ * 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
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
- this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
- .then()
- .isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
+ this.isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
.then()
.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
}
- /** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
-
/**
* Checks that the status bar layer is visible at the end of the trace
*
- * It is not possible to check at the start because the animation is working differently
- * in devices with and without blur (b/202936526)
+ * It is not possible to check at the start because the screen is off
*/
@Presubmit
@Test
@@ -186,31 +118,31 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
}
/** {@inheritDoc} */
- @FlakyTest(bugId = 202936526)
+ @FlakyTest(bugId = 206753786)
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerPositionAtEnd() {
testSpec.assertLayersEnd {
val display = this.entry.displays.minByOrNull { it.id }
- ?: throw RuntimeException("There is no display!")
+ ?: error("There is no display!")
this.visibleRegion(FlickerComponentName.STATUS_BAR)
.coversExactly(WindowUtils.getStatusBarPosition(display))
}
}
- /** {@inheritDoc} */
- @FlakyTest
- @Test
- override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @FlakyTest
+ /**
+ * 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
@Test
- override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+ override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd()
/** {@inheritDoc} */
@FlakyTest
@@ -219,47 +151,14 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
- @Postsubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
-
- /**
- * Checks that the focus changes from the launcher to [testApp]
- */
@FlakyTest
@Test
- override fun focusChanges() = super.focusChanges()
-
- /**
- * Checks that the screen is locked at the start of the transition ([colorFadComponent])
- * layer is visible
- */
- @Postsubmit
- @Test
- fun screenLockedStart() {
- testSpec.assertLayersStart {
- isVisible(colorFadComponent)
- }
- }
+ override fun entireScreenCovered() = super.entireScreenCovered()
- /**
- * This test checks if the launcher is visible at the start and the app at the end,
- * it cannot use the regular assertion (check over time), because on lock screen neither
- * the app not the launcher are visible, and there is no top visible window.
- */
- @Postsubmit
+ @FlakyTest(bugId = 218470989)
@Test
- override fun appWindowReplacesLauncherAsTopWindow() {
- testSpec.assertWm {
- this.invoke("noAppWindowsOnTop") {
- Truth.assertWithMessage("Should not have any app window on top " +
- "when the screen is locked")
- .that(it.wmState.topVisibleAppWindow)
- .isEmpty()
- }.then()
- .isAppWindowOnTop(testApp.component)
- }
- }
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
/**
@@ -273,11 +172,11 @@ class OpenAppNonResizeableTest(testSpec: FlickerTestParameter) : OpenAppTransiti
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedNavigationModes =
listOf(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/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 7af7b3ab6f24..20e6d0222854 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
@@ -21,19 +21,15 @@ 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 com.android.server.wm.flicker.entireScreenCovered
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.repetitions
-import com.android.server.wm.flicker.replacesLayer
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
@@ -50,18 +46,16 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
/**
* Defines the transition used to run the test
*/
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
test {
- testApp.exit()
+ testApp.exit(wmHelper)
}
}
}
@@ -73,7 +67,7 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
@@ -155,52 +149,87 @@ abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
open fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
- * Checks that the focus changes from the launcher to [testApp]
+ * 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 focusChanges() {
- testSpec.assertEventLog {
- this.focusChanges("NexusLauncherActivity", testApp.`package`)
+ 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)
}
}
- /**
- * Checks that [LAUNCHER_COMPONENT] 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)
+ 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)
+ }
}
/**
- * 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 the app 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.
*/
@Presubmit
@Test
- open fun appWindowReplacesLauncherAsTopWindow() {
+ open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+
+ protected fun appWindowBecomesVisible_coldStart() {
testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+ this.notContains(testApp.component)
.then()
- .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .isAppWindowInvisible(testApp.component, isOptional = true)
.then()
- .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
+ .isAppWindowVisible(testApp.component)
+ }
+ }
+
+ protected fun appWindowBecomesVisible_warmStart() {
+ testSpec.assertWm {
+ this.isAppWindowInvisible(testApp.component)
.then()
- .isAppWindowOnTop(testApp.component)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp.component)
}
}
/**
- * Checks that [LAUNCHER_COMPONENT] window is visible at the start, and
- * becomes invisible during the transition
+ * 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.
*/
- open fun launcherWindowBecomesInvisible() {
+ @Presubmit
+ @Test
+ open fun appWindowBecomesTopWindow() {
testSpec.assertWm {
- this.isAppWindowOnTop(LAUNCHER_COMPONENT)
+ this.isAppWindowNotOnTop(testApp.component)
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowOnTop(FlickerComponentName.SPLASH_SCREEN, isOptional = true)
.then()
- .isAppWindowNotOnTop(LAUNCHER_COMPONENT)
+ .isAppWindowOnTop(testApp.component)
}
}
-} \ No newline at end of file
+}
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 5edee0cf0ca0..97528c0471cc 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
@@ -17,15 +17,14 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
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.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,13 +54,14 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+open class OpenAppWarmTest(testSpec: FlickerTestParameter)
+ : OpenAppFromLauncherTransition(testSpec) {
/**
* Defines the transition used to run the test
*/
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
@@ -69,7 +69,7 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
eachRun {
device.pressHome()
wmHelper.waitForHomeActivityVisible()
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -84,6 +84,23 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
}
/** {@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()
@@ -96,17 +113,26 @@ class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSp
/** {@inheritDoc} */
@Presubmit
@Test
- override fun launcherWindowBecomesInvisible() = super.launcherWindowBecomesInvisible()
+ override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
+ override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
/** {@inheritDoc} */
@Presubmit
@Test
- override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible_warmStart()
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun appWindowBecomesVisible() = super.appWindowBecomesVisible_warmStart()
+
+ @FlakyTest(bugId = 229735718)
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
/**
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 495e2d62a11d..2f546b56f145 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
@@ -33,8 +33,6 @@ 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.repetitions
-import com.android.server.wm.flicker.startRotation
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
@@ -65,14 +63,16 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
class TaskTransitionTest(val testSpec: FlickerTestParameter) {
- val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ 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 {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
eachRun {
mTestApp.launchViaIntent(wmHelper)
@@ -101,7 +101,7 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) {
@Test
fun wallpaperWindowIsNeverVisible() {
testSpec.assertWm {
- this.isNonAppWindowInvisible(WALLPAPER)
+ this.isNonAppWindowInvisible(mWallpaper)
}
}
@@ -113,7 +113,7 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) {
@Test
fun wallpaperLayerIsNeverVisible() {
testSpec.assertLayers {
- this.isInvisible(WALLPAPER)
+ this.isInvisible(mWallpaper)
this.isInvisible(WALLPAPER_BBQ_WRAPPER)
}
}
@@ -149,25 +149,31 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) {
@Test
fun colorLayerIsVisibleDuringTransition() {
val bgColorLayer = FlickerComponentName("", "colorBackgroundLayer")
- val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
testSpec.assertLayers {
- this.coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
+ this.invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning
.isVisible(bgColorLayer)
.then()
// Fully transitioned to simple SIMPLE_ACTIVITY
- .coversExactly(displayBounds, SIMPLE_ACTIVITY)
+ .invoke("SIMPLE_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(SIMPLE_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
.then()
// Transitioning back
.isVisible(bgColorLayer)
.then()
// Fully transitioned back to LAUNCH_NEW_TASK_ACTIVITY
+ .invoke("LAUNCH_NEW_TASK_ACTIVITY coversExactly displayBounds") {
+ it.visibleRegion(LAUNCH_NEW_TASK_ACTIVITY).coversExactly(displayBounds)
+ }
.isInvisible(bgColorLayer)
- .coversExactly(displayBounds, LAUNCH_NEW_TASK_ACTIVITY)
}
}
@@ -227,22 +233,21 @@ class TaskTransitionTest(val testSpec: FlickerTestParameter) {
fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
companion object {
- private val WALLPAPER = getWallpaperPackage(InstrumentationRegistry.getInstrumentation())
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): FlickerComponentName? {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
- return wallpaperManager.wallpaperInfo.component.toFlickerComponent()
+ return wallpaperManager.wallpaperInfo?.component?.toFlickerComponent()
}
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigNonRotationTests(repetitions = 5)
+ .getConfigNonRotationTests(repetitions = 3)
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
new file mode 100644
index 000000000000..897fe5dee7fb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
@@ -0,0 +1,2 @@
+# System UI > ... > Launcher > Gesture nav
+# Bug component: 565144
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 52904cce8772..c89e6a44ab6c 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
@@ -17,11 +17,12 @@
package com.android.server.wm.flicker.quickswitch
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.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
@@ -32,14 +33,15 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
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.isRotated
+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.startRotation
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.Test
import org.junit.runner.RunWith
@@ -62,18 +64,28 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
+open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val taplInstrumentation = LauncherInstrumentation()
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ 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)
@@ -83,20 +95,10 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
}
}
transitions {
- // Swipe right from bottom to quick switch back
- // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
- // as to not accidentally trigger a swipe back or forward action which would result
- // in the same behavior but not testing quick swap.
- device.swipe(
- startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- 2 * startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
- )
-
+ taplInstrumentation.launchedAppState.quickSwitchToPreviousApp()
wmHelper.waitForFullScreenApp(testApp1.component)
wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForNavBarStatusBarVisible()
}
teardown {
@@ -112,7 +114,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that the transition starts with [testApp2]'s windows filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithApp2WindowsCoverFullScreen() {
testSpec.assertWmStart {
@@ -124,7 +126,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that the transition starts with [testApp2]'s layers filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithApp2LayersCoverFullScreen() {
testSpec.assertLayersStart {
@@ -135,7 +137,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
/**
* Checks that the transition starts with [testApp2] being the top window.
*/
- @Postsubmit
+ @Presubmit
@Test
fun startsWithApp2WindowBeingOnTop() {
testSpec.assertWmStart {
@@ -147,7 +149,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* 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].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithApp1WindowsCoveringFullScreen() {
testSpec.assertWmEnd {
@@ -159,7 +161,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that [testApp1] layers 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].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithApp1LayersCoveringFullScreen() {
testSpec.assertLayersEnd {
@@ -171,7 +173,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* 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].
*/
- @Postsubmit
+ @Presubmit
@Test
fun endsWithApp1BeingOnTop() {
testSpec.assertWmEnd {
@@ -183,7 +185,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that [testApp1]'s window starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app1WindowBecomesAndStaysVisible() {
testSpec.assertWm {
@@ -199,7 +201,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app1LayerBecomesAndStaysVisible() {
testSpec.assertLayers {
@@ -213,7 +215,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that [testApp2]'s window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app2WindowBecomesAndStaysInvisible() {
testSpec.assertWm {
@@ -227,7 +229,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app2LayerBecomesAndStaysInvisible() {
testSpec.assertLayers {
@@ -242,7 +244,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
* visible.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app1WindowIsVisibleOnceApp2WindowIsInvisible() {
testSpec.assertWm {
@@ -262,7 +264,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
* Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially
* visible.
*/
- @Postsubmit
+ @Presubmit
@Test
fun app1LayerIsVisibleOnceApp2LayerIsInvisible() {
testSpec.assertLayers {
@@ -279,14 +281,14 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
/**
* Checks that the navbar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
@@ -295,21 +297,21 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
*
* NOTE: This doesn't check that the navbar is visible or not.
*/
- @Postsubmit
+ @Presubmit
@Test
fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
@@ -319,7 +321,7 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
@@ -327,4 +329,4 @@ class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParamet
)
}
}
-} \ No newline at end of file
+}
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
new file mode 100644
index 000000000000..b9fef085da29
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt
@@ -0,0 +1,55 @@
+/*
+ * 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 842aa2b548db..725d2c3d818c 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
@@ -17,11 +17,12 @@
package com.android.server.wm.flicker.quickswitch
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.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
@@ -31,16 +32,16 @@ import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
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.isRotated
+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.repetitions
-import com.android.server.wm.flicker.startRotation
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.Test
import org.junit.runner.RunWith
@@ -50,7 +51,7 @@ import org.junit.runners.Parameterized
/**
* Test quick switching back to previous app from last opened app
*
- * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsBackTest`
+ * To run this test: `atest FlickerTests:QuickSwitchBetweenTwoAppsForwardTest`
*
* Actions:
* Launch an app [testApp1]
@@ -63,20 +64,26 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
+open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val taplInstrumentation = LauncherInstrumentation()
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+ @Before
+ open fun before() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ }
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- withTestName { testSpec.name }
- repeat { testSpec.config.repetitions }
setup {
+ test {
+ taplInstrumentation.setExpectedRotation(testSpec.startRotation)
+ }
+
eachRun {
testApp1.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp1.component)
@@ -84,43 +91,29 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
testApp2.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp2.component)
- // Swipe right from bottom to quick switch back
- // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
- // as to not accidentally trigger a swipe back or forward action which would result
- // in the same behavior but not testing quick swap.
- device.swipe(
- startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- 2 * startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
- )
+ startDisplayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+
+ taplInstrumentation.launchedAppState.quickSwitchToPreviousApp()
wmHelper.waitForFullScreenApp(testApp1.component)
wmHelper.waitForAppTransitionIdle()
}
}
transitions {
- // Swipe left from bottom to quick switch forward
- // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
- // as to not accidentally trigger a swipe back or forward action which would result
- // in the same behavior but not testing quick swap.
- device.swipe(
- 2 * startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- if (testSpec.config.startRotation.isRotated()) 75 else 30
- )
+ taplInstrumentation.launchedAppState.quickSwitchToPreviousAppSwipeLeft()
wmHelper.waitForFullScreenApp(testApp2.component)
wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForNavBarStatusBarVisible()
}
teardown {
test {
- testApp1.exit()
- testApp2.exit()
+ testApp1.exit(wmHelper)
+ testApp2.exit(wmHelper)
}
}
}
@@ -130,11 +123,12 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that the transition starts with [testApp1]'s windows filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun startsWithApp1WindowsCoverFullScreen() {
+ open fun startsWithApp1WindowsCoverFullScreen() {
testSpec.assertWmStart {
- this.frameRegion(testApp1.component).coversExactly(startDisplayBounds)
+ this.frameRegion(testApp1.component, FlickerComponentName.LETTERBOX)
+ .coversExactly(startDisplayBounds)
}
}
@@ -142,9 +136,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that the transition starts with [testApp1]'s layers filling/covering exactly the
* entirety of the display.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun startsWithApp1LayersCoverFullScreen() {
+ open fun startsWithApp1LayersCoverFullScreen() {
testSpec.assertLayersStart {
this.visibleRegion(testApp1.component).coversExactly(startDisplayBounds)
}
@@ -153,21 +147,21 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
/**
* Checks that the transition starts with [testApp1] being the top window.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun startsWithApp1WindowBeingOnTop() {
+ open fun startsWithApp1WindowBeingOnTop() {
testSpec.assertWmStart {
this.isAppWindowOnTop(testApp1.component)
}
}
/**
- * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of the
- * transition once we have fully quick switched from [testApp1] back to the [testApp2].
+ * Checks that [testApp2] windows fill the entire screen (i.e. is "fullscreen") at the end of
+ * the transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
- @Postsubmit
+ @Presubmit
@Test
- fun endsWithApp2WindowsCoveringFullScreen() {
+ open fun endsWithApp2WindowsCoveringFullScreen() {
testSpec.assertWmEnd {
this.frameRegion(testApp2.component).coversExactly(startDisplayBounds)
}
@@ -177,21 +171,22 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that [testApp2] layers fill the entire screen (i.e. is "fullscreen") at the end of the
* transition once we have fully quick switched from [testApp1] back to the [testApp2].
*/
- @Postsubmit
+ @Presubmit
@Test
- fun endsWithApp2LayersCoveringFullScreen() {
+ open fun endsWithApp2LayersCoveringFullScreen() {
testSpec.assertLayersEnd {
- this.visibleRegion(testApp2.component).coversExactly(startDisplayBounds)
+ this.visibleRegion(testApp2.component, FlickerComponentName.LETTERBOX)
+ .coversExactly(startDisplayBounds)
}
}
/**
- * Checks that [testApp2] is the top window at the end of the transition once we have fully quick
- * switched from [testApp1] back to the [testApp2].
+ * Checks that [testApp2] is the top window at the end of the transition once we have fully
+ * quick switched from [testApp1] back to the [testApp2].
*/
- @Postsubmit
+ @Presubmit
@Test
- fun endsWithApp2BeingOnTop() {
+ open fun endsWithApp2BeingOnTop() {
testSpec.assertWmEnd {
this.isAppWindowOnTop(testApp2.component)
}
@@ -201,9 +196,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that [testApp2]'s window starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app2WindowBecomesAndStaysVisible() {
+ open fun app2WindowBecomesAndStaysVisible() {
testSpec.assertWm {
this.isAppWindowInvisible(testApp2.component)
.then()
@@ -217,9 +212,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that [testApp2]'s layer starts off invisible and becomes visible at some point before
* the end of the transition and then stays visible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app2LayerBecomesAndStaysVisible() {
+ open fun app2LayerBecomesAndStaysVisible() {
testSpec.assertLayers {
this.isInvisible(testApp2.component)
.then()
@@ -231,9 +226,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that [testApp1]'s window starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app1WindowBecomesAndStaysInvisible() {
+ open fun app1WindowBecomesAndStaysInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(testApp1.component)
.then()
@@ -245,9 +240,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Checks that [testApp1]'s layer starts off visible and becomes invisible at some point before
* the end of the transition and then stays invisible until the end of the transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app1LayerBecomesAndStaysInvisible() {
+ open fun app1LayerBecomesAndStaysInvisible() {
testSpec.assertLayers {
this.isVisible(testApp1.component)
.then()
@@ -260,9 +255,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
+ open fun app2WindowIsVisibleOnceApp1WindowIsInvisible() {
testSpec.assertWm {
this.isAppWindowVisible(testApp1.component)
.then()
@@ -279,9 +274,9 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
* Ensures that at any point, either [testApp2] or [testApp1]'s windows are at least partially
* visible.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
+ open fun app2LayerIsVisibleOnceApp1LayerIsInvisible() {
testSpec.assertLayers {
this.isVisible(testApp1.component)
.then()
@@ -296,47 +291,59 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
/**
* Checks that the navbar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible()
+ open fun navBarWindowIsAlwaysVisible() {
+ testSpec.navBarWindowIsVisible()
+ }
/**
* Checks that the navbar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun navBarLayerAlwaysIsVisible() = testSpec.navBarLayerIsVisible()
+ open 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.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun navbarIsAlwaysInRightPosition() = testSpec.navBarLayerRotatesAndScales()
+ open fun navbarIsAlwaysInRightPosition() {
+ testSpec.navBarLayerRotatesAndScales()
+ }
/**
* Checks that the status bar window is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsVisible()
+ open fun statusBarWindowIsAlwaysVisible() {
+ testSpec.statusBarWindowIsVisible()
+ }
/**
* Checks that the status bar layer is visible throughout the entire transition.
*/
- @Postsubmit
+ @Presubmit
@Test
- fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsVisible()
+ open fun statusBarLayerIsAlwaysVisible() {
+ testSpec.statusBarLayerIsVisible()
+ }
companion object {
+ private var startDisplayBounds = Rect.EMPTY
+
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
@@ -344,4 +351,4 @@ class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestPara
)
}
}
-} \ No newline at end of file
+}
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
new file mode 100644
index 000000000000..4b8a8c80cd45
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt
@@ -0,0 +1,55 @@
+/*
+ * 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 10ca0d9b323b..cc4a4b2d38aa 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
@@ -22,12 +22,13 @@ 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.Group4
+import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -35,7 +36,6 @@ 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.startRotation
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
@@ -60,16 +60,23 @@ import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@Group4
+@Group1
class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val taplInstrumentation = LauncherInstrumentation()
+
private val testApp = SimpleAppHelper(instrumentation)
- private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
+
+ private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
+ test {
+ taplInstrumentation.setExpectedRotation(testSpec.startRotation)
+ }
+
eachRun {
testApp.launchViaIntent(wmHelper)
device.pressHome()
@@ -78,20 +85,10 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
}
}
transitions {
- // Swipe right from bottom to quick switch back
- // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle
- // as to not accidentally trigger a swipe back or forward action which would result
- // in the same behavior but not testing quick swap.
- device.swipe(
- startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- 2 * startDisplayBounds.bounds.right / 3,
- startDisplayBounds.bounds.bottom,
- 50
- )
-
+ taplInstrumentation.workspace.quickSwitchToPreviousApp()
wmHelper.waitForFullScreenApp(testApp.component)
wmHelper.waitForAppTransitionIdle()
+ wmHelper.waitForNavBarStatusBarVisible()
}
teardown {
@@ -262,7 +259,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
testSpec.assertWm {
this.isAppWindowOnTop(LAUNCHER_COMPONENT)
.then()
- .isAppWindowVisible(FlickerComponentName.SNAPSHOT)
+ .isAppWindowVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isAppWindowVisible(testApp.component)
}
@@ -278,7 +275,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
testSpec.assertLayers {
this.isVisible(LAUNCHER_COMPONENT)
.then()
- .isVisible(FlickerComponentName.SNAPSHOT)
+ .isVisible(FlickerComponentName.SNAPSHOT, isOptional = true)
.then()
.isVisible(testApp.component)
}
@@ -334,7 +331,7 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(
- repetitions = 5,
+ repetitions = 3,
supportedNavigationModes = listOf(
WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY
),
@@ -343,4 +340,4 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) {
)
}
}
-} \ No newline at end of file
+}
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 fd8abc621b33..f0d16f3edb11 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,7 +16,6 @@
package com.android.server.wm.flicker.rotation
-import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -26,13 +25,11 @@ 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.rules.WMFlickerServiceRuleForTestSpec
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.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -81,13 +78,10 @@ import org.junit.runners.Parameterized
class ChangeAppRotationTest(
testSpec: FlickerTestParameter
) : RotationTransition(testSpec) {
- @get:Rule
- val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
-
override val testApp = SimpleAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper)
@@ -95,38 +89,23 @@ class ChangeAppRotationTest(
}
}
- @Postsubmit
- @Test
- fun runPresubmitAssertion() {
- flickerRule.checkPresubmitAssertions()
- }
-
- @Postsubmit
- @Test
- fun runPostsubmitAssertion() {
- flickerRule.checkPostsubmitAssertions()
- }
-
- @FlakyTest
- @Test
- fun runFlakyAssertion() {
- flickerRule.checkFlakyAssertions()
- }
-
- /** {@inheritDoc} */
- @FlakyTest(bugId = 190185577)
+ /**
+ * Windows maybe recreated when rotated. Checks that the focus does not change or if it does,
+ * focus returns to [testApp]
+ */
+ @Presubmit
@Test
- override fun focusDoesNotChange() {
- super.focusDoesNotChange()
+ fun focusChanges() {
+ testSpec.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
*/
- @Presubmit
- @Test
- fun rotationLayerAppearsAndVanishes() {
+ fun rotationLayerAppearsAndVanishesAssertion() {
testSpec.assertLayers {
this.isVisible(testApp.component)
.then()
@@ -138,6 +117,16 @@ class ChangeAppRotationTest(
}
/**
+ * Checks that the [FlickerComponentName.ROTATION] layer appears during the transition,
+ * doesn't flicker, and disappears before the transition is complete
+ */
+ @Presubmit
+ @Test
+ fun rotationLayerAppearsAndVanishes() {
+ rotationLayerAppearsAndVanishesAssertion()
+ }
+
+ /**
* Checks that the status bar window is visible and above the app windows in all WM
* trace entries
*/
@@ -159,7 +148,7 @@ class ChangeAppRotationTest(
/**
* Checks the position of the status bar at the start and end of the transition
*/
- @Presubmit
+ @FlakyTest(bugId = 206753786)
@Test
fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
@@ -187,7 +176,7 @@ class ChangeAppRotationTest(
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
- .getConfigRotationTests(repetitions = 5)
+ .getConfigRotationTests(repetitions = 3)
}
}
-} \ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
new file mode 100644
index 000000000000..f7c0a87f5dac
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
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 e850632ed8af..0becadf630e1 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
@@ -22,14 +22,12 @@ 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.endRotation
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.startRotation
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.Test
@@ -41,10 +39,10 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
- protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+ protected open val transition: FlickerBuilder.() -> Unit = {
setup {
eachRun {
- this.setRotation(testSpec.config.startRotation)
+ this.setRotation(testSpec.startRotation)
}
}
teardown {
@@ -53,7 +51,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
}
}
transitions {
- this.setRotation(testSpec.config.endRotation)
+ this.setRotation(testSpec.endRotation)
}
}
@@ -64,7 +62,7 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
- transition(testSpec.config)
+ transition()
}
}
@@ -131,17 +129,6 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
open fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
- * Checks that the focus doesn't change during animation
- */
- @Presubmit
- @Test
- open fun focusDoesNotChange() {
- testSpec.assertEventLog {
- this.focusDoesNotChange()
- }
- }
-
- /**
* Checks that [testApp] layer covers the entire screen at the start of the transition
*/
@Presubmit
@@ -166,4 +153,4 @@ abstract class RotationTransition(protected val testSpec: FlickerTestParameter)
}
}
}
-} \ No newline at end of file
+}
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 310f04b9710f..fac5baf7a2f9 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
@@ -17,9 +17,9 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
import android.view.WindowManager
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.FlickerTestParameterFactory
@@ -76,19 +76,19 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class SeamlessAppRotationTest(
+open class SeamlessAppRotationTest(
testSpec: FlickerTestParameter
) : RotationTransition(testSpec) {
override val testApp = SeamlessRotationAppHelper(instrumentation)
- override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+ override val transition: FlickerBuilder.() -> Unit
get() = {
- super.transition(this, it)
+ super.transition(this)
setup {
test {
testApp.launchViaIntent(wmHelper,
- stringExtras = mapOf(
- ActivityOptions.EXTRA_STARVE_UI_THREAD to it.starveUiThread.toString())
+ stringExtras = mapOf(ActivityOptions.EXTRA_STARVE_UI_THREAD
+ to testSpec.starveUiThread.toString())
)
}
}
@@ -179,21 +179,34 @@ class SeamlessAppRotationTest(
}
}
+ /**
+ * Checks that the focus doesn't change during animation
+ */
+ @Presubmit
+ @Test
+ fun focusDoesNotChange() {
+ testSpec.assertEventLog {
+ this.focusDoesNotChange()
+ }
+ }
+
/** {@inheritDoc} */
@FlakyTest
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
companion object {
- private val Map<String, Any?>.starveUiThread
- get() = this.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
+ private val FlickerTestParameter.starveUiThread
+ get() = config.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
- private fun FlickerTestParameter.createConfig(
+ private fun createConfig(
+ sourceConfig: FlickerTestParameter,
starveUiThread: Boolean
- ): MutableMap<String, Any?> {
- val config = this.config.toMutableMap()
- config[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread
- return config
+ ): FlickerTestParameter {
+ val newConfig = sourceConfig.config.toMutableMap()
+ .also { it[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread }
+ val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
+ return FlickerTestParameter(newConfig, nameOverride = "$sourceConfig$nameExt")
}
/**
@@ -206,15 +219,10 @@ class SeamlessAppRotationTest(
private fun getConfigurations(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigRotationTests(repetitions = 2)
- .flatMap {
- val defaultRun = it.createConfig(starveUiThread = false)
- val busyUiRun = it.createConfig(starveUiThread = true)
- listOf(
- FlickerTestParameter(defaultRun),
- FlickerTestParameter(busyUiRun,
- name = "${FlickerTestParameter.defaultName(busyUiRun)}_BUSY_UI_THREAD"
- )
- )
+ .flatMap { sourceConfig ->
+ val defaultRun = createConfig(sourceConfig, starveUiThread = false)
+ val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ listOf(defaultRun, busyUiRun)
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index cb37fc7b47e9..43aa4b151548 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:supportsRtl="true">
<activity android:name=".SimpleActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="SimpleApp"
android:exported="true">
<intent-filter>
@@ -32,6 +33,7 @@
</activity>
<activity android:name=".ImeActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
android:label="ImeApp"
android:exported="true">
<intent-filter>
@@ -40,6 +42,7 @@
</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"
@@ -49,8 +52,19 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".ImeStateInitializeActivity"
+ android:theme="@style/no_starting_window"
+ android:windowSoftInputMode="stateAlwaysHidden"
+ android:label="ImeStateInitializeActivity"
+ 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=".SeamlessRotationActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="SeamlessApp"
android:exported="true">
@@ -60,6 +74,7 @@
</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"
@@ -72,6 +87,7 @@
</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">
@@ -82,6 +98,7 @@
</activity>
<activity android:name=".LaunchNewTaskActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewTaskActivity"
+ android:theme="@style/CutoutShortEdges"
android:configChanges="orientation|screenSize"
android:label="LaunchNewTaskActivity"
android:exported="true">
@@ -90,5 +107,60 @@
<category android:name="android.intent.category.LAUNCHER"/>
</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">
+ <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">
+ <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">
+ <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">
+ <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:theme="@style/CutoutShortEdges"
+ 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>
</application>
</manifest>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_notification.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_notification.xml
new file mode 100644
index 000000000000..09bd44c10865
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_notification.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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
+</vector>
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 2620ff407efc..baaf7073b3a6 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -31,4 +31,9 @@
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" />
</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml
new file mode 100644
index 000000000000..78072006f681
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/notification_button.xml
@@ -0,0 +1,27 @@
+<?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:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/holo_orange_light">
+ <Button
+ android:id="@+id/post_notification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Post Notification" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
new file mode 100644
index 000000000000..1d21fd56a487
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -0,0 +1,49 @@
+<?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>
+ <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowBackground">@android:color/darker_gray</item>
+ </style>
+
+ <style name="CutoutDefault" parent="@style/DefaultTheme">
+ <item name="android:windowLayoutInDisplayCutoutMode">default</item>
+ </style>
+
+ <style name="CutoutShortEdges" parent="@style/DefaultTheme">
+ <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
+ </style>
+
+ <style name="CutoutNever" parent="@style/DefaultTheme">
+ <item name="android:windowLayoutInDisplayCutoutMode">never</item>
+ </style>
+
+ <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowSoftInputMode">stateUnchanged</item>
+ </style>
+
+ <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowDisablePreview">true</item>
+ </style>
+</resources>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index baf36ab0e132..6cda482dd30a 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
@@ -37,6 +37,11 @@ public class ActivityOptions {
new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivity");
+ public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
+ public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
+ 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,
@@ -56,4 +61,30 @@ public class ActivityOptions {
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,
+ 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");
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
new file mode 100644
index 000000000000..20eb295d3e6b
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/DialogThemedActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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 static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class DialogThemedActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_simple);
+ getWindow().addFlags(FLAG_NOT_FOCUSABLE);
+ getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
+ TextView textView = new TextView(this);
+ // Print SystemBars' insets visibility on this window for demonstrating during the test.
+ textView.setId(android.R.id.text1);
+ textView.setText("Insets visibility\n\n");
+ textView.setTextColor(Color.BLACK);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setBackgroundColor(Color.GREEN);
+ layout.addView(textView);
+
+ // Create a dialog with dialog-themed activity
+ AlertDialog dialog = new AlertDialog.Builder(this)
+ .setView(layout)
+ .setTitle("Dialog for test")
+ .create();
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(MATCH_PARENT,
+ MATCH_PARENT);
+ attrs.flags = FLAG_DIM_BEHIND | FLAG_ALT_FOCUSABLE_IM;
+ dialog.getWindow().getDecorView().setLayoutParams(attrs);
+ dialog.setCanceledOnTouchOutside(true);
+ dialog.setOnShowListener(d -> textView.setText(textView.getText()
+ + "IME: " + isInsetsVisible(dialog, ime()) + "\n"
+ + "StatusBar: " + isInsetsVisible(dialog, statusBars()) + "\n"
+ + "NavBar: " + isInsetsVisible(dialog, navigationBars()) + "\n")
+ );
+ dialog.show();
+ dialog.setOnDismissListener((d) -> finish());
+ }
+
+ private String isInsetsVisible(Dialog d, int type) {
+ return d.getWindow().getDecorView().getRootWindowInsets().isVisible(type) ? "VISIBLE"
+ : "INVISIBLE";
+ }
+}
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 05da717620aa..bb200f125507 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,6 +16,8 @@
package com.android.server.wm.flicker.testapp;
+import android.content.Intent;
+import android.widget.Button;
import android.widget.EditText;
public class ImeActivityAutoFocus extends ImeActivity {
@@ -26,5 +28,9 @@ public class ImeActivityAutoFocus extends ImeActivity {
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)));
}
}
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
new file mode 100644
index 000000000000..a8613f531e1c
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeEditorPopupDialogActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.AlertDialog;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+public class ImeEditorPopupDialogActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ setContentView(R.layout.activity_simple);
+
+ final EditText editText = new EditText(this);
+ editText.setHint("focused editText");
+ final AlertDialog dialog = new AlertDialog.Builder(this)
+ .setView(editText)
+ .setPositiveButton("Dismiss", (d, which) -> d.dismiss())
+ .create();
+ dialog.show();
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
new file mode 100644
index 000000000000..4be79c4f7bad
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeStateInitializeActivity.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A nop {@link Activity} to make sure that the test starts from a deterministic state.
+ *
+ * <p>Currently this {@link Activity} makes sure the following things</p>
+ * <li>
+ * <ul>Hide the software keyboard with
+ * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}</ul>
+ * <ul>Make sure that the navigation bar (if supported) is rendered with {@link Color#BLACK}.
+ * </ul>
+ * </li>
+ */
+public class ImeStateInitializeActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+ final View view = new View(this);
+ view.setBackgroundColor(Color.WHITE);
+ view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+
+ // Make sure that navigation bar is rendered with black (if supported).
+ getWindow().setNavigationBarColor(Color.BLACK);
+
+ setContentView(view);
+ }
+}
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
new file mode 100644
index 000000000000..b31af385d363
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -0,0 +1,80 @@
+/*
+ * 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.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.TaskStackBuilder;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class NotificationActivity extends Activity {
+ private static final String CHANNEL_ID = "notification_channel";
+ private static final int NOTIFICATION_ID = 1;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.notification_button);
+
+ Button button = findViewById(R.id.post_notification);
+ button.setOnClickListener(v -> postNotification());
+
+ createNotificationChannel();
+ }
+
+ private void postNotification() {
+ 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);
+
+ Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle("Flicker Test Notification")
+ .setContentText("Flicker Test Notification")
+ // Set the intent that will fire when the user taps the notification
+ .setContentIntent(resultPendingIntent)
+ .setAutoCancel(true);
+
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.notify(NOTIFICATION_ID, builder.build());
+ }
+
+ private void createNotificationChannel() {
+ CharSequence name = "channel_name";
+ String description = "channel_description";
+ int importance = NotificationManager.IMPORTANCE_HIGH;
+ NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
+ channel.setDescription(description);
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
new file mode 100644
index 000000000000..b1876b5e5511
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PortraitOnlyActivity.java
@@ -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 com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class PortraitOnlyActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_simple);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java
new file mode 100644
index 000000000000..6f94b74ccf41
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ShowWhenLockedActivity.java
@@ -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 com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ShowWhenLockedActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ WindowManager.LayoutParams p = getWindow().getAttributes();
+ p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+ .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+ getWindow().setAttributes(p);
+ setContentView(R.layout.activity_simple);
+ }
+}
diff --git a/tests/benchmarks/Android.bp b/tests/HandwritingIme/Android.bp
index f87ca2ef928b..1f552bf4dc6d 100644
--- a/tests/benchmarks/Android.bp
+++ b/tests/HandwritingIme/Android.bp
@@ -1,4 +1,4 @@
-// Copyright (C) 2015 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.
@@ -12,9 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// build framework base core benchmarks
-// ============================================================
-
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
@@ -24,15 +21,15 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-java_library {
- name: "networkStatsFactory-benchmarks",
- installable: true,
-
+android_test {
+ name: "HandwritingIme",
srcs: ["src/**/*.java"],
-
- libs: [
- "caliper-api-target",
- "services.core",
+ resource_dirs: ["res"],
+ certificate: "platform",
+ platform_apis: true,
+ static_libs: [
+ "androidx.core_core",
+ "androidx.appcompat_appcompat",
+ "com.google.android.material_material",
],
-
}
diff --git a/tests/HandwritingIme/AndroidManifest.xml b/tests/HandwritingIme/AndroidManifest.xml
new file mode 100644
index 000000000000..1445d95c2879
--- /dev/null
+++ b/tests/HandwritingIme/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (018C) 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.google.android.test.handwritingime">
+
+ <application android:label="Handwriting IME">
+ <service android:name=".HandwritingIme"
+ android:process=":HandwritingIme"
+ android:label="Handwriting IME"
+ android:permission="android.permission.BIND_INPUT_METHOD"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.view.InputMethod"/>
+ </intent-filter>
+ <meta-data android:name="android.view.im"
+ android:resource="@xml/ime"/>
+ </service>
+
+ </application>
+</manifest>
diff --git a/tests/HandwritingIme/res/xml/ime.xml b/tests/HandwritingIme/res/xml/ime.xml
new file mode 100644
index 000000000000..2e84a0389429
--- /dev/null
+++ b/tests/HandwritingIme/res/xml/ime.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.
+ -->
+
+<!-- Configuration info for an input method -->
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsStylusHandwriting="true"/> \ No newline at end of file
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
new file mode 100644
index 000000000000..bf8bd14645a0
--- /dev/null
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -0,0 +1,121 @@
+/*
+ * 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.google.android.test.handwritingime;
+
+import android.annotation.Nullable;
+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.widget.FrameLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.Random;
+
+public class HandwritingIme extends InputMethodService {
+
+ public static final int HEIGHT_DP = 100;
+
+ private Window mInkWindow;
+ private InkView mInk;
+
+ static final String TAG = "HandwritingIme";
+
+ interface HandwritingFinisher {
+ void finish();
+ }
+
+ interface StylusListener {
+ void onStylusEvent(MotionEvent me);
+ }
+
+ final class StylusConsumer implements StylusListener {
+ @Override
+ public void onStylusEvent(MotionEvent me) {
+ HandwritingIme.this.onStylusEvent(me);
+ }
+ }
+
+ final class HandwritingFinisherImpl implements HandwritingFinisher {
+
+ HandwritingFinisherImpl() {}
+
+ @Override
+ public void finish() {
+ finishStylusHandwriting();
+ Log.d(TAG, "HandwritingIme called finishStylusHandwriting() ");
+ }
+ }
+
+ private void onStylusEvent(@Nullable MotionEvent event) {
+ // TODO Hookup recognizer here
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ sendKeyChar((char) (56 + new Random().nextInt(66)));
+ }
+ }
+
+ @Override
+ 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
+
+ return view;
+ }
+
+ public void onPrepareStylusHandwriting() {
+ Log.d(TAG, "onPrepareStylusHandwriting ");
+ if (mInk == null) {
+ mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer());
+ }
+ }
+
+ @Override
+ public boolean onStartStylusHandwriting() {
+ Log.d(TAG, "onStartStylusHandwriting ");
+ Toast.makeText(this, "START HW", Toast.LENGTH_SHORT).show();
+ mInkWindow = getStylusHandwritingWindow();
+ mInkWindow.setContentView(mInk, mInk.getLayoutParams());
+ return true;
+ }
+
+ @Override
+ public void onFinishStylusHandwriting() {
+ Log.d(TAG, "onFinishStylusHandwriting ");
+ Toast.makeText(this, "Finish HW", Toast.LENGTH_SHORT).show();
+ // Free-up
+ ((ViewGroup) mInk.getParent()).removeView(mInk);
+ mInk = null;
+ }
+}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
new file mode 100644
index 000000000000..87a5b900cc18
--- /dev/null
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -0,0 +1,168 @@
+/*
+ * 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.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;
+
+class InkView extends View {
+ private static final long FINISH_TIMEOUT = 2500;
+ private final HandwritingIme.HandwritingFinisher mHwCanceller;
+ private final HandwritingIme.StylusConsumer mConsumer;
+ private final int mTopInset;
+ private Paint mPaint;
+ private Path mPath;
+ private float mX, mY;
+ private static final float STYLUS_MOVE_TOLERANCE = 1;
+ private Runnable mFinishRunnable;
+
+ InkView(Context context, HandwritingIme.HandwritingFinisher hwController,
+ HandwritingIme.StylusConsumer consumer) {
+ super(context);
+ mHwCanceller = hwController;
+ mConsumer = consumer;
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setDither(true);
+ mPaint.setColor(Color.GREEN);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeJoin(Paint.Join.ROUND);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+ mPaint.setStrokeWidth(14);
+
+ mPath = new Path();
+
+ 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;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ canvas.drawPath(mPath, mPaint);
+ canvas.drawARGB(20, 255, 50, 50);
+ }
+
+ 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()) {
+ stylusStart(x, y);
+ }
+ if (dx >= STYLUS_MOVE_TOLERANCE || dy >= STYLUS_MOVE_TOLERANCE) {
+ mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
+ mX = x;
+ mY = y;
+ }
+ }
+
+ private void stylusFinish() {
+ mPath.lineTo(mX, mY);
+ // TODO: support offscreen? e.g. mCanvas.drawPath(mPath, mPaint);
+ mPath.reset();
+ mX = 0;
+ mY = 0;
+
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
+ mConsumer.onStylusEvent(event);
+ android.util.Log.w(HandwritingIme.TAG, "INK touch onStylusEvent " + event);
+ float x = event.getX();
+ float y = event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ cancelTimer();
+ stylusStart(x, y);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ stylusMove(x, y);
+ invalidate();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ scheduleTimer();
+ break;
+
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void cancelTimer() {
+ if (mFinishRunnable != null) {
+ if (getHandler() != null) {
+ getHandler().removeCallbacks(mFinishRunnable);
+ }
+ mFinishRunnable = null;
+ }
+ if (getHandler() != null) {
+ getHandler().removeCallbacksAndMessages(null);
+ }
+ }
+
+ private void scheduleTimer() {
+ cancelTimer();
+ if (getHandler() != null) {
+ postDelayed(getFinishRunnable(), FINISH_TIMEOUT);
+ }
+ }
+
+ private Runnable getFinishRunnable() {
+ mFinishRunnable = () -> {
+ android.util.Log.e(HandwritingIme.TAG, "Hw view timer finishHandwriting ");
+ mHwCanceller.finish();
+ stylusFinish();
+ mPath.reset();
+ invalidate();
+ };
+
+ return mFinishRunnable;
+ }
+
+}
diff --git a/tests/HwAccelerationTest/.project b/tests/HwAccelerationTest/.project
deleted file mode 100644
index 7c04d3cb6426..000000000000
--- a/tests/HwAccelerationTest/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>HwAccelerationTest</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/tests/HwAccelerationTest/Android.bp b/tests/HwAccelerationTest/Android.bp
index 76063227eac1..51848f2857c9 100644
--- a/tests/HwAccelerationTest/Android.bp
+++ b/tests/HwAccelerationTest/Android.bp
@@ -25,7 +25,17 @@ package {
android_test {
name: "HwAccelerationTest",
- srcs: ["**/*.java"],
+ jni_libs: [
+ "libhwaccelerationtest_jni",
+ ],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
+ static_libs: [
+ "androidx.cardview_cardview",
+ ],
+
platform_apis: true,
certificate: "platform",
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 04a55d6038b0..b0ccbd1cf22f 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -436,6 +436,15 @@
</intent-filter>
</activity>
+ <activity android:name=".PenStylusActivity"
+ android:label="Pen/Draw"
+ 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="GLTextureViewActivity"
android:label="TextureView/OpenGL"
android:exported="true">
@@ -780,6 +789,15 @@
</intent-filter>
</activity>
+ <activity android:name="RenderEffectViewActivity"
+ android:label="RenderEffect/View"
+ 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="StretchShaderActivity"
android:label="RenderEffect/Stretch"
android:exported="true">
diff --git a/tests/HwAccelerationTest/jni/Android.bp b/tests/HwAccelerationTest/jni/Android.bp
new file mode 100644
index 000000000000..8edddab0ad1f
--- /dev/null
+++ b/tests/HwAccelerationTest/jni/Android.bp
@@ -0,0 +1,44 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test_library {
+
+ name: "libhwaccelerationtest_jni",
+
+ cflags: [
+ "-Werror",
+ "-Wno-error=deprecated-declarations",
+ ],
+
+ gtest: false,
+
+ srcs: [
+ "native-lib.cpp",
+ ],
+
+ shared_libs: [
+ "libnativehelper",
+ "libandroid",
+ "liblog",
+ ],
+
+ stl: "c++_static",
+
+ sdk_version: "current",
+
+}
diff --git a/tests/HwAccelerationTest/jni/native-lib.cpp b/tests/HwAccelerationTest/jni/native-lib.cpp
new file mode 100644
index 000000000000..407d4bf76336
--- /dev/null
+++ b/tests/HwAccelerationTest/jni/native-lib.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include <android/hardware_buffer.h>
+#include <android/hardware_buffer_jni.h>
+#include <android/native_window.h>
+#include <android/native_window_jni.h>
+#include <android/surface_control.h>
+#include <jni.h>
+
+struct MyWrapper {
+ MyWrapper(ANativeWindow* parent) {
+ surfaceControl = ASurfaceControl_createFromWindow(parent, "PenLayer");
+ }
+
+ ~MyWrapper() { ASurfaceControl_release(surfaceControl); }
+
+ void setBuffer(AHardwareBuffer* buffer) {
+ ASurfaceTransaction* transaction = ASurfaceTransaction_create();
+ ASurfaceTransaction_setBuffer(transaction, surfaceControl, buffer);
+ ASurfaceTransaction_setVisibility(transaction, surfaceControl,
+ ASURFACE_TRANSACTION_VISIBILITY_SHOW);
+ ASurfaceTransaction_apply(transaction);
+ ASurfaceTransaction_delete(transaction);
+ }
+
+ ASurfaceControl* surfaceControl = nullptr;
+};
+
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_android_test_hwui_FrontBufferedLayer_nCreate(JNIEnv* env, jclass, jobject jSurface) {
+ ANativeWindow* window = ANativeWindow_fromSurface(env, jSurface);
+ MyWrapper* wrapper = new MyWrapper(window);
+ ANativeWindow_release(window);
+ return reinterpret_cast<jlong>(wrapper);
+}
+
+extern "C" JNIEXPORT void JNICALL
+Java_com_android_test_hwui_FrontBufferedLayer_nDestroy(JNIEnv*, jclass, jlong ptr) {
+ MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
+ delete wrapper;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_com_android_test_hwui_FrontBufferedLayer_nUpdateBuffer(
+ JNIEnv* env, jclass, jlong ptr, jobject jbuffer) {
+ MyWrapper* wrapper = reinterpret_cast<MyWrapper*>(ptr);
+ AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, jbuffer);
+ wrapper->setBuffer(buffer);
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
new file mode 100644
index 000000000000..cc8adf15f4f0
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/scratches.png
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
new file mode 100644
index 000000000000..b5aff104207a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable-nodpi/weather_2.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/layout/pen_stylus.xml b/tests/HwAccelerationTest/res/layout/pen_stylus.xml
new file mode 100644
index 000000000000..37aafed208fb
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/pen_stylus.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<com.android.test.hwui.FrontBufferedLayer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+/> \ No newline at end of file
diff --git a/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
new file mode 100644
index 000000000000..b91377d1ab49
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/view_runtime_shader.xml
@@ -0,0 +1,205 @@
+<?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.
+ -->
+
+<ScrollView
+ 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">
+ <LinearLayout
+ android:id="@+id/TopLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #1"/>
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="Image Transition"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Touch the image to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <com.android.test.hwui.BitmapTransitionView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:padding="8dp"/>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:text="Sample Card #2"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/CardView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:layout_marginBottom="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:minHeight="148dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp">
+
+ <ImageView
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:contentDescription="Logo"
+ android:src="@drawable/icon"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:text="View Group Manipulation"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:text="Tap the card to trigger the animation"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="194dp"
+ android:background="@android:color/transparent"
+ android:src="@drawable/weather_2"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:paddingBottom="8dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <RatingBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="Card Rating"
+ android:isIndicator="true"
+ android:numStars="5"
+ android:rating="4.5"
+
+ android:stepSize="0.5"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="Category 4.5 Storm"/>
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxLines="3"
+ android:textIsSelectable="true"
+ android:text="Lorem ipsum dolor sit amet, nec no nominavi scaevola. Per et
+ sint sapientem, nobis perpetua salutandi mei te. Quo tamquam probatus
+ reprehendunt in. Eos esse purto eruditi ea. Enim tation persius ut sea,
+ eos ad consul populo. Ne eum solet altera. Cibo eligendi et est, electram
+ theophrastus te vel eu."/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </androidx.cardview.widget.CardView>
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/HwAccelerationTest/res/values/styles.xml b/tests/HwAccelerationTest/res/values/styles.xml
index fa5437f38ace..55f4dd697907 100644
--- a/tests/HwAccelerationTest/res/values/styles.xml
+++ b/tests/HwAccelerationTest/res/values/styles.xml
@@ -41,4 +41,5 @@
<item name="android:spotShadowAlpha">1</item>
-->
</style>
+
</resources>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
new file mode 100644
index 000000000000..3af54503d469
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.test.hwui
+
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.BitmapShader
+import android.graphics.Canvas
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.RuntimeShader
+import android.graphics.Shader
+import android.util.AttributeSet
+import android.view.View
+
+class BitmapTransitionView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+
+ private val mPaint = Paint()
+ private val mImageA = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.large_photo))
+ private val mImageB = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.resources, R.drawable.very_large_photo))
+ private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+ private val mShader = RuntimeShader(AGSL)
+ private var mCurrentProgress = -1f
+ private var mForwardProgress = true
+ private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f)
+
+ init {
+ isClickable = true
+
+ mCurrentAnimator.duration = 1500
+ mCurrentAnimator.addUpdateListener { animation ->
+ mCurrentProgress = animation.animatedValue as Float
+ postInvalidate()
+ }
+ }
+
+ override fun performClick(): Boolean {
+ if (super.performClick()) return true
+
+ if (mCurrentAnimator.isRunning) {
+ mCurrentAnimator.reverse()
+ return true
+ }
+
+ if (mForwardProgress) {
+ mCurrentAnimator.setFloatValues(-1f, 1f)
+ mForwardProgress = false
+ } else {
+ mCurrentAnimator.setFloatValues(1f, -1f)
+ mForwardProgress = true
+ }
+
+ mCurrentAnimator.start()
+ postInvalidate()
+ return true
+ }
+
+ override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
+ val matrixA = Matrix()
+ val matrixB = Matrix()
+
+ matrixA.postScale(width.toFloat() / mImageA.width, height.toFloat() / mImageA.height)
+ matrixB.postScale(width.toFloat() / mImageB.width, height.toFloat() / mImageB.height)
+
+ mShaderA.setLocalMatrix(matrixA)
+ mShaderB.setLocalMatrix(matrixB)
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ mShader.setInputShader("imageA", mShaderA)
+ mShader.setInputShader("imageB", mShaderB)
+ mShader.setIntUniform("imageDimensions", width, height)
+ mShader.setFloatUniform("progress", mCurrentProgress)
+
+ mPaint.shader = mShader
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mPaint)
+ }
+
+ private companion object {
+ const val AGSL = """
+ uniform shader imageA;
+ uniform shader imageB;
+ uniform ivec2 imageDimensions;
+ uniform float progress;
+
+ const vec2 iSize = vec2(48.0, 48.0);
+ const float iDir = 0.5;
+ const float iRand = 0.81;
+
+ float hash12(vec2 p) {
+ vec3 p3 = fract(vec3(p.xyx) * .1031);
+ p3 += dot(p3, p3.yzx + 33.33);
+ return fract((p3.x + p3.y) * p3.z);
+ }
+
+ float ramp(float2 p) {
+ return mix(hash12(p),
+ dot(p/vec2(imageDimensions), float2(iDir, 1 - iDir)),
+ iRand);
+ }
+
+ half4 main(float2 p) {
+ float2 lowRes = p / iSize;
+ float2 cellCenter = (floor(lowRes) + 0.5) * iSize;
+ float2 posInCell = fract(lowRes) * 2 - 1;
+
+ float v = ramp(cellCenter) + progress;
+ float distToCenter = max(abs(posInCell.x), abs(posInCell.y));
+
+ return distToCenter > v ? imageA.eval(p).rgb1 : imageB.eval(p).rgb1;
+ }
+ """
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index c06f8fd44c03..fafe60b46676 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -63,7 +63,7 @@ public class ColorFiltersMutateActivity extends Activity {
"uniform shader bitmapShader;\n"
+ "uniform float param1;\n"
+ "half4 main(float2 xy) {\n"
- + " return half4(sample(bitmapShader, xy).rgb, param1);\n"
+ + " return half4(bitmapShader.eval(xy).rgb, param1);\n"
+ "}\n";
BitmapsView(Context c) {
@@ -83,8 +83,8 @@ public class ColorFiltersMutateActivity extends Activity {
mBlendPaint = new Paint();
mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
- mRuntimeShader = new RuntimeShader(sSkSL, false);
- mRuntimeShader.setUniform("param1", mShaderParam1);
+ mRuntimeShader = new RuntimeShader(sSkSL);
+ mRuntimeShader.setFloatUniform("param1", mShaderParam1);
mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1,
Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP));
@@ -177,7 +177,7 @@ public class ColorFiltersMutateActivity extends Activity {
public void setShaderParam1(float value) {
mShaderParam1 = value;
- mRuntimeShader.setUniform("param1", mShaderParam1);
+ mRuntimeShader.setFloatUniform("param1", mShaderParam1);
invalidate();
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
index 7ea2a62d7494..d4bc2a6d3317 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
@@ -42,7 +42,7 @@ public class ColoredRectsActivity extends Activity {
swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
frame.addView(swView);
final RectsView hwBothView = new RectsView(this, 850, Color.GREEN);
- // Don't actually need to render to a hw layer, but it's a good sanity-check that
+ // Don't actually need to render to a hw layer, but it's a good check that
// we're rendering to/from layers correctly
hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
frame.addView(hwBothView);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
new file mode 100644
index 000000000000..ebec22e29d69
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/FrontBufferedLayer.kt
@@ -0,0 +1,132 @@
+/*
+ * 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.test.hwui
+
+import android.content.Context
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.util.AttributeSet
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.Surface
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+
+class FrontBufferedLayer : SurfaceView, SurfaceHolder.Callback {
+ var mRenderer: PenStylusActivity.SingleBufferedCanvas? = null
+
+ constructor(context: Context) : super(context)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+
+ init {
+ holder.addCallback(this)
+ setZOrderOnTop(true)
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ nDestroy(mNativePtr)
+ mNativePtr = nCreate(holder.surface)
+ mRenderer = PenStylusActivity.SingleBufferedCanvas(width, height)
+ clearOverlay()
+
+ if ((false)) {
+ val canvas = holder.lockCanvas()
+ canvas.drawColor(Color.LTGRAY)
+ holder.unlockCanvasAndPost(canvas)
+ }
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ mRenderer = null
+ nDestroy(mNativePtr)
+ mNativePtr = 0
+ }
+
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ requestUnbufferedDispatch(InputDevice.SOURCE_CLASS_POINTER)
+ this.windowInsetsController?.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_DEFAULT)
+ this.windowInsetsController?.hide(WindowInsets.Type.navigationBars())
+ this.windowInsetsController?.hide(WindowInsets.Type.statusBars())
+ }
+
+ private fun clearOverlay() {
+ mRenderer?.let {
+ it.update(null) {
+ drawColor(Color.WHITE, BlendMode.SRC)
+ }
+ nUpdateBuffer(mNativePtr, it.mHardwareBuffer)
+ }
+ }
+
+ private var prevX: Float = 0f
+ private var prevY: Float = 0f
+ private val paint = Paint().also {
+ it.color = Color.BLACK
+ it.strokeWidth = 10f
+ it.isAntiAlias = true
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (!event.isFromSource(InputDevice.SOURCE_STYLUS)) {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ clearOverlay()
+ }
+ return true
+ }
+ val action = event.actionMasked
+ if (action == MotionEvent.ACTION_DOWN ||
+ action == MotionEvent.ACTION_MOVE) {
+ mRenderer?.let {
+ val left = minOf(prevX, event.x).toInt() - 10
+ val top = minOf(prevY, event.y).toInt() - 10
+ val right = maxOf(prevX, event.x).toInt() + 10
+ val bottom = maxOf(prevY, event.y).toInt() + 10
+ it.update(Rect(left, top, right, bottom)) {
+ if (action == MotionEvent.ACTION_MOVE) {
+ drawLine(prevX, prevY, event.x, event.y, paint)
+ }
+ drawCircle(event.x, event.y, 5f, paint)
+ }
+ nUpdateBuffer(mNativePtr, it.mHardwareBuffer)
+ }
+ prevX = event.x
+ prevY = event.y
+ }
+ return true
+ }
+
+ private var mNativePtr: Long = 0
+
+ private external fun nCreate(surface: Surface): Long
+ private external fun nDestroy(ptr: Long)
+ private external fun nUpdateBuffer(ptr: Long, buffer: HardwareBuffer)
+
+ companion object {
+ init {
+ System.loadLibrary("hwaccelerationtest_jni")
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
index 7173a85f73e7..584ab596836c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
@@ -42,7 +42,7 @@ public class Lines2Activity extends Activity {
swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
frame.addView(swView);
final LinesView hwBothView = new LinesView(this, 850, Color.GREEN);
- // Don't actually need to render to a hw layer, but it's a good sanity-check that
+ // Don't actually need to render to a hw layer, but it's a good check that
// we're rendering to/from layers correctly
hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
frame.addView(hwBothView);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
new file mode 100644
index 000000000000..1445b1db801e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PenStylusActivity.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.test.hwui
+
+import android.app.Activity
+import android.os.Bundle
+import android.hardware.HardwareBuffer
+import android.graphics.Canvas
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.media.ImageReader
+import android.view.Surface
+import java.lang.IllegalArgumentException
+
+const val USAGE_HWC = 0x800L
+
+class PenStylusActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(FrontBufferedLayer(this))
+ }
+
+ class SingleBufferedCanvas : AutoCloseable {
+ val mHardwareBuffer: HardwareBuffer
+ private var mCanvas: Canvas?
+ private val mImageReader: ImageReader
+ private val mSurface: Surface
+
+ constructor(width: Int, height: Int) {
+ mImageReader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888, 1,
+ HardwareBuffer.USAGE_CPU_READ_RARELY or HardwareBuffer.USAGE_CPU_WRITE_RARELY or
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or USAGE_HWC)
+
+ mSurface = mImageReader.surface
+ mSurface.unlockCanvasAndPost(mSurface.lockCanvas(null))
+ val image = mImageReader.acquireNextImage()
+ mHardwareBuffer = image.hardwareBuffer!!
+ image.close()
+ mCanvas = mSurface.lockCanvas(null)
+ }
+
+ fun lockCanvas(rect: Rect?): Canvas {
+ if (mCanvas != null) {
+ unlockCanvas(mCanvas!!)
+ }
+ mCanvas = mSurface.lockCanvas(rect)
+ return mCanvas!!
+ }
+
+ fun unlockCanvas(canvas: Canvas) {
+ if (this.mCanvas !== canvas) throw IllegalArgumentException()
+ mSurface.unlockCanvasAndPost(canvas)
+ this.mCanvas = null
+ mImageReader.acquireNextImage().close()
+ }
+
+ inline fun update(area: Rect?, func: Canvas.() -> Unit) {
+ val canvas = lockCanvas(area)
+ func(canvas)
+ unlockCanvas(canvas)
+ }
+
+ override fun close() {
+ mHardwareBuffer.close()
+ mSurface.unlockCanvasAndPost(mCanvas)
+ mImageReader.close()
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
new file mode 100644
index 000000000000..3c71b96c6c31
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.test.hwui
+
+import android.animation.ValueAnimator
+import android.app.Activity
+import android.graphics.Bitmap
+import android.graphics.BitmapShader
+import android.graphics.ImageDecoder
+import android.graphics.Matrix
+import android.graphics.Shader
+import android.graphics.RenderEffect
+import android.graphics.RuntimeShader
+import android.os.Bundle
+import android.view.View
+
+class RenderEffectViewActivity : Activity() {
+
+ private val mDropsShader = RuntimeShader(dropsAGSL)
+ private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f)
+ private var mStartTime = System.currentTimeMillis()
+ private lateinit var mScratchesImage: Bitmap
+ private lateinit var mScratchesShader: BitmapShader
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.view_runtime_shader)
+
+ val dropsView = findViewById<View>(R.id.CardView)!!
+ dropsView.isClickable = true
+ dropsView.setOnClickListener {
+ if (mDropsAnimator.isRunning) {
+ mDropsAnimator.cancel()
+ dropsView.setRenderEffect(null)
+ } else {
+ mDropsAnimator.start()
+ }
+ }
+
+ val imgSource = ImageDecoder.createSource(resources, R.drawable.scratches)
+ mScratchesImage = ImageDecoder.decodeBitmap(imgSource)
+ mScratchesShader = BitmapShader(mScratchesImage,
+ Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
+
+ mDropsAnimator.duration = 1000
+ mDropsAnimator.repeatCount = ValueAnimator.INFINITE
+ mDropsAnimator.addUpdateListener { _ ->
+ val viewWidth = dropsView.width.toFloat()
+ val viewHeight = dropsView.height.toFloat()
+ val scratchesMatrix = Matrix()
+ scratchesMatrix.postScale(viewWidth / mScratchesImage.width,
+ viewHeight / mScratchesImage.height)
+ mScratchesShader.setLocalMatrix(scratchesMatrix)
+
+ mDropsShader.setInputShader("scratches", mScratchesShader)
+ mDropsShader.setFloatUniform("elapsedSeconds",
+ (System.currentTimeMillis() - mStartTime) / 1000f)
+ mDropsShader.setFloatUniform("viewDimensions", viewWidth, viewHeight)
+
+ val dropsEffect = RenderEffect.createRuntimeShaderEffect(mDropsShader, "background")
+ val blurEffect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP)
+
+ dropsView.setRenderEffect(RenderEffect.createChainEffect(dropsEffect, blurEffect))
+ }
+ }
+
+ private companion object {
+ const val dropsAGSL = """
+ uniform float elapsedSeconds;
+ uniform vec2 viewDimensions;
+ uniform shader background;
+ uniform shader scratches;
+
+ vec2 dropsUV(vec2 fragCoord ) {
+ vec2 uv = fragCoord.xy / viewDimensions.xy; // 0 <> 1
+ vec2 offs = vec2(0.);
+ return (offs + uv).xy;
+ }
+
+ const vec3 iFrostColorRGB = vec3(0.5, 0.5, 0.5);
+ const float iFrostColorAlpha = .3;
+
+ half4 main(float2 fragCoord) {
+ half4 bg = background.eval(dropsUV(fragCoord)*viewDimensions.xy);
+ float2 scratchCoord = fragCoord.xy / viewDimensions.xy;;
+ scratchCoord += 1.5;
+ scratchCoord = mod(scratchCoord, 1);
+ half scratch = scratches.eval(scratchCoord*viewDimensions.xy).r;
+ bg.rgb = mix(bg.rgb, iFrostColorRGB, iFrostColorAlpha);
+ bg.rgb = mix(bg.rgb, half3(1), pow(scratch,3));
+ return bg;
+ }
+ """
+ }
+} \ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
index d925541c76d6..b78907c46744 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -91,7 +91,7 @@ public class RippleActivity extends Activity {
+ " d = rand(float2(x, y)) > density ? d : d * .2;\n"
+ " d = d * rand(float2(fraction, x * y));\n"
+ " float alpha = 1. - pow(fraction, 3.);\n"
- + " return float4(sample(in_paintColor, p).rgb, d * alpha);\n"
+ + " return float4(in_paintColor.eval(p).rgb, d * alpha);\n"
+ "}";
RippleView(Context c) {
@@ -109,8 +109,8 @@ public class RippleActivity extends Activity {
p.setColor(mColor);
mPaint = CanvasProperty.createPaint(p);
- mRuntimeShader = new RuntimeShader(sSkSL, false);
- mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS);
+ mRuntimeShader = new RuntimeShader(sSkSL);
+ mRuntimeShader.setFloatUniform("in_maxRadius", MAX_RADIUS);
}
@Override
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
index 3307c36d9d1a..2990c9e59fec 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -69,7 +69,7 @@ public class StretchShaderActivity extends Activity {
linearLayout.setOrientation(LinearLayout.VERTICAL);
mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap();
- mRuntimeShader = new RuntimeShader(SKSL, false);
+ mRuntimeShader = new RuntimeShader(SKSL);
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
@@ -357,18 +357,18 @@ public class StretchShaderActivity extends Activity {
float uScrollX = mScrollX;
float uScrollY = mScrollY;
- mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity);
- mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance);
- mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX);
- mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY);
- mRuntimeShader.setUniform("uDistDiffX", diffX);
- mRuntimeShader.setUniform("uDistDiffY", diffY);
- mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX);
- mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY);
- mRuntimeShader.setUniform("uScrollX", uScrollX);
- mRuntimeShader.setUniform("uScrollY", uScrollY);
- mRuntimeShader.setUniform("viewportWidth", width);
- mRuntimeShader.setUniform("viewportHeight", height);
+ mRuntimeShader.setFloatUniform("uMaxStretchIntensity", mMaxStretchIntensity);
+ mRuntimeShader.setFloatUniform("uStretchAffectedDist", mStretchAffectedDistance);
+ mRuntimeShader.setFloatUniform("uDistanceStretchedX", distanceStretchedX);
+ mRuntimeShader.setFloatUniform("uDistanceStretchedY", distanceStretchedY);
+ mRuntimeShader.setFloatUniform("uDistDiffX", diffX);
+ mRuntimeShader.setFloatUniform("uDistDiffY", diffY);
+ mRuntimeShader.setFloatUniform("uOverscrollX", normOverScrollDistX);
+ mRuntimeShader.setFloatUniform("uOverscrollY", normOverScrollDistY);
+ mRuntimeShader.setFloatUniform("uScrollX", uScrollX);
+ mRuntimeShader.setFloatUniform("uScrollY", uScrollY);
+ mRuntimeShader.setFloatUniform("viewportWidth", width);
+ mRuntimeShader.setFloatUniform("viewportHeight", height);
mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
@@ -532,6 +532,6 @@ public class StretchShaderActivity extends Activity {
+ " uv.y = outV;\n"
+ " coord.x = uv.x * viewportWidth;\n"
+ " coord.y = uv.y * viewportHeight;\n"
- + " return sample(uContentTexture, coord);\n"
+ + " return uContentTexture.eval(coord);\n"
+ "}";
}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index eacf5b287a2e..de9bbb6ef9fa 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -18,6 +18,8 @@ android_test {
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "services.core.unboosted",
+ "testables",
"truth-prebuilt",
"ub-uiautomator",
],
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 4da3eca25ea0..1d65cc35c3bc 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -19,7 +19,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.MediumTest
+import android.app.ActivityManager
+import android.app.ApplicationExitInfo
import android.graphics.Rect
+import android.os.Build
+import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
@@ -27,10 +31,13 @@ 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 org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
@@ -51,28 +58,99 @@ import org.junit.runner.RunWith
class AnrTest {
companion object {
private const val TAG = "AnrTest"
+ private const val ALL_PIDS = 0
+ private const val NO_MAX = 0
}
- val mInstrumentation = InstrumentationRegistry.getInstrumentation()
- var mHideErrorDialogs = 0
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private var hideErrorDialogs = 0
+ private lateinit var PACKAGE_NAME: String
+ private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ Build.HW_TIMEOUT_MULTIPLIER)
@Before
fun setUp() {
- val contentResolver = mInstrumentation.targetContext.contentResolver
- mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+ 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()
}
@After
fun tearDown() {
- val contentResolver = mInstrumentation.targetContext.contentResolver
- Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
+ val contentResolver = instrumentation.targetContext.contentResolver
+ Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, hideErrorDialogs)
}
@Test
- fun testGestureMonitorAnr() {
+ fun testGestureMonitorAnr_Close() {
+ triggerAnr()
+ clickCloseAppOnAnrDialog()
+ }
+
+ @Test
+ fun testGestureMonitorAnr_Wait() {
+ triggerAnr()
+ clickWaitOnAnrDialog()
+ SystemClock.sleep(500) // Wait at least 500ms after tapping on wait
+ // ANR dialog should reappear after a delay - find the close button on it to verify
+ clickCloseAppOnAnrDialog()
+ }
+
+ private fun clickCloseAppOnAnrDialog() {
+ // Find anr dialog and kill app
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ val closeAppButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
+ if (closeAppButton == null) {
+ fail("Could not find anr dialog")
+ return
+ }
+ val initialReasons = getExitReasons()
+ closeAppButton.click()
+ /**
+ * We must wait for the app to be fully closed before exiting this test. This is because
+ * another test may again invoke 'am start' for the same activity.
+ * If the 1st process that got ANRd isn't killed by the time second 'am start' runs,
+ * the killing logic will apply to the newly launched 'am start' instance, and the second
+ * test will fail because the unresponsive activity will never be launched.
+ */
+ waitForNewExitReason(initialReasons[0].timestamp)
+ }
+
+ private fun clickWaitOnAnrDialog() {
+ // Find anr dialog and tap on wait
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
+ val waitButton: UiObject2? =
+ uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
+ if (waitButton == null) {
+ fail("Could not find anr dialog/wait button")
+ return
+ }
+ waitButton.click()
+ }
+
+ private fun getExitReasons(): List<ApplicationExitInfo> {
+ lateinit var infos: List<ApplicationExitInfo>
+ instrumentation.runOnMainSync {
+ val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)
+ infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, ALL_PIDS, NO_MAX)
+ }
+ return infos
+ }
+
+ private fun waitForNewExitReason(previousExitTimestamp: Long) {
+ PollingCheck.waitFor {
+ getExitReasons()[0].timestamp > previousExitTimestamp
+ }
+ val reasons = getExitReasons()
+ assertTrue(reasons[0].timestamp > previousExitTimestamp)
+ assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
+ }
+
+ private fun triggerAnr() {
startUnresponsiveActivity()
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val obj: UiObject2? = uiDevice.wait(Until.findObject(
By.text("Unresponsive gesture monitor")), 10000)
@@ -87,29 +165,14 @@ class AnrTest {
MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
- mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
-
- // Todo: replace using timeout from android.hardware.input.IInputManager
- SystemClock.sleep(5000) // default ANR timeout for gesture monitors
-
- clickCloseAppOnAnrDialog()
- }
+ instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
- private fun clickCloseAppOnAnrDialog() {
- // Find anr dialog and kill app
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
- val closeAppButton: UiObject2? =
- uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
- if (closeAppButton == null) {
- fail("Could not find anr dialog")
- return
- }
- closeAppButton.click()
+ SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
}
private fun startUnresponsiveActivity() {
val flags = " -W -n "
- val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
- mInstrumentation.uiAutomation.executeShellCommand(startCmd)
+ val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
+ instrumentation.uiAutomation.executeShellCommand(startCmd)
}
-} \ No newline at end of file
+}
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index 014efc2b954c..37b67f4c183c 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -17,16 +17,11 @@
package com.android.test.input
import android.os.HandlerThread
-import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.Looper
import android.view.InputChannel
import android.view.InputEvent
import android.view.InputEventReceiver
-import android.view.InputEventSender
import android.view.KeyEvent
-import android.view.MotionEvent
-import java.util.concurrent.LinkedBlockingQueue
-import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -46,54 +41,19 @@ private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
assertEquals(expected.displayId, received.displayId)
}
-private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T {
- try {
- return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
- } catch (e: InterruptedException) {
- throw RuntimeException("Unexpectedly interrupted while waiting for event")
- }
+private fun getTestKeyEvent(): KeyEvent {
+ return KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0 /*repeat*/)
}
-class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+private class CrashingInputEventReceiver(channel: InputChannel, looper: Looper) :
InputEventReceiver(channel, looper) {
- private val mInputEvents = LinkedBlockingQueue<InputEvent>()
-
override fun onInputEvent(event: InputEvent) {
- when (event) {
- is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
- is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
- else -> throw Exception("Received $event is neither a key nor a motion")
+ try {
+ throw IllegalArgumentException("This receiver crashes when it receives input event")
+ } finally {
+ finishInputEvent(event, true /*handled*/)
}
- finishInputEvent(event, true /*handled*/)
- }
-
- fun getInputEvent(): InputEvent {
- return getEvent(mInputEvents)
- }
-}
-
-class TestInputEventSender(channel: InputChannel, looper: Looper) :
- InputEventSender(channel, looper) {
- data class FinishedSignal(val seq: Int, val handled: Boolean)
- data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
-
- private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
- private val mTimelines = LinkedBlockingQueue<Timeline>()
-
- override fun onInputEventFinished(seq: Int, handled: Boolean) {
- mFinishedSignals.put(FinishedSignal(seq, handled))
- }
-
- override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
- mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
- }
-
- fun getFinishedSignal(): FinishedSignal {
- return getEvent(mFinishedSignals)
- }
-
- fun getTimeline(): Timeline {
- return getEvent(mTimelines)
}
}
@@ -102,8 +62,8 @@ class InputEventSenderAndReceiverTest {
private const val TAG = "InputEventSenderAndReceiverTest"
}
private val mHandlerThread = HandlerThread("Process input events")
- private lateinit var mReceiver: TestInputEventReceiver
- private lateinit var mSender: TestInputEventSender
+ private lateinit var mReceiver: SpyInputEventReceiver
+ private lateinit var mSender: SpyInputEventSender
@Before
fun setUp() {
@@ -111,8 +71,8 @@ class InputEventSenderAndReceiverTest {
mHandlerThread.start()
val looper = mHandlerThread.getLooper()
- mSender = TestInputEventSender(channels[0], looper)
- mReceiver = TestInputEventReceiver(channels[1], looper)
+ mSender = SpyInputEventSender(channels[0], looper)
+ mReceiver = SpyInputEventReceiver(channels[1], looper)
}
@After
@@ -122,8 +82,7 @@ class InputEventSenderAndReceiverTest {
@Test
fun testSendAndReceiveKey() {
- val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_A, 0 /*repeat*/)
+ val key = getTestKeyEvent()
val seq = 10
mSender.sendInputEvent(seq, key)
val receivedKey = mReceiver.getInputEvent() as KeyEvent
@@ -133,13 +92,13 @@ class InputEventSenderAndReceiverTest {
assertKeyEvent(key, receivedKey)
// Check sender
- assertEquals(TestInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
}
// The timeline case is slightly unusual because it goes from InputConsumer to InputPublisher.
@Test
fun testSendAndReceiveTimeline() {
- val sent = TestInputEventSender.Timeline(
+ val sent = SpyInputEventSender.Timeline(
inputEventId = 1, gpuCompletedTime = 2, presentTime = 3)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
val received = mSender.getTimeline()
@@ -151,7 +110,7 @@ class InputEventSenderAndReceiverTest {
// event processing.
@Test
fun testSendAndReceiveInvalidTimeline() {
- val sent = TestInputEventSender.Timeline(
+ val sent = SpyInputEventSender.Timeline(
inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
val received = mSender.getTimeline()
@@ -162,4 +121,41 @@ class InputEventSenderAndReceiverTest {
val receivedSecondTimeline = mSender.getTimeline()
assertEquals(null, receivedSecondTimeline)
}
+
+ /**
+ * If a receiver throws an exception during 'onInputEvent' execution, the 'finally' block still
+ * completes, and therefore, finishInputEvent is called. Make sure that there's no crash in the
+ * native layer in these circumstances.
+ * In this test, we are reusing the 'mHandlerThread', but we are creating new sender and
+ * receiver.
+ */
+ @Test
+ fun testCrashingReceiverDoesNotCrash() {
+ val channels = InputChannel.openInputChannelPair("TestChannel2")
+ val sender = SpyInputEventSender(channels[0], mHandlerThread.getLooper())
+
+ // Need a separate thread for the receiver so that the sender can still get the response
+ // after the receiver crashes
+ val receiverThread = HandlerThread("Receive input events")
+ receiverThread.start()
+ val crashingReceiver = CrashingInputEventReceiver(channels[1], receiverThread.getLooper())
+ receiverThread.setUncaughtExceptionHandler { thread, exception ->
+ if (thread == receiverThread && exception is IllegalArgumentException) {
+ // do nothing - this is the exception that we need to ignore
+ } else {
+ throw exception
+ }
+ }
+
+ val key = getTestKeyEvent()
+ val seq = 11
+ sender.sendInputEvent(seq, key)
+ val finishedSignal = sender.getFinishedSignal()
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+
+ // Clean up
+ crashingReceiver.dispose()
+ sender.dispose()
+ receiverThread.quitSafely()
+ }
}
diff --git a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
new file mode 100644
index 000000000000..1099878a1954
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.test.input
+
+import android.os.HandlerThread
+import android.view.InputChannel
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.WindowManagerPolicyConstants.PointerEventListener
+
+import com.android.server.UiThread
+import com.android.server.wm.PointerEventDispatcher
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+private class CrashingPointerEventListener : PointerEventListener {
+ override fun onPointerEvent(motionEvent: MotionEvent) {
+ throw IllegalArgumentException("This listener crashes when input event occurs")
+ }
+}
+
+class PointerEventDispatcherTest {
+ companion object {
+ private const val TAG = "PointerEventDispatcherTest"
+ }
+ private val mHandlerThread = HandlerThread("Process input events")
+ private lateinit var mSender: SpyInputEventSender
+ private lateinit var mPointerEventDispatcher: PointerEventDispatcher
+ private val mListener = CrashingPointerEventListener()
+
+ @Before
+ fun setUp() {
+ val channels = InputChannel.openInputChannelPair("TestChannel")
+
+ mHandlerThread.start()
+ val looper = mHandlerThread.getLooper()
+ mSender = SpyInputEventSender(channels[0], looper)
+
+ mPointerEventDispatcher = PointerEventDispatcher(channels[1])
+ mPointerEventDispatcher.registerInputEventListener(mListener)
+ }
+
+ @After
+ fun tearDown() {
+ mHandlerThread.quitSafely()
+ }
+
+ @Test
+ fun testSendMotionToCrashingListenerDoesNotCrash() {
+ // The exception will occur on the UiThread, so we can't catch it here on the test thread
+ UiThread.get().setUncaughtExceptionHandler { thread, exception ->
+ if (thread == UiThread.get() && exception is IllegalArgumentException) {
+ // do nothing - this is the exception that we need to ignore
+ } else {
+ throw exception
+ }
+ }
+
+ // The MotionEvent properties aren't important for this test, as long as the event
+ // is a pointer event, so that it gets processed by CrashingPointerEventListener
+ val downTime = 0L
+ val motionEvent = MotionEvent.obtain(downTime, downTime,
+ MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */)
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ val seq = 10
+ mSender.sendInputEvent(seq, motionEvent)
+ val finishedSignal = mSender.getFinishedSignal()
+
+ // Since the listener raises an exception during the event handling, the event should be
+ // marked as 'not handled'.
+ assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = false), finishedSignal)
+ // Ensure that there aren't double finish calls. This would crash if there's a call
+ // to finish twice.
+ assertNull(mSender.getFinishedSignal())
+ }
+}
diff --git a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
new file mode 100644
index 000000000000..2d9af9a65d33
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.test.input
+
+import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
+import android.os.Looper
+import android.view.InputChannel
+import android.view.InputEvent
+import android.view.InputEventReceiver
+import android.view.InputEventSender
+import android.view.KeyEvent
+import android.view.MotionEvent
+import java.util.concurrent.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T? {
+ return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
+}
+
+class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
+ InputEventReceiver(channel, looper) {
+ private val mInputEvents = LinkedBlockingQueue<InputEvent>()
+
+ override fun onInputEvent(event: InputEvent) {
+ when (event) {
+ is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
+ is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
+ else -> throw Exception("Received $event is neither a key nor a motion")
+ }
+ finishInputEvent(event, true /*handled*/)
+ }
+
+ fun getInputEvent(): InputEvent? {
+ return getEvent(mInputEvents)
+ }
+}
+
+class SpyInputEventSender(channel: InputChannel, looper: Looper) :
+ InputEventSender(channel, looper) {
+ data class FinishedSignal(val seq: Int, val handled: Boolean)
+ data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
+
+ private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
+ private val mTimelines = LinkedBlockingQueue<Timeline>()
+
+ override fun onInputEventFinished(seq: Int, handled: Boolean) {
+ mFinishedSignals.put(FinishedSignal(seq, handled))
+ }
+
+ override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
+ mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
+ }
+
+ fun getFinishedSignal(): FinishedSignal? {
+ return getEvent(mFinishedSignals)
+ }
+
+ fun getTimeline(): Timeline? {
+ return getEvent(mTimelines)
+ }
+}
diff --git a/tests/InputMethodStressTest/Android.bp b/tests/InputMethodStressTest/Android.bp
new file mode 100644
index 000000000000..0ad38768238a
--- /dev/null
+++ b/tests/InputMethodStressTest/Android.bp
@@ -0,0 +1,36 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "InputMethodStressTest",
+ srcs: ["src/**/*.java"],
+ libs: ["android.test.runner"],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator_uiautomator",
+ "compatibility-device-util-axt",
+ "platform-test-annotations",
+ "platform-test-rules",
+ "truth-prebuilt",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
+ sdk_version: "31",
+}
diff --git a/tests/InputMethodStressTest/AndroidManifest.xml b/tests/InputMethodStressTest/AndroidManifest.xml
new file mode 100644
index 000000000000..f5fe8f2e8e76
--- /dev/null
+++ b/tests/InputMethodStressTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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="com.android.inputmethod.stresstest">
+
+ <application>
+ <activity android:name=".AutoShowTest$TestActivity"/>
+ <activity android:name=".ImeOpenCloseStressTest$TestActivity"/>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.inputmethod.stresstest">
+ </instrumentation>
+</manifest>
diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml
new file mode 100644
index 000000000000..9ac41351f684
--- /dev/null
+++ b/tests/InputMethodStressTest/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?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 description="InputMethod integration/regression test">
+ <option name="test-suite-tag" value="apct" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <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="InputMethodStressTest.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.inputmethod.stresstest" />
+ </test>
+
+ <!-- Collect the files in the dump directory for debugging -->
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/InputMethodStressTest/" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
+</configuration>
diff --git a/tests/InputMethodStressTest/OWNERS b/tests/InputMethodStressTest/OWNERS
new file mode 100644
index 000000000000..6bb4b17ed4eb
--- /dev/null
+++ b/tests/InputMethodStressTest/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/InputMethodStressTest/TEST_MAPPING b/tests/InputMethodStressTest/TEST_MAPPING
new file mode 100644
index 000000000000..ad07205ab02d
--- /dev/null
+++ b/tests/InputMethodStressTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "InputMethodStressTest"
+ }
+ ]
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
new file mode 100644
index 000000000000..c84c2bcf19c6
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/AutoShowTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.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.waitOnMainUntilImeIsShown;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.rule.UnlockScreenRule;
+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;
+
+@RootPermissionTest
+@RunWith(AndroidJUnit4.class)
+public final class AutoShowTest {
+
+ @Rule
+ public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+
+ @Rule
+ public ScreenCaptureRule mScreenCaptureRule =
+ new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+
+ @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);
+ 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();
+ }
+
+ public EditText getEditText() {
+ return mEditText;
+ }
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
new file mode 100644
index 000000000000..8419276f4406
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeOpenCloseStressTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.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.inputmethod.stresstest.ImeStressTestUtil.isImeShown;
+import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntil;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.rule.UnlockScreenRule;
+import android.util.Log;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.view.inputmethod.InputMethodManager;
+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.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RootPermissionTest
+@RunWith(AndroidJUnit4.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 =
+ new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setUp() {
+ 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++) {
+ 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));
+ }
+ }
+
+ @Test
+ public void testShowHide_waitingAnimationEnd() {
+ TestActivity activity = TestActivity.start();
+ 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",
+ () -> !activity.isAnimating() && isImeShown(editText));
+ mInstrumentation.runOnMainSync(activity::hideIme);
+ waitOnMainUntil(msgPrefix + "IME should be hidden",
+ () -> !activity.isAnimating() && !isImeShown(editText));
+ }
+ }
+
+ @Test
+ public void testShowHide_intervalAfterHide() {
+ // Regression test for b/221483132
+ TestActivity activity = TestActivity.start();
+ EditText editText = activity.getEditText();
+ // 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);
+ SystemClock.sleep(intervalMillis);
+ mInstrumentation.runOnMainSync(activity::showIme);
+ waitOnMainUntil(msgPrefix + "IME should be visible",
+ () -> isImeShown(editText));
+ }
+ }
+
+ @Test
+ public void testShowHideInSameFrame() {
+ TestActivity activity = TestActivity.start();
+ activity.enableAnimationMonitoring();
+ EditText editText = activity.getEditText();
+ waitOnMainUntil("activity should gain focus", editText::hasWindowFocus);
+
+ // hidden -> show -> hide
+ mInstrumentation.runOnMainSync(() -> {
+ Log.i(TAG, "Calling showIme() and hideIme()");
+ activity.showIme();
+ activity.hideIme();
+ });
+ // Wait until IMMS / IMS handles messages.
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ waitOnMainUntil("IME should be invisible after show/hide", () -> !isImeShown(editText));
+
+ mInstrumentation.runOnMainSync(activity::showIme);
+ waitOnMainUntil("IME should be visible",
+ () -> !activity.isAnimating() && isImeShown(editText));
+ mInstrumentation.waitForIdleSync();
+
+ // shown -> hide -> show
+ mInstrumentation.runOnMainSync(() -> {
+ Log.i(TAG, "Calling hideIme() and showIme()");
+ activity.hideIme();
+ activity.showIme();
+ });
+ // Wait until IMMS / IMS handles messages.
+ SystemClock.sleep(1000);
+ mInstrumentation.waitForIdleSync();
+ waitOnMainUntil("IME should be visible after hide/show",
+ () -> !activity.isAnimating() && isImeShown(editText));
+ }
+
+ public static class TestActivity extends Activity {
+
+ private EditText mEditText;
+ private boolean mIsAnimating;
+
+ 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;
+ }
+ };
+
+ 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);
+ }
+
+ @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);
+ }
+
+ public EditText getEditText() {
+ return mEditText;
+ }
+
+ public void showIme() {
+ Log.i(TAG, "TestActivity.showIme");
+ mEditText.requestFocus();
+ InputMethodManager imm = getSystemService(InputMethodManager.class);
+ imm.showSoftInput(mEditText, 0);
+ }
+
+ public void hideIme() {
+ Log.i(TAG, "TestActivity.hideIme");
+ InputMethodManager imm = getSystemService(InputMethodManager.class);
+ imm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+ }
+
+ 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;
+ }
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
new file mode 100644
index 000000000000..ba2ba3c75bc2
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -0,0 +1,80 @@
+/*
+ * 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.inputmethod.stresstest;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.view.View;
+import android.view.WindowInsets;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+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 ImeStressTestUtil() {
+ }
+
+ /** 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();
+ 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);
+ }
+ });
+ return result.get();
+ }
+
+ /**
+ * Waits until {@code pred} returns true, or throws on timeout.
+ *
+ * <p>The given {@code pred} will be called on the main thread.
+ */
+ public static void waitOnMainUntil(String message, Callable<Boolean> pred) {
+ eventually(() -> assertWithMessage(message).that(pred.call()).isTrue(), TIMEOUT);
+ }
+
+ /** 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);
+ }
+
+ /** 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);
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
new file mode 100644
index 000000000000..47f87d6d75ff
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/NotificationTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.inputmethod.stresstest;
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeFalse;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Icon;
+import android.platform.test.annotations.RootPermissionTest;
+import android.platform.test.rule.UnlockScreenRule;
+import android.provider.Settings;
+import android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+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.Until;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+@RootPermissionTest
+@RunWith(AndroidJUnit4.class)
+public final class NotificationTest {
+
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10);
+
+ private static final String CHANNEL_ID = "TEST_CHANNEL";
+ private static final String CHANNEL_NAME = "Test channel";
+
+ private static final String REPLY_INPUT_KEY = "REPLY_KEY";
+ private static final String REPLY_INPUT_LABEL = "Test reply label";
+ private static final String ACTION_REPLY = "com.android.inputmethod.stresstest.ACTION_REPLY";
+ private static final String REPLY_ACTION_LABEL = "Test reply";
+ private static final int REPLY_REQUEST_CODE = 1;
+
+ private static final String NOTIFICATION_TITLE = "Test notification";
+ private static final String NOTIFICATION_CONTENT = "Test notification content";
+ private static final int NOTIFICATION_ID = 2000;
+
+ // 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");
+
+ @Rule
+ public UnlockScreenRule mUnlockScreenRule = new UnlockScreenRule();
+
+ @Rule
+ public ScreenCaptureRule mScreenCaptureRule =
+ new ScreenCaptureRule("/sdcard/InputMethodStressTest");
+
+ private Context mContext;
+ private NotificationManager mNotificationManager;
+ private UiDevice mUiDevice;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ PackageManager pm = mContext.getPackageManager();
+ // Do not run on Automotive.
+ assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
+ // Do not run on TV. Direct Reply isn't supported on TV.
+ assumeFalse(pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
+ }
+
+ @After
+ public void tearDown() {
+ mNotificationManager.cancelAll();
+ }
+
+ @Test
+ public void testDirectReply() {
+ postMessagingNotification();
+ mUiDevice.openNotification();
+ // The text can be shown as-is, or all-caps, depending on the system.
+ Pattern actionLabelPattern = Pattern.compile(REPLY_ACTION_LABEL, Pattern.CASE_INSENSITIVE);
+ mUiDevice.wait(Until.findObject(By.text(actionLabelPattern)), TIMEOUT).click();
+ // Verify that IME is visible.
+ assertThat(mUiDevice.wait(Until.findObject(By.pkg(getImePackage(mContext))), TIMEOUT))
+ .isNotNull();
+ // Type something, which enables the Send button, then click the Send button.
+ 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();
+ // Verify that IME is gone.
+ assertThat(mUiDevice.wait(Until.gone(By.pkg(getImePackage(mContext))), TIMEOUT)).isTrue();
+ }
+
+ private void postMessagingNotification() {
+ // Register the channel. It's safe to register the same channel again and again.
+ NotificationChannel channel =
+ new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, IMPORTANCE_HIGH);
+ mNotificationManager.createNotificationChannel(channel);
+
+ // Post inline reply notification.
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, REPLY_REQUEST_CODE, new Intent().setAction(ACTION_REPLY),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
+ RemoteInput remoteInput = new RemoteInput.Builder(REPLY_INPUT_KEY)
+ .setLabel(REPLY_INPUT_LABEL)
+ .build();
+ Icon icon = Icon.createWithResource(mContext, android.R.drawable.ic_menu_edit);
+ Notification.Action action =
+ new Notification.Action.Builder(icon, REPLY_ACTION_LABEL, pendingIntent)
+ .addRemoteInput(remoteInput)
+ .build();
+ Notification notification = new Notification.Builder(mContext, CHANNEL_ID)
+ .setSmallIcon(android.R.drawable.ic_menu_edit)
+ .setContentTitle(NOTIFICATION_TITLE)
+ .setContentText(NOTIFICATION_CONTENT)
+ .addAction(action)
+ .build();
+ mNotificationManager.notify(NOTIFICATION_ID, notification);
+ }
+
+ private static String getImePackage(Context context) {
+ String imeId = Settings.Secure.getString(
+ context.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ ComponentName cn = ComponentName.unflattenFromString(imeId);
+ assertThat(cn).isNotNull();
+ return cn.getPackageName();
+ }
+}
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenCaptureRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenCaptureRule.java
new file mode 100644
index 000000000000..4e4ef2edd06b
--- /dev/null
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ScreenCaptureRule.java
@@ -0,0 +1,69 @@
+/*
+ * 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.inputmethod.stresstest;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Takes a screenshot when the test fails.
+ *
+ * <p>Use {@link com.android.tradefed.device.metric.FilePullerLogCollector} to collect screenshots
+ * taken.
+ * For example, in AndroidTest.xml:
+ * <code>
+ * <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ * <option name="directory-keys" value="/sdcard/MyTest/" />
+ * <option name="collect-on-run-ended-only" value="true" />
+ * </metrics_collector>
+ * </code>
+ * in MyTest.java:
+ * <code>
+ * @Rule
+ * public ScreenCaptureRule mScreenCaptureRule = new ScreenCaptureRule("/sdcard/MyTest");
+ * </code>
+ */
+public class ScreenCaptureRule extends TestWatcher {
+
+ private static final String TAG = "ScreenCaptureRule";
+
+ private final String mOutDir;
+
+ public ScreenCaptureRule(String outDir) {
+ mOutDir = outDir;
+ }
+
+ @Override
+ protected void failed(Throwable e, Description description) {
+ super.failed(e, description);
+ String time = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());
+ String fileName = "screenshot-" + time + ".png";
+ capture(fileName);
+ }
+
+ /** Take a screenshot. */
+ public void capture(String fileName) {
+ SystemUtil.runCommandAndPrintOnLogcat(TAG, String.format("mkdir -p %s", mOutDir));
+ SystemUtil.runCommandAndPrintOnLogcat(TAG,
+ String.format("screencap %s/%s", mOutDir, fileName));
+ }
+}
diff --git a/tests/Internal/src/com/android/internal/util/ParcellingTests.java b/tests/Internal/src/com/android/internal/util/ParcellingTests.java
new file mode 100644
index 000000000000..65a3436a4c5e
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/ParcellingTests.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.internal.util;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.util.Parcelling.BuiltIn.ForInstant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.time.Instant;
+
+/** Tests for {@link Parcelling}. */
+@SmallTest
+@Presubmit
+@RunWith(JUnit4.class)
+public class ParcellingTests {
+
+ private Parcel mParcel = Parcel.obtain();
+
+ @Test
+ public void forInstant_normal() {
+ testForInstant(Instant.ofEpochSecond(500L, 10));
+ }
+
+ @Test
+ public void forInstant_minimum() {
+ testForInstant(Instant.MIN);
+ }
+
+ @Test
+ public void forInstant_maximum() {
+ testForInstant(Instant.MAX);
+ }
+
+ @Test
+ public void forInstant_null() {
+ testForInstant(null);
+ }
+
+ private void testForInstant(Instant instant) {
+ Parcelling<Instant> parcelling = new ForInstant();
+ parcelling.parcel(instant, mParcel, 0);
+ mParcel.setDataPosition(0);
+
+ Instant created = parcelling.unparcel(mParcel);
+
+ if (instant == null) {
+ assertNull(created);
+ } else {
+ assertEquals(instant, created);
+ }
+ }
+
+}
diff --git a/tests/Internal/src/com/android/internal/util/UserIconsTest.java b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
new file mode 100644
index 000000000000..cc7b20b28a97
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/util/UserIconsTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UserIconsTest {
+
+ @Test
+ public void convertToBitmapAtUserIconSize_sizeIsCorrect() {
+ Resources res = InstrumentationRegistry.getTargetContext().getResources();
+ Drawable icon = UserIcons.getDefaultUserIcon(res, 0, true);
+ Bitmap bitmap = UserIcons.convertToBitmapAtUserIconSize(res, icon);
+ int expectedSize = res.getDimensionPixelSize(R.dimen.user_icon_size);
+
+ assertThat(bitmap.getWidth()).isEqualTo(expectedSize);
+ assertThat(bitmap.getHeight()).isEqualTo(expectedSize);
+ }
+
+}
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 e956be339bc4..dd9b294a9596 100644
--- a/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
+++ b/tests/JobSchedulerPerfTests/src/com/android/frameworks/perftests/job/JobStorePerfTests.java
@@ -82,11 +82,10 @@ public class JobStorePerfTests {
long elapsedTimeNs = 0;
while (benchmarkState.keepRunning(elapsedTimeNs)) {
- sJobStore.clear();
+ sJobStore.clearForTesting();
for (JobStatus job : jobList) {
- sJobStore.add(job);
+ sJobStore.addForTesting(job);
}
- sJobStore.waitForWriteToCompleteForTesting(10_000);
final long startTime = SystemClock.elapsedRealtimeNanos();
sJobStore.writeStatusToDiskForTesting();
@@ -110,11 +109,11 @@ public class JobStorePerfTests {
long elapsedTimeNs = 0;
while (benchmarkState.keepRunning(elapsedTimeNs)) {
- sJobStore.clear();
+ sJobStore.clearForTesting();
for (JobStatus job : jobList) {
- sJobStore.add(job);
+ sJobStore.addForTesting(job);
}
- sJobStore.waitForWriteToCompleteForTesting(10_000);
+ sJobStore.writeStatusToDiskForTesting();
JobSet jobSet = new JobSet();
diff --git a/tests/LockTaskTests/Android.bp b/tests/LockTaskTests/Android.bp
new file mode 100644
index 000000000000..dce681ead4b0
--- /dev/null
+++ b/tests/LockTaskTests/Android.bp
@@ -0,0 +1,32 @@
+// 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "LockTaskTests",
+
+ privileged: true,
+
+ sdk_version: "current",
+ certificate: "platform",
+
+ srcs: [
+ "src/**/I*.aidl",
+ "src/**/*.java",
+ ],
+
+}
diff --git a/tests/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk
deleted file mode 100644
index 5406ee19041b..000000000000
--- a/tests/LockTaskTests/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
-
-LOCAL_PACKAGE_NAME := LockTaskTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SRC_FILES := $(call all-Iaidl-files-under, src) $(call all-java-files-under, src)
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AppLaunchWear/Android.bp b/tests/MultiUser/Android.bp
index e2fc4735a7c2..bde309fe3015 100644
--- a/tests/AppLaunchWear/Android.bp
+++ b/tests/MultiUser/Android.bp
@@ -8,15 +8,20 @@ package {
}
android_test {
- name: "AppLaunchWear",
- // Only compile source java files in this apk.
- srcs: ["src/**/*.java"],
+ name: "MultiUserTests",
platform_apis: true,
- certificate: "platform",
+ srcs: ["src/**/*.java"],
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "services.core",
+ ],
libs: [
- "android.test.base",
"android.test.runner",
+ "android.test.base",
+ "android.test.mock",
],
- static_libs: ["androidx.test.rules"],
+ certificate: "platform",
test_suites: ["device-tests"],
}
diff --git a/tests/MultiUser/AndroidManifest.xml b/tests/MultiUser/AndroidManifest.xml
new file mode 100644
index 000000000000..9a25c0990de4
--- /dev/null
+++ b/tests/MultiUser/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.multiuser">
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <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.test.multiuser">
+ </instrumentation>
+</manifest>
diff --git a/tests/MultiUser/TEST_MAPPING b/tests/MultiUser/TEST_MAPPING
new file mode 100644
index 000000000000..0dbef6cbc6ff
--- /dev/null
+++ b/tests/MultiUser/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MultiUserTests"
+ }
+ ]
+}
diff --git a/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java b/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java
new file mode 100644
index 000000000000..1521cc617873
--- /dev/null
+++ b/tests/MultiUser/src/com/android/test/multiuser/MultiUserSettingsTests.java
@@ -0,0 +1,164 @@
+/*
+ * 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.test.multiuser;
+
+import static android.provider.Settings.Secure.FONT_WEIGHT_ADJUSTMENT;
+import static android.provider.Settings.System.FONT_SCALE;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class MultiUserSettingsTests {
+ private final Context mContext = getInstrumentation().getTargetContext();
+ private final ContentResolver mContentResolver = mContext.getContentResolver();
+
+ private static void waitForBroadcastIdle() throws InterruptedException {
+ final int sleepDuration = 1000;
+ final String cmdAmWaitForBroadcastIdle = "am wait-for-broadcast-idle";
+
+ Thread.sleep(sleepDuration);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmdAmWaitForBroadcastIdle);
+ Thread.sleep(sleepDuration);
+ }
+
+ private float getGlobalFontScale() {
+ return mContext.getResources().getConfiguration().fontScale;
+ }
+
+ private int getGlobalFontWeight() {
+ return mContext.getResources().getConfiguration().fontWeightAdjustment;
+ }
+
+ private float getFontScaleOfUser(int userId) {
+ return Settings.System.getFloatForUser(mContentResolver, FONT_SCALE, 1, userId);
+ }
+
+ private int getFontWeightOfUser(int userId) {
+ return Settings.Secure.getIntForUser(mContentResolver, FONT_WEIGHT_ADJUSTMENT, 1, userId);
+ }
+
+ private void setFontScaleOfUser(float fontScale, int userId) throws InterruptedException {
+ Settings.System.putFloatForUser(mContentResolver, FONT_SCALE, fontScale, userId);
+ waitForBroadcastIdle();
+ }
+
+ private void setFontWeightOfUser(int fontWeight, int userId) throws InterruptedException {
+ Settings.Secure.putIntForUser(mContentResolver, FONT_WEIGHT_ADJUSTMENT, fontWeight, userId);
+ waitForBroadcastIdle();
+ }
+
+ @Test
+ public void testChangingFontScaleOfABackgroundUser_shouldNotAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ UserManager userManager = UserManager.get(mContext);
+
+ final int backgroundUserId = userManager.createUser("test_user",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0).id;
+ final float oldFontScaleOfBgUser = getFontScaleOfUser(backgroundUserId);
+ final float oldGlobalFontScale = getGlobalFontScale();
+ final float newFontScaleOfBgUser = 1 + Math.max(oldGlobalFontScale, oldFontScaleOfBgUser);
+
+ try {
+ setFontScaleOfUser(newFontScaleOfBgUser, backgroundUserId);
+ final float newGlobalFontScale = getGlobalFontScale();
+ assertEquals(oldGlobalFontScale, newGlobalFontScale, 0);
+ } finally {
+ setFontScaleOfUser(oldFontScaleOfBgUser, backgroundUserId);
+ userManager.removeUser(backgroundUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontWeightOfABackgroundUser_shouldNotAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ UserManager userManager = UserManager.get(mContext);
+
+ final int backgroundUserId = userManager.createUser("test_user",
+ UserManager.USER_TYPE_FULL_SECONDARY, 0).id;
+ final int oldFontWeightOfBgUser = getFontWeightOfUser(backgroundUserId);
+ final int oldGlobalFontWeight = getGlobalFontWeight();
+ final int newFontWeightOfBgUser = 2 * Math.max(oldGlobalFontWeight, oldFontWeightOfBgUser);
+
+ try {
+ setFontWeightOfUser(newFontWeightOfBgUser, backgroundUserId);
+ final int newGlobalFontWeight = getGlobalFontWeight();
+ assertEquals(oldGlobalFontWeight, newGlobalFontWeight);
+ } finally {
+ setFontWeightOfUser(oldFontWeightOfBgUser, backgroundUserId);
+ userManager.removeUser(backgroundUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontScaleOfTheForegroundUser_shouldAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ final int currentUserId = mContext.getUserId();
+ final float oldFontScale = getFontScaleOfUser(currentUserId);
+ final float newFontScale = 1 + oldFontScale;
+
+ try {
+ setFontScaleOfUser(newFontScale, currentUserId);
+ final float globalFontScale = getGlobalFontScale();
+ assertEquals(newFontScale, globalFontScale, 0);
+ } finally {
+ setFontScaleOfUser(oldFontScale, currentUserId);
+ }
+ }
+
+ @Test
+ public void testChangingFontWeightOfTheForegroundUser_shouldAffectUI()
+ throws InterruptedException {
+
+ Assume.assumeTrue(UserManager.supportsMultipleUsers());
+
+ final int currentUserId = mContext.getUserId();
+ final int oldFontWeight = getFontWeightOfUser(currentUserId);
+ final int newFontWeight = 2 * oldFontWeight;
+
+ try {
+ setFontWeightOfUser(newFontWeight, currentUserId);
+ final int globalFontWeight = getGlobalFontWeight();
+ assertEquals(newFontWeight, globalFontWeight);
+ } finally {
+ setFontWeightOfUser(oldFontWeight, currentUserId);
+ }
+ }
+}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 6e1cef496f40..9f6ce4e8425b 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -31,7 +31,8 @@ android_test {
test_config: "RollbackTest.xml",
java_resources: [
":com.android.apex.apkrollback.test_v2",
- ":com.android.apex.apkrollback.test_v2Crashing"
+ ":com.android.apex.apkrollback.test_v2Crashing",
+ ":test.rebootless_apex_v2",
],
}
@@ -47,7 +48,10 @@ java_test_host {
],
test_suites: ["general-tests"],
test_config: "StagedRollbackTest.xml",
- data: [":com.android.apex.apkrollback.test_v1"],
+ data: [
+ ":com.android.apex.apkrollback.test_v1",
+ ":test.rebootless_apex_v1",
+ ],
}
java_test_host {
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 7b2a07fd80f8..cbdcb8869628 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -16,7 +16,6 @@
package com.android.tests.rollback;
-import static com.android.cts.install.lib.InstallUtils.processUserData;
import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage;
import static com.android.cts.rollback.lib.RollbackUtils.waitForAvailableRollback;
@@ -31,11 +30,9 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
import android.os.UserManager;
-import android.provider.DeviceConfig;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -55,7 +52,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -71,12 +67,6 @@ public class RollbackTest {
private static final String INSTRUMENTED_APP = "com.android.tests.rollback";
- // copied from PackageManagerService#PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS
- // TODO: find a better place for the property so that it can be imported in tests
- // maybe android.content.pm.PackageManager?
- private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
- "enable_rollback_timeout";
-
private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
return rollbacks.stream().anyMatch(
ri -> ri.getPackages().stream().anyMatch(
@@ -203,740 +193,6 @@ public class RollbackTest {
}
}
- /**
- * Test that multiple available rollbacks are properly persisted.
- */
- @Test
- public void testAvailableRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- Uninstall.packages(TestApp.B);
- Install.single(TestApp.B1).commit();
- Install.single(TestApp.B2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // Both test apps should now be available for rollback.
- RollbackInfo rollbackA = waitForAvailableRollback(TestApp.A);
- assertThat(rollbackA).isNotNull();
- assertThat(rollbackA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- RollbackInfo rollbackB = waitForAvailableRollback(TestApp.B);
- assertThat(rollbackB).isNotNull();
- assertThat(rollbackB).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // The apps should still be available for rollback.
- rollbackA = waitForAvailableRollback(TestApp.A);
- assertThat(rollbackA).isNotNull();
- assertThat(rollbackA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- rollbackB = waitForAvailableRollback(TestApp.B);
- assertThat(rollbackB).isNotNull();
- assertThat(rollbackB).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback of B should not rollback A
- RollbackUtils.rollback(rollbackB.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that available multi-package rollbacks are properly persisted.
- */
- @Test
- public void testAvailableMultiPackageRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.multi(TestApp.A1, TestApp.B1).commit();
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // The app should now be available for rollback.
- RollbackInfo availableA = waitForAvailableRollback(TestApp.A);
- assertThat(availableA).isNotNull();
- assertThat(availableA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- RollbackInfo availableB = waitForAvailableRollback(TestApp.B);
- assertThat(availableB).isNotNull();
- assertThat(availableB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Assert they're both the same rollback
- assertThat(availableA).hasRollbackId(availableB.getRollbackId());
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // The apps should still be available for rollback.
- availableA = waitForAvailableRollback(TestApp.A);
- assertThat(availableA).isNotNull();
- assertThat(availableA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- availableB = waitForAvailableRollback(TestApp.B);
- assertThat(availableB).isNotNull();
- assertThat(availableB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback of B should rollback A as well
- RollbackUtils.rollback(availableB.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
-
- RollbackInfo committedA = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committedA).isNotNull();
- assertThat(committedA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- RollbackInfo committedB = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committedB).isNotNull();
- assertThat(committedB).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Assert they're both the same rollback
- assertThat(committedA).hasRollbackId(committedB.getRollbackId());
- assertThat(committedA).hasRollbackId(availableA.getRollbackId());
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that recently committed rollback data is properly persisted.
- */
- @Test
- public void testRecentlyCommittedRollbackPersistence() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // The app should now be available for rollback.
- RollbackInfo available = waitForAvailableRollback(TestApp.A);
- assertThat(available).isNotNull();
-
- // Roll back the app.
- TestApp cause = new TestApp("Foo", "com.android.tests.rollback.testapp.Foo",
- /*versionCode*/ 42, /*isApex*/ false);
- RollbackUtils.rollback(available.getRollbackId(), cause);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- // Verify the recent rollback has been recorded.
- RollbackInfo committed = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committed).isNotNull();
- assertThat(committed).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(committed).causePackagesContainsExactly(cause);
-
- // Reload the persisted data.
- rm.reloadPersistedData();
-
- // Verify the recent rollback is still recorded.
- committed = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(committed).isNotNull();
- assertThat(committed).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(committed).causePackagesContainsExactly(cause);
- assertThat(committed).hasRollbackId(available.getRollbackId());
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test the scheduling aspect of rollback expiration.
- */
- @Test
- public void testRollbackExpiresAfterLifetime() throws Exception {
- long expirationTime = TimeUnit.SECONDS.toMillis(30);
- long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.WRITE_DEVICE_CONFIG);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(expirationTime), false /* makeDefault*/);
-
- // Uninstall TestApp.A
- Uninstall.packages(TestApp.A);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-
- // Install v1 of the app (without rollbacks enabled).
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- // Upgrade from v1 to v2, with rollbacks enabled.
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // Check that the rollback data has not expired
- Thread.sleep(1000);
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- // Give it a little more time, but still not long enough to expire
- Thread.sleep(expirationTime / 2);
- rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- // Check that the data has expired after the expiration time (with a buffer of 1 second)
- Thread.sleep(expirationTime / 2);
- rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNull();
-
- } finally {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(defaultExpirationTime), false /* makeDefault*/);
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that available rollbacks should expire correctly when the property
- * {@link RollbackManager#PROPERTY_ROLLBACK_LIFETIME_MILLIS} is changed
- */
- @Test
- public void testRollbackExpiresWhenLifetimeChanges() throws Exception {
- long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.WRITE_DEVICE_CONFIG);
-
- Uninstall.packages(TestApp.A);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
-
- // Change the lifetime to 0 which should expire rollbacks immediately
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(0), false /* makeDefault*/);
-
- // Keep polling until device config changes has happened (which might take more than
- // 5 sec depending how busy system_server is) and rollbacks have expired
- for (int i = 0; i < 30; ++i) {
- if (hasRollbackInclude(rm.getAvailableRollbacks(), TestApp.A)) {
- Thread.sleep(1000);
- }
- }
- rollback = getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNull();
- } finally {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(defaultExpirationTime), false /* makeDefault*/);
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that changing time on device does not affect the duration of time that we keep
- * rollback available
- */
- @Test
- public void testTimeChangeDoesNotAffectLifetime() throws Exception {
- long expirationTime = TimeUnit.SECONDS.toMillis(30);
- long defaultExpirationTime = TimeUnit.HOURS.toMillis(48);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.WRITE_DEVICE_CONFIG,
- Manifest.permission.SET_TIME);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(expirationTime), false /* makeDefault*/);
-
- // Install app A with rollback enabled
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- Thread.sleep(expirationTime / 2);
-
- // Install app B with rollback enabled
- Uninstall.packages(TestApp.B);
- Install.single(TestApp.B1).commit();
- Install.single(TestApp.B2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // 1 second buffer
- Thread.sleep(1000);
-
- try {
- // Change the time
- RollbackUtils.forwardTimeBy(expirationTime);
-
- // 1 second buffer to allow Rollback Manager to handle time change before loading
- // persisted data
- Thread.sleep(1000);
-
- // Load timestamps from storage
- rm.reloadPersistedData();
-
- // Wait until rollback for app A has expired
- // This will trigger an expiration run that should expire app A but not B
- Thread.sleep(expirationTime / 2);
- RollbackInfo rollbackA =
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A);
- Log.i(TAG, "Checking if the rollback for TestApp.A is null");
-
- // Rollback for app B should not be expired
- RollbackInfo rollbackB1 = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.B);
-
- // Wait until rollback for app B has expired
- Thread.sleep(expirationTime / 2);
- RollbackInfo rollbackB2 = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.B);
-
- assertThat(rollbackA).isNull();
- assertThat(rollbackB1).isNotNull();
- assertThat(rollbackB1).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
- assertThat(rollbackB2).isNull();
- } finally {
- RollbackUtils.forwardTimeBy(-expirationTime);
- }
- } finally {
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
- RollbackManager.PROPERTY_ROLLBACK_LIFETIME_MILLIS,
- Long.toString(defaultExpirationTime), false /* makeDefault*/);
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test explicit expiration of rollbacks.
- * Does not test the scheduling aspects of rollback expiration.
- */
- @Test
- public void testRollbackExpiration() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // The app should now be available for rollback.
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- // Expire the rollback.
- rm.expireRollbackForPackage(TestApp.A);
-
- // The rollback should no longer be available.
- assertThat(getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that app user data is rolled back.
- */
- @Test
- public void testUserDataRollback() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- processUserData(TestApp.A);
- Install.single(TestApp.A2).setEnableRollback().commit();
- processUserData(TestApp.A);
-
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- RollbackUtils.rollback(rollback.getRollbackId());
- processUserData(TestApp.A);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test rollback of apks involving splits.
- */
- @Test
- public void testRollbackWithSplits() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.ASplit1).commit();
- processUserData(TestApp.A);
-
- Install.single(TestApp.ASplit2).setEnableRollback().commit();
- processUserData(TestApp.A);
-
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- RollbackUtils.rollback(rollback.getRollbackId());
- processUserData(TestApp.A);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test restrictions on rollback broadcast sender.
- * A random app should not be able to send a ROLLBACK_COMMITTED broadcast.
- */
- @Test
- public void testRollbackBroadcastRestrictions() throws Exception {
- RollbackBroadcastReceiver broadcastReceiver = new RollbackBroadcastReceiver();
- Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
- try {
- InstrumentationRegistry.getContext().sendBroadcast(broadcast);
- fail("Succeeded in sending restricted broadcast from app context.");
- } catch (SecurityException se) {
- // Expected behavior.
- }
-
- // Confirm that we really haven't received the broadcast.
- // TODO: How long to wait for the expected timeout?
- assertThat(broadcastReceiver.poll(5, TimeUnit.SECONDS)).isNull();
-
- // TODO: Do we need to do this? Do we need to ensure this is always
- // called, even when the test fails?
- broadcastReceiver.unregister();
- }
-
- /**
- * Regression test for rollback in the case when multiple apps are
- * available for rollback at the same time.
- */
- @Test
- public void testMultipleRollbackAvailable() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- // Prep installation of the test apps.
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- Uninstall.packages(TestApp.B);
- Install.single(TestApp.B1).commit();
- Install.single(TestApp.B2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // Both test apps should now be available for rollback, and the
- // RollbackInfo returned for the rollbacks should be correct.
- RollbackInfo rollbackA = waitForAvailableRollback(TestApp.A);
- assertThat(rollbackA).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
-
- RollbackInfo rollbackB = waitForAvailableRollback(TestApp.B);
- assertThat(rollbackB).packagesContainsExactly(
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Executing rollback should roll back the correct package.
- RollbackUtils.rollback(rollbackA.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- Install.single(TestApp.A2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- RollbackUtils.rollback(rollbackB.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that the MANAGE_ROLLBACKS permission is required to call
- * RollbackManager APIs.
- */
- @Test
- public void testManageRollbacksPermission() throws Exception {
- // We shouldn't be allowed to call any of the RollbackManager APIs
- // without the MANAGE_ROLLBACKS permission.
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- try {
- rm.getAvailableRollbacks();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.getRecentlyCommittedRollbacks();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- // TODO: What if the implementation checks arguments for non-null
- // first? Then this test isn't valid.
- rm.commitRollback(0, Collections.emptyList(), null);
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.reloadPersistedData();
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
-
- try {
- rm.expireRollbackForPackage(TestApp.A);
- fail("expected SecurityException");
- } catch (SecurityException e) {
- // Expected.
- }
- }
-
- /**
- * Test that you cannot enable rollback for a package without the
- * MANAGE_ROLLBACKS permission.
- */
- @Test
- public void testEnableRollbackPermission() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().commit();
-
- // We expect v2 of the app was installed, but rollback has not
- // been enabled.
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test that you cannot enable rollback for a non-module package when
- * holding the MANAGE_ROLLBACKS permission.
- */
- @Test
- public void testNonModuleEnableRollback() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().commit();
-
- // We expect v2 of the app was installed, but rollback has not
- // been enabled because the test app is not a module.
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test rollback of multi-package installs is implemented.
- */
- @Test
- public void testMultiPackage() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- // Prep installation of the test apps.
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.multi(TestApp.A1, TestApp.B1).commit();
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // TestApp.A should now be available for rollback.
- RollbackInfo rollback = waitForAvailableRollback(TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- // Rollback the app. It should cause both test apps to be rolled
- // back.
- RollbackUtils.rollback(rollback.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
-
- // We should see recent rollbacks listed for both A and B.
- Thread.sleep(1000);
- RollbackInfo rollbackA = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
-
- RollbackInfo rollbackB = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.B);
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1),
- Rollback.from(TestApp.B2).to(TestApp.B1));
-
- assertThat(rollbackA).hasRollbackId(rollbackB.getRollbackId());
-
- processUserData(TestApp.A);
- processUserData(TestApp.B);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test failure to enable rollback for multi-package installs.
- * If any one of the packages fail to enable rollback, we shouldn't enable
- * rollback for any package.
- */
- @Test
- public void testMultiPackageEnableFail() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.single(TestApp.A1).commit();
- // We should fail to enable rollback here because TestApp B is not
- // already installed.
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- assertThat(getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A)).isNull();
- assertThat(getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.B)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
@Test
@Ignore("b/120200473")
/**
@@ -1074,195 +330,4 @@ public class RollbackTest {
InstallUtils.dropShellPermissionIdentity();
}
}
-
- @Test
- public void testEnableRollbackTimeoutFailsRollback() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.MANAGE_ROLLBACKS,
- Manifest.permission.WRITE_DEVICE_CONFIG);
-
- //setting the timeout to a very short amount that will definitely be triggered
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- Long.toString(0), false /* makeDefault*/);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- waitForUnavailableRollback(TestApp.A);
-
- // Block the RollbackManager to make extra sure it will not be
- // able to enable the rollback in time.
- rm.blockRollbackManager(TimeUnit.SECONDS.toMillis(1));
- Install.single(TestApp.A2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // Give plenty of time for RollbackManager to unblock and attempt
- // to make the rollback available before asserting that the
- // rollback was not made available.
- Thread.sleep(TimeUnit.SECONDS.toMillis(2));
- assertThat(
- getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TestApp.A)).isNull();
- } finally {
- //setting the timeout back to default
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- null, false /* makeDefault*/);
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- @Test
- public void testEnableRollbackTimeoutFailsRollback_MultiPackage() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.MANAGE_ROLLBACKS,
- Manifest.permission.WRITE_DEVICE_CONFIG);
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- Long.toString(5000), false /* makeDefault*/);
- RollbackManager rm = RollbackUtils.getRollbackManager();
-
- Uninstall.packages(TestApp.A, TestApp.B);
- Install.multi(TestApp.A1, TestApp.B1).commit();
- waitForUnavailableRollback(TestApp.A);
-
- // Block the 2nd session for 10s so it will not be able to enable the rollback in time.
- rm.blockRollbackManager(TimeUnit.SECONDS.toMillis(0));
- rm.blockRollbackManager(TimeUnit.SECONDS.toMillis(10));
- Install.multi(TestApp.A2, TestApp.B2).setEnableRollback().commit();
-
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
-
- // Give plenty of time for RollbackManager to unblock and attempt
- // to make the rollback available before asserting that the
- // rollback was not made available.
- Thread.sleep(TimeUnit.SECONDS.toMillis(2));
-
- List<RollbackInfo> available = rm.getAvailableRollbacks();
- assertThat(getUniqueRollbackInfoForPackage(available, TestApp.A)).isNull();
- assertThat(getUniqueRollbackInfoForPackage(available, TestApp.B)).isNull();
- } finally {
- //setting the timeout back to default
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS,
- null, false /* makeDefault*/);
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Test we can't enable rollback for non-whitelisted app without
- * TEST_MANAGE_ROLLBACKS permission
- */
- @Test
- public void testNonRollbackWhitelistedApp() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A);
- Install.single(TestApp.A1).commit();
- assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
-
- Install.single(TestApp.A2).setEnableRollback().commit();
- Thread.sleep(TimeUnit.SECONDS.toMillis(2));
- assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNull();
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- @Test
- public void testRollbackDataPolicy() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS);
-
- Uninstall.packages(TestApp.A, TestApp.B, TestApp.C);
- Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
- // Write user data version = 1
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- Install a2 = Install.single(TestApp.A2)
- .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
- Install b2 = Install.single(TestApp.B2)
- .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
- // The rollback data policy of C2 is specified in the manifest
- Install c2 = Install.single(TestApp.C2).setEnableRollback();
- Install.multi(a2, b2, c2).setEnableRollback().commit();
- // Write user data version = 2
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
- RollbackUtils.rollback(info.getRollbackId());
- // Read user data version from userdata.txt
- // A's user data version is -1 for user data is wiped.
- // B's user data version is 1 as rollback committed.
- // C's user data version is -1 for user data is wiped.
- assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.C)).isEqualTo(-1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
-
- /**
- * Tests an app can be rolled back to the previous signing key.
- *
- * <p>The rollback capability in the signing lineage allows an app to be updated to an APK
- * signed with a previous signing key in the lineage; however this often defeats the purpose
- * of key rotation as a compromised key could then be used to roll an app back to the previous
- * key. To avoid requiring the rollback capability to support app rollbacks the PackageManager
- * allows an app to be rolled back to the previous signing key if the rollback install reason
- * is set.
- */
- @Test
- public void testRollbackAfterKeyRotation() throws Exception {
- try {
- InstallUtils.adoptShellPermissionIdentity(
- Manifest.permission.INSTALL_PACKAGES,
- Manifest.permission.DELETE_PACKAGES,
- Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.MANAGE_ROLLBACKS);
-
- // Uninstall TestApp.A
- Uninstall.packages(TestApp.A);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-
- // Install v1 of the app with the original signing key (without rollbacks enabled).
- Install.single(TestApp.AOriginal1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- // Upgrade from v1 to v2 with the rotated signing key, with rollbacks enabled.
- Install.single(TestApp.ARotated2).setEnableRollback().commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
-
- // Roll back the app.
- RollbackInfo available = waitForAvailableRollback(TestApp.A);
- RollbackUtils.rollback(available.getRollbackId());
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- } finally {
- InstallUtils.dropShellPermissionIdentity();
- }
- }
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 642b19e6d961..4cddcfeb91dc 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,12 +22,10 @@ import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoFo
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
-import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.rollback.RollbackInfo;
import android.content.rollback.RollbackManager;
-import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -60,6 +58,7 @@ import java.util.concurrent.TimeUnit;
public class StagedRollbackTest {
private static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
"watchdog_trigger_failure_count";
+ private static final String REBOOTLESS_APEX_NAME = "test.apex.rebootless";
/**
* Adopts common shell permissions needed for rollback tests.
@@ -82,155 +81,6 @@ public class StagedRollbackTest {
InstallUtils.dropShellPermissionIdentity();
}
- /**
- * Test rollbacks of staged installs involving only apks with bad update.
- * Enable rollback phase.
- */
- @Test
- public void testBadApkOnly_Phase1_Install() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- InstallUtils.processUserData(TestApp.A);
-
- Install.single(TestApp.ACrashing2).setEnableRollback().setStaged().commit();
- }
-
- /**
- * Test rollbacks of staged installs involving only apks with bad update.
- * Confirm that rollback was successfully enabled.
- */
- @Test
- public void testBadApkOnly_Phase2_VerifyInstall() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- InstallUtils.processUserData(TestApp.A);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(rollback.isStaged()).isTrue();
-
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT,
- Integer.toString(5), false);
- RollbackUtils.sendCrashBroadcast(TestApp.A, 4);
- // Sleep for a while to make sure we don't trigger rollback
- Thread.sleep(TimeUnit.SECONDS.toMillis(30));
- }
-
- /**
- * Test rollbacks of staged installs involving only apks.
- * Confirm rollback phase.
- */
- @Test
- public void testBadApkOnly_Phase3_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- InstallUtils.processUserData(TestApp.A);
-
- RollbackManager rm = RollbackUtils.getRollbackManager();
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getRecentlyCommittedRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(
- Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(rollback).causePackagesContainsExactly(TestApp.ACrashing2);
- assertThat(rollback).isStaged();
- assertThat(rollback.getCommittedSessionId()).isNotEqualTo(-1);
- }
-
- /**
- * Stage install an apk with rollback that will be later triggered by unattributable crash.
- */
- @Test
- public void testNativeWatchdogTriggersRollback_Phase1_Install() throws Exception {
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
- }
-
- /**
- * Verify the rollback is available.
- */
- @Test
- public void testNativeWatchdogTriggersRollback_Phase2_VerifyInstall() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- TestApp.A)).isNotNull();
- }
-
- /**
- * Verify the rollback is committed after crashing.
- */
- @Test
- public void testNativeWatchdogTriggersRollback_Phase3_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- TestApp.A)).isNotNull();
- }
-
- /**
- * Stage install an apk with rollback that will be later triggered by unattributable crash.
- */
- @Test
- public void testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA() throws Exception {
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
-
- Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
- }
-
- /**
- * Verify the rollback is available and then install another package with rollback.
- */
- @Test
- public void testNativeWatchdogTriggersRollbackForAll_Phase2_InstallB() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- TestApp.A)).isNotNull();
-
- // Install another package with rollback
- Install.single(TestApp.B1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
-
- Install.single(TestApp.B2).setEnableRollback().setStaged().commit();
- }
-
- /**
- * Verify the rollbacks are available.
- */
- @Test
- public void testNativeWatchdogTriggersRollbackForAll_Phase3_VerifyInstall() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- TestApp.A)).isNotNull();
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- TestApp.B)).isNotNull();
- }
-
- /**
- * Verify the rollbacks are committed after crashing.
- */
- @Test
- public void testNativeWatchdogTriggersRollbackForAll_Phase4_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- TestApp.A)).isNotNull();
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- TestApp.B)).isNotNull();
- }
-
@Test
public void testPreviouslyAbandonedRollbacks_Phase1_InstallAndAbandon() throws Exception {
Install.single(TestApp.A1).commit();
@@ -293,50 +143,6 @@ public class StagedRollbackTest {
}
@Test
- public void testRollbackDataPolicy_Phase1_Install() throws Exception {
- Install.multi(TestApp.A1, TestApp.B1, TestApp.C1).commit();
- // Write user data version = 1
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- Install a2 = Install.single(TestApp.A2).setStaged()
- .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
- Install b2 = Install.single(TestApp.B2).setStaged()
- .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
- // The rollback data policy of C2 is specified in the manifest
- Install c2 = Install.single(TestApp.C2).setStaged().setEnableRollback();
- Install.multi(a2, b2, c2).setEnableRollback().setStaged().commit();
- }
-
- @Test
- public void testRollbackDataPolicy_Phase2_Rollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
- // Write user data version = 2
- InstallUtils.processUserData(TestApp.A);
- InstallUtils.processUserData(TestApp.B);
- InstallUtils.processUserData(TestApp.C);
-
- RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
- RollbackUtils.rollback(info.getRollbackId());
- }
-
- @Test
- public void testRollbackDataPolicy_Phase3_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.C)).isEqualTo(1);
- // Read user data version from userdata.txt
- // A's user data version is -1 for user data is wiped.
- // B's user data version is 1 as rollback committed.
- // C's user data version is -1 for user data is wiped.
- assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
- assertThat(InstallUtils.getUserDataVersion(TestApp.C)).isEqualTo(-1);
- }
-
- @Test
public void expireRollbacks() throws Exception {
// testNativeWatchdogTriggersRollback will fail if multiple staged sessions are
// committed on a device which doesn't support checkpoint. Let's clean up all rollbacks
@@ -359,60 +165,14 @@ public class StagedRollbackTest {
"TestApexWithApkV2Crashing", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true,
APK_IN_APEX_TESTAPEX_NAME + "_v2Crashing.apex");
- @Test
- public void testRollbackApexWithApk_Phase1_Install() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- InstallUtils.processUserData(TestApp.A);
-
- int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback()
- .commit();
- InstallUtils.waitForSessionReady(sessionId);
- }
-
- @Test
- public void testRollbackApexWithApk_Phase2_Rollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- InstallUtils.processUserData(TestApp.A);
-
- RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
- assertThat(available).isStaged();
- assertThat(available).packagesContainsExactly(
- Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
- Rollback.from(TestApp.A, 0).to(TestApp.A1));
-
- RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
- RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
- assertThat(committed).isNotNull();
- assertThat(committed).isStaged();
- assertThat(committed).packagesContainsExactly(
- Rollback.from(TEST_APEX_WITH_APK_V2).to(TEST_APEX_WITH_APK_V1),
- Rollback.from(TestApp.A, 0).to(TestApp.A1));
- assertThat(committed).causePackagesContainsExactly(TEST_APEX_WITH_APK_V2);
- assertThat(committed.getCommittedSessionId()).isNotEqualTo(-1);
-
- // Note: The app is not rolled back until after the rollback is staged
- // and the device has been rebooted.
- InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
- assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(2);
- }
-
- @Test
- public void testRollbackApexWithApk_Phase3_VerifyRollback() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(APK_IN_APEX_TESTAPEX_NAME)).isEqualTo(1);
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- InstallUtils.processUserData(TestApp.A);
- }
-
/**
* Installs an apex with an apk that can crash.
*/
@Test
public void testRollbackApexWithApkCrashing_Phase1_Install() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- int sessionId = Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged()
+ Install.single(TEST_APEX_WITH_APK_V2_CRASHING).setStaged()
.setEnableRollback().commit();
- InstallUtils.waitForSessionReady(sessionId);
}
/**
@@ -442,52 +202,6 @@ public class StagedRollbackTest {
}
@Test
- public void testRollbackApexDataDirectories_Phase1_Install() throws Exception {
- int sessionId = Install.single(TEST_APEX_WITH_APK_V2).setStaged().setEnableRollback()
- .commit();
- InstallUtils.waitForSessionReady(sessionId);
- }
-
- @Test
- public void testRollbackApexDataDirectories_Phase2_Rollback() throws Exception {
- RollbackInfo available = RollbackUtils.getAvailableRollback(APK_IN_APEX_TESTAPEX_NAME);
-
- RollbackUtils.rollback(available.getRollbackId(), TEST_APEX_WITH_APK_V2);
- RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
-
- // Note: The app is not rolled back until after the rollback is staged
- // and the device has been rebooted.
- InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
- }
-
- @Test
- public void testRollbackApkDataDirectories_Phase1_InstallV1() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- }
-
- @Test
- public void testRollbackApkDataDirectories_Phase2_InstallV2() throws Exception {
- Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
- }
-
- @Test
- public void testRollbackApkDataDirectories_Phase3_Rollback() throws Exception {
- RollbackInfo available = RollbackUtils.getAvailableRollback(TestApp.A);
- RollbackUtils.rollback(available.getRollbackId(), TestApp.A2);
- RollbackInfo committed = RollbackUtils.getCommittedRollbackById(available.getRollbackId());
- InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
- }
-
- @Test
- public void isCheckpointSupported() {
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
- assertThat(sm.isCheckpointSupported()).isTrue();
- }
-
- @Test
public void testWatchdogMonitorsAcrossReboots_Phase1_Install() throws Exception {
assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
Install.single(TestApp.A1).commit();
@@ -527,30 +241,49 @@ public class StagedRollbackTest {
}
@Test
- public void testExpireSession_Phase1_Install() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
- Install.single(TestApp.A1).commit();
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
- Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
+ public void testRollbackRebootlessApex() throws Exception {
+ final String packageName = REBOOTLESS_APEX_NAME;
+ assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(1);
+
+ // install
+ TestApp apex1 = new TestApp("TestRebootlessApexV1", packageName, 1,
+ /* isApex= */ true, "test.rebootless_apex_v1.apex");
+ TestApp apex2 = new TestApp("TestRebootlessApexV2", packageName, 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ Install.single(apex2).setEnableRollback(PackageManager.ROLLBACK_DATA_POLICY_RETAIN)
+ .commit();
+
+ // verify rollback
+ assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(2);
+ RollbackInfo rollback = RollbackUtils.waitForAvailableRollback(packageName);
+ assertThat(rollback).packagesContainsExactly(Rollback.from(apex2).to(apex1));
+ assertThat(rollback).isNotStaged();
+
+ // rollback
+ RollbackUtils.rollback(rollback.getRollbackId());
+ assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(1);
}
@Test
- public void testExpireSession_Phase2_VerifyInstall() throws Exception {
- assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
- RollbackManager rm = RollbackUtils.getRollbackManager();
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
- assertThat(rollback).packagesContainsExactly(Rollback.from(TestApp.A2).to(TestApp.A1));
- assertThat(rollback.isStaged()).isTrue();
+ public void testNativeWatchdogTriggersRebootlessApexRollback_Phase1_Install() throws Exception {
+ assertThat(InstallUtils.getInstalledVersion(REBOOTLESS_APEX_NAME)).isEqualTo(1);
+
+ TestApp apex2 = new TestApp("TestRebootlessApexV2", REBOOTLESS_APEX_NAME, 2,
+ /* isApex= */ true, "test.rebootless_apex_v2.apex");
+ Install.single(apex2).setEnableRollback(PackageManager.ROLLBACK_DATA_POLICY_RETAIN)
+ .commit();
+ Install.single(TestApp.A1).commit();
+ Install.single(TestApp.A2).setEnableRollback().commit();
+
+ RollbackUtils.waitForAvailableRollback(TestApp.A);
+ RollbackUtils.waitForAvailableRollback(REBOOTLESS_APEX_NAME);
}
@Test
- public void testExpireSession_Phase3_VerifyRollback() throws Exception {
- RollbackManager rm = RollbackUtils.getRollbackManager();
- RollbackInfo rollback = getUniqueRollbackInfoForPackage(
- rm.getAvailableRollbacks(), TestApp.A);
- assertThat(rollback).isNotNull();
+ public void testNativeWatchdogTriggersRebootlessApexRollback_Phase2_Verify() throws Exception {
+ // Check only rebootless apex is rolled back. Other rollbacks should remain unchanged.
+ assertThat(RollbackUtils.getCommittedRollback(REBOOTLESS_APEX_NAME)).isNotNull();
+ assertThat(RollbackUtils.getAvailableRollback(TestApp.A)).isNotNull();
}
@Test
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 1aa5c249ff18..05e8bde23008 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -21,13 +21,9 @@ import static com.android.tests.rollback.host.WatchdogEventLogger.Subject.assert
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
-import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.Log;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.IFileEntry;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -40,12 +36,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
-import java.time.Instant;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* Runs the staged rollback tests.
@@ -72,17 +63,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
private static final String TESTAPP_A = "com.android.cts.install.lib.testapp.A";
- private static final String TEST_SUBDIR = "/subdir/";
-
- private static final String TEST_FILENAME_1 = "test_file.txt";
- private static final String TEST_STRING_1 = "hello this is a test";
- private static final String TEST_FILENAME_2 = "another_file.txt";
- private static final String TEST_STRING_2 = "this is a different file";
- private static final String TEST_FILENAME_3 = "also.xyz";
- private static final String TEST_STRING_3 = "also\n a\n test\n string";
- private static final String TEST_FILENAME_4 = "one_more.test";
- private static final String TEST_STRING_4 = "once more unto the test";
-
private static final String REASON_APP_CRASH = "REASON_APP_CRASH";
private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH";
@@ -116,7 +96,9 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
"/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "*",
- apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "*");
+ apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "*",
+ "/system/apex/test.rebootless_apex_v*.apex",
+ "/data/apex/active/test.apex.rebootless*.apex");
}
/**
@@ -124,26 +106,26 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
- boolean found = false;
- for (String file : files) {
- CommandResult result = getDevice().executeShellV2Command("ls " + file);
- if (result.getStatus() == CommandStatus.SUCCESS) {
- found = true;
- break;
+ try {
+ getDevice().enableAdbRoot();
+ boolean found = false;
+ for (String file : files) {
+ CommandResult result = getDevice().executeShellV2Command("ls " + file);
+ if (result.getStatus() == CommandStatus.SUCCESS) {
+ found = true;
+ break;
+ }
}
- }
- if (found) {
- try {
- getDevice().enableAdbRoot();
+ if (found) {
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
}
- } finally {
- getDevice().disableAdbRoot();
+ getDevice().reboot();
}
- getDevice().reboot();
+ } finally {
+ getDevice().disableAdbRoot();
}
}
@@ -153,98 +135,6 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
}
/**
- * Tests watchdog triggered staged rollbacks involving only apks.
- */
- @Test
- public void testBadApkOnly() throws Exception {
- runPhase("testBadApkOnly_Phase1_Install");
- getDevice().reboot();
- runPhase("testBadApkOnly_Phase2_VerifyInstall");
-
- // Launch the app to crash to trigger rollback
- startActivity(TESTAPP_A);
- // Wait for reboot to happen
- waitForDeviceNotAvailable(2, TimeUnit.MINUTES);
-
- getDevice().waitForDeviceAvailable();
-
- runPhase("testBadApkOnly_Phase3_VerifyRollback");
-
- assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_APP_CRASH, TESTAPP_A);
- assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
- assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
- }
-
- @Test
- public void testNativeWatchdogTriggersRollback() throws Exception {
- runPhase("testNativeWatchdogTriggersRollback_Phase1_Install");
-
- // Reboot device to activate staged package
- getDevice().reboot();
-
- runPhase("testNativeWatchdogTriggersRollback_Phase2_VerifyInstall");
-
- // crash system_server enough times to trigger a rollback
- crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
-
- // Rollback should be committed automatically now.
- // Give time for rollback to be committed. This could take a while,
- // because we need all of the following to happen:
- // 1. system_server comes back up and boot completes.
- // 2. Rollback health observer detects updatable crashing signal.
- // 3. Staged rollback session becomes ready.
- // 4. Device actually reboots.
- // So we give a generous timeout here.
- waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
- getDevice().waitForDeviceAvailable();
-
- // verify rollback committed
- runPhase("testNativeWatchdogTriggersRollback_Phase3_VerifyRollback");
-
- assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
- assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
- assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
- }
-
- @Test
- public void testNativeWatchdogTriggersRollbackForAll() throws Exception {
- // This test requires committing multiple staged rollbacks
- assumeTrue(isCheckpointSupported());
-
- // Install a package with rollback enabled.
- runPhase("testNativeWatchdogTriggersRollbackForAll_Phase1_InstallA");
- getDevice().reboot();
-
- // Once previous staged install is applied, install another package
- runPhase("testNativeWatchdogTriggersRollbackForAll_Phase2_InstallB");
- getDevice().reboot();
-
- // Verify the new staged install has also been applied successfully.
- runPhase("testNativeWatchdogTriggersRollbackForAll_Phase3_VerifyInstall");
-
- // crash system_server enough times to trigger a rollback
- crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
-
- // Rollback should be committed automatically now.
- // Give time for rollback to be committed. This could take a while,
- // because we need all of the following to happen:
- // 1. system_server comes back up and boot completes.
- // 2. Rollback health observer detects updatable crashing signal.
- // 3. Staged rollback session becomes ready.
- // 4. Device actually reboots.
- // So we give a generous timeout here.
- waitForDeviceNotAvailable(5, TimeUnit.MINUTES);
- getDevice().waitForDeviceAvailable();
-
- // verify all available rollbacks have been committed
- runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4_VerifyRollback");
-
- assertThat(mLogger).eventOccurred(ROLLBACK_INITIATE, null, REASON_NATIVE_CRASH, null);
- assertThat(mLogger).eventOccurred(ROLLBACK_BOOT_TRIGGERED, null, null, null);
- assertThat(mLogger).eventOccurred(ROLLBACK_SUCCESS, null, null, null);
- }
-
- /**
* Tests rolling back user data where there are multiple rollbacks for that package.
*/
@Test
@@ -267,44 +157,12 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
runPhase("testRollbackAllowlistedApp_Phase2_VerifyInstall");
}
- @Test
- public void testRollbackDataPolicy() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc_ce/0/rollback");
-
- runPhase("testRollbackDataPolicy_Phase1_Install");
- getDevice().reboot();
- runPhase("testRollbackDataPolicy_Phase2_Rollback");
- getDevice().reboot();
- runPhase("testRollbackDataPolicy_Phase3_VerifyRollback");
-
- // Verify snapshots are deleted after restoration
- List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- assertDirectoryIsEmpty(after.get(0));
- }
-
- /**
- * Tests that userdata of apk-in-apex is restored when apex is rolled back.
- */
- @Test
- public void testRollbackApexWithApk() throws Exception {
- pushTestApex();
- runPhase("testRollbackApexWithApk_Phase1_Install");
- getDevice().reboot();
- runPhase("testRollbackApexWithApk_Phase2_Rollback");
- getDevice().reboot();
- runPhase("testRollbackApexWithApk_Phase3_VerifyRollback");
- }
-
/**
* Tests that RollbackPackageHealthObserver is observing apk-in-apex.
*/
@Test
public void testRollbackApexWithApkCrashing() throws Exception {
- pushTestApex();
+ pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
// Install an apex with apk that crashes
runPhase("testRollbackApexWithApkCrashing_Phase1_Install");
@@ -325,239 +183,24 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
}
/**
- * Tests that data in DE_sys apex data directory is restored when apex is rolled back.
- */
- @Test
- public void testRollbackApexDataDirectories_DeSys() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc/apexrollback");
- pushTestApex();
-
- // Push files to apex data directory
- String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1;
- String oldFilePath2 =
- apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2;
- runAsRoot(() -> {
- pushString(TEST_STRING_1, oldFilePath1);
- pushString(TEST_STRING_2, oldFilePath2);
- });
-
- // Install new version of the APEX with rollback enabled
- runPhase("testRollbackApexDataDirectories_Phase1_Install");
- getDevice().reboot();
-
- // Replace files in data directory
- String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3;
- String newFilePath4 =
- apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4;
- runAsRoot(() -> {
- getDevice().deleteFile(oldFilePath1);
- getDevice().deleteFile(oldFilePath2);
- pushString(TEST_STRING_3, newFilePath3);
- pushString(TEST_STRING_4, newFilePath4);
- });
-
- // Roll back the APEX
- runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
- getDevice().reboot();
-
- // Verify that old files have been restored and new files are gone
- runAsRoot(() -> {
- assertFileContents(TEST_STRING_1, oldFilePath1);
- assertFileContents(TEST_STRING_2, oldFilePath2);
- assertFileNotExists(newFilePath3);
- assertFileNotExists(newFilePath4);
- });
-
- // Verify snapshots are deleted after restoration
- List<String> after = getSnapshotDirectories("/data/misc/apexrollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc/apexrollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- assertDirectoryIsEmpty(after.get(0));
- }
-
- /**
- * Tests that data in DE (user) apex data directory is restored when apex is rolled back.
+ * Tests rollback is supported correctly for rebootless apex
*/
@Test
- public void testRollbackApexDataDirectories_DeUser() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc_de/0/apexrollback");
- pushTestApex();
-
- // Push files to apex data directory
- String oldFilePath1 = apexDataDirDeUser(
- APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
- String oldFilePath2 =
- apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- runAsRoot(() -> {
- pushString(TEST_STRING_1, oldFilePath1);
- pushString(TEST_STRING_2, oldFilePath2);
- });
-
- // Install new version of the APEX with rollback enabled
- runPhase("testRollbackApexDataDirectories_Phase1_Install");
- getDevice().reboot();
-
- // Replace files in data directory
- String newFilePath3 =
- apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
- String newFilePath4 =
- apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- runAsRoot(() -> {
- getDevice().deleteFile(oldFilePath1);
- getDevice().deleteFile(oldFilePath2);
- pushString(TEST_STRING_3, newFilePath3);
- pushString(TEST_STRING_4, newFilePath4);
- });
-
- // Roll back the APEX
- runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
- getDevice().reboot();
-
- // Verify that old files have been restored and new files are gone
- runAsRoot(() -> {
- assertFileContents(TEST_STRING_1, oldFilePath1);
- assertFileContents(TEST_STRING_2, oldFilePath2);
- assertFileNotExists(newFilePath3);
- assertFileNotExists(newFilePath4);
- });
-
- // Verify snapshots are deleted after restoration
- List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- assertDirectoryIsEmpty(after.get(0));
- }
-
- /**
- * Tests that data in CE apex data directory is restored when apex is rolled back.
- */
- @Test
- public void testRollbackApexDataDirectories_Ce() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
- pushTestApex();
-
- // Push files to apex data directory
- String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
- String oldFilePath2 =
- apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- runAsRoot(() -> {
- pushString(TEST_STRING_1, oldFilePath1);
- pushString(TEST_STRING_2, oldFilePath2);
- });
-
- // Install new version of the APEX with rollback enabled
- runPhase("testRollbackApexDataDirectories_Phase1_Install");
- getDevice().reboot();
-
- // Replace files in data directory
- String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3;
- String newFilePath4 =
- apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- runAsRoot(() -> {
- getDevice().deleteFile(oldFilePath1);
- getDevice().deleteFile(oldFilePath2);
- pushString(TEST_STRING_3, newFilePath3);
- pushString(TEST_STRING_4, newFilePath4);
- });
-
- // Roll back the APEX
- runPhase("testRollbackApexDataDirectories_Phase2_Rollback");
- getDevice().reboot();
-
- // Verify that old files have been restored and new files are gone
- runAsRoot(() -> {
- assertFileContents(TEST_STRING_1, oldFilePath1);
- assertFileContents(TEST_STRING_2, oldFilePath2);
- assertFileNotExists(newFilePath3);
- assertFileNotExists(newFilePath4);
- });
-
- // Verify snapshots are deleted after restoration
- List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- assertDirectoryIsEmpty(after.get(0));
+ public void testRollbackRebootlessApex() throws Exception {
+ pushTestApex("test.rebootless_apex_v1.apex");
+ runPhase("testRollbackRebootlessApex");
}
/**
- * Tests that data in DE apk data directory is restored when apk is rolled back.
+ * Tests only rebootless apex (if any) is rolled back when native crash happens
*/
@Test
- public void testRollbackApkDataDirectories_De() throws Exception {
- // Install version 1 of TESTAPP_A
- runPhase("testRollbackApkDataDirectories_Phase1_InstallV1");
-
- // Push files to apk data directory
- String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1;
- String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- runAsRoot(() -> {
- pushString(TEST_STRING_1, oldFilePath1);
- pushString(TEST_STRING_2, oldFilePath2);
- });
-
- // Install version 2 of TESTAPP_A with rollback enabled
- runPhase("testRollbackApkDataDirectories_Phase2_InstallV2");
- getDevice().reboot();
-
- // Replace files in data directory
- String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3;
- String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4;
- runAsRoot(() -> {
- getDevice().deleteFile(oldFilePath1);
- getDevice().deleteFile(oldFilePath2);
- pushString(TEST_STRING_3, newFilePath3);
- pushString(TEST_STRING_4, newFilePath4);
- });
-
- // Roll back the APK
- runPhase("testRollbackApkDataDirectories_Phase3_Rollback");
- getDevice().reboot();
-
- // Verify that old files have been restored and new files are gone
- runAsRoot(() -> {
- assertFileContents(TEST_STRING_1, oldFilePath1);
- assertFileContents(TEST_STRING_2, oldFilePath2);
- assertFileNotExists(newFilePath3);
- assertFileNotExists(newFilePath4);
- });
- }
-
- @Test
- public void testExpireApexRollback() throws Exception {
- List<String> before = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
- pushTestApex();
-
- // Push files to apex data directory
- String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1;
- String oldFilePath2 =
- apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2;
- runAsRoot(() -> {
- pushString(TEST_STRING_1, oldFilePath1);
- pushString(TEST_STRING_2, oldFilePath2);
- });
-
- // Install new version of the APEX with rollback enabled
- runPhase("testRollbackApexDataDirectories_Phase1_Install");
- getDevice().reboot();
-
- List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
- // Only check directories newly created during the test
- after.removeAll(before);
- // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
- assertThat(after).hasSize(1);
- // Expire all rollbacks and check CE snapshot directories are deleted
- runPhase("expireRollbacks");
- runAsRoot(() -> {
- for (String dir : after) {
- assertFileNotExists(dir);
- }
- });
+ public void testNativeWatchdogTriggersRebootlessApexRollback() throws Exception {
+ pushTestApex("test.rebootless_apex_v1.apex");
+ runPhase("testNativeWatchdogTriggersRebootlessApexRollback_Phase1_Install");
+ crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
+ getDevice().waitForDeviceAvailable();
+ runPhase("testNativeWatchdogTriggersRebootlessApexRollback_Phase2_Verify");
}
/**
@@ -584,30 +227,8 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
runPhase("testWatchdogMonitorsAcrossReboots_Phase3_VerifyRollback");
}
- /**
- * Tests an available rollback shouldn't be deleted when its session expires.
- */
- @Test
- public void testExpireSession() throws Exception {
- runPhase("testExpireSession_Phase1_Install");
- getDevice().reboot();
- runPhase("testExpireSession_Phase2_VerifyInstall");
-
- // Advance system clock by 7 days to expire the staged session
- Instant t1 = Instant.ofEpochMilli(getDevice().getDeviceDate());
- Instant t2 = t1.plusMillis(TimeUnit.DAYS.toMillis(7));
- runAsRoot(() -> getDevice().setDate(Date.from(t2)));
-
- // Somehow we need to wait for a while before reboot. Otherwise the change to the
- // system clock will be reset after reboot.
- Thread.sleep(3000);
- getDevice().reboot();
- runPhase("testExpireSession_Phase3_VerifyRollback");
- }
-
- private void pushTestApex() throws Exception {
+ private void pushTestApex(String fileName) throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
- final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex";
final File apex = buildHelper.getTestFile(fileName);
try {
getDevice().enableAdbRoot();
@@ -619,101 +240,20 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
getDevice().reboot();
}
- private void pushString(String contents, String path) throws Exception {
- assertWithMessage("Failed to push file to device, content=%s path=%s", contents, path)
- .that(getDevice().pushString(contents, path)).isTrue();
- }
-
- private void assertFileContents(String expectedContents, String path) throws Exception {
- String actualContents = getDevice().pullFileContents(path);
- assertWithMessage("Failed to retrieve file=%s", path).that(actualContents).isNotNull();
- assertWithMessage("Mismatched file contents, path=%s", path)
- .that(actualContents).isEqualTo(expectedContents);
- }
-
- private void assertFileNotExists(String path) throws Exception {
- assertWithMessage("File shouldn't exist, path=%s", path)
- .that(getDevice().getFileEntry(path)).isNull();
- }
-
private static String apexDataDirDeSys(String apexName) {
return String.format("/data/misc/apexdata/%s", apexName);
}
- private static String apexDataDirDeUser(String apexName, int userId) {
- return String.format("/data/misc_de/%d/apexdata/%s", userId, apexName);
- }
-
private static String apexDataDirCe(String apexName, int userId) {
return String.format("/data/misc_ce/%d/apexdata/%s", userId, apexName);
}
- private static String apkDataDirDe(String apexName, int userId) {
- return String.format("/data/user_de/%d/%s", userId, apexName);
- }
-
- private List<String> getSnapshotDirectories(String baseDir) throws Exception {
- try {
- getDevice().enableAdbRoot();
- IFileEntry f = getDevice().getFileEntry(baseDir);
- if (f == null) {
- Log.d(TAG, "baseDir doesn't exist: " + baseDir);
- return Collections.EMPTY_LIST;
- }
- List<String> list = f.getChildren(false)
- .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?"))
- .map(entry -> entry.getFullPath())
- .collect(Collectors.toList());
- Log.d(TAG, "getSnapshotDirectories=" + list);
- return list;
- } finally {
- getDevice().disableAdbRoot();
- }
- }
-
- private void assertDirectoryIsEmpty(String path) throws Exception {
- try {
- getDevice().enableAdbRoot();
- IFileEntry file = getDevice().getFileEntry(path);
- assertWithMessage("Not a directory: " + path).that(file.isDirectory()).isTrue();
- assertWithMessage("Directory not empty: " + path)
- .that(file.getChildren(false)).isEmpty();
- } catch (DeviceNotAvailableException e) {
- fail("Can't access directory: " + path);
- } finally {
- getDevice().disableAdbRoot();
- }
- }
-
private void startActivity(String packageName) throws Exception {
String cmd = "am start -S -a android.intent.action.MAIN "
+ "-c android.intent.category.LAUNCHER " + packageName;
getDevice().executeShellCommand(cmd);
}
- private void crashProcess(String processName, int numberOfCrashes) throws Exception {
- String pid = "";
- String lastPid = "invalid";
- for (int i = 0; i < numberOfCrashes; ++i) {
- // This condition makes sure before we kill the process, the process is running AND
- // the last crash was finished.
- while ("".equals(pid) || lastPid.equals(pid)) {
- pid = getDevice().executeShellCommand("pidof " + processName);
- }
- getDevice().executeShellCommand("kill " + pid);
- lastPid = pid;
- }
- }
-
- private boolean isCheckpointSupported() throws Exception {
- try {
- runPhase("isCheckpointSupported");
- return true;
- } catch (AssertionError ignore) {
- return false;
- }
- }
-
/**
* True if this build has mainline modules installed.
*/
@@ -726,17 +266,17 @@ public class StagedRollbackTest extends BaseHostJUnit4Test {
}
}
- @FunctionalInterface
- private interface ExceptionalRunnable {
- void run() throws Exception;
- }
-
- private void runAsRoot(ExceptionalRunnable runnable) throws Exception {
- try {
- getDevice().enableAdbRoot();
- runnable.run();
- } finally {
- getDevice().disableAdbRoot();
+ private void crashProcess(String processName, int numberOfCrashes) throws Exception {
+ String pid = "";
+ String lastPid = "invalid";
+ for (int i = 0; i < numberOfCrashes; ++i) {
+ // This condition makes sure before we kill the process, the process is running AND
+ // the last crash was finished.
+ while ("".equals(pid) || lastPid.equals(pid)) {
+ pid = getDevice().executeShellCommand("pidof " + processName);
+ }
+ getDevice().executeShellCommand("kill " + pid);
+ lastPid = pid;
}
}
}
diff --git a/tests/SharedLibraryLoadingTest/Android.bp b/tests/SharedLibraryLoadingTest/Android.bp
new file mode 100644
index 000000000000..088278d6ee89
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // 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: "SharedLibraryLoadingTests",
+ libs: [
+ "tradefed",
+ "junit",
+ ],
+ test_suites: ["general-tests"],
+ data: [
+ ":SharedLibraryLoadingTests_StandardSharedLibrary",
+ ":SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ ":SharedLibraryLoadingTests_SharedLibraryClientTests",
+ ":SharedLibraryLoadingTests_Overlay",
+ ],
+}
diff --git a/tests/SharedLibraryLoadingTest/AndroidTest.xml b/tests/SharedLibraryLoadingTest/AndroidTest.xml
new file mode 100644
index 000000000000..947453d07bd9
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/AndroidTest.xml
@@ -0,0 +1,45 @@
+<?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 description="Host-driven test module config for SharedLibraryHostTests">
+ <option name="test-tag" value="SharedLibraryLoadingTests" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <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" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk->/product/app/SharedLibraryLoadingTests_SharedLibraryLoadedAfter.apk" />
+ <option name="push"
+ value="SharedLibraryLoadingTests_Overlay.apk->/product/overlay/SharedLibraryLoadingTests_Overlay.apk" />
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="SharedLibraryLoadingTests_SharedLibraryClientTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.sharedlibloadingtest.client" />
+ </test>
+</configuration> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/OWNERS b/tests/SharedLibraryLoadingTest/OWNERS
new file mode 100644
index 000000000000..d7b4569b6bc0
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/OWNERS
@@ -0,0 +1,2 @@
+stenning@google.com
+
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
new file mode 100644
index 000000000000..b2f4e8925b58
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+ // 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: "SharedLibraryLoadingTests_Overlay",
+ platform_apis: true,
+ certificate: "platform",
+ aaptflags: ["--no-resource-removal"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml
new file mode 100644
index 000000000000..ae2784ca0904
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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="com.android.sharedlibloadingtest.overlay">
+ <application android:hasCode="false" />
+ <overlay android:targetPackage="android"
+ android:isStatic="true"
+ android:priority="1"/>
+</manifest> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
new file mode 100644
index 000000000000..15da3dbafd84
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/Overlay/res/values/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2020 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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-array name="config_sharedLibrariesLoadedAfterApp" translatable="false">
+ <item>com.android.sharedlibloadingtest.shared_library_after</item>
+ </string-array>
+</resources> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
new file mode 100644
index 000000000000..0d204979cb92
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "SharedLibraryLoadingTests_SharedLibraryClientTests",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ libs: [
+ "SharedLibraryLoadingTests_StandardSharedLibrary",
+ "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ "android.test.base",
+ ],
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "androidx.test.core",
+ "testng",
+ ],
+ platform_apis: true,
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
new file mode 100644
index 000000000000..e3a9b9bca78a
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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="com.android.sharedlibloadingtest.client">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library"/>
+ <uses-library android:name="com.android.sharedlibloadingtest.shared_library_after"/>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.sharedlibloadingtest.client" />
+</manifest> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
new file mode 100644
index 000000000000..5e0544eb8696
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <string name="identical_resource_key">client value</string>
+</resources> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
new file mode 100644
index 000000000000..e48fb833bd76
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/ClientClass.java
@@ -0,0 +1,24 @@
+/*
+ * 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.sharedlibloadingtest;
+
+public class ClientClass {
+ @Override
+ public String toString() {
+ return "Client Code";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
new file mode 100644
index 000000000000..4c771557e119
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -0,0 +1,24 @@
+/*
+ * 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.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Client's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
new file mode 100644
index 000000000000..86aa6a1a0901
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -0,0 +1,24 @@
+/*
+ * 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.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Client's Version B";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
new file mode 100644
index 000000000000..43bcb1ad7d27
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryClientTests/src/com/android/sharedlibloadingtest/client/SharedLibraryLoadingOrderTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.sharedlibloadingtest.client;
+
+import static org.testng.Assert.assertEquals;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.util.Preconditions;
+import com.android.sharedlibloadingtest.ClientClass;
+import com.android.sharedlibloadingtest.DuplicateClassA;
+import com.android.sharedlibloadingtest.DuplicateClassB;
+import com.android.sharedlibloadingtest.SharedClassAfter;
+import com.android.sharedlibloadingtest.StdSharedClass;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class SharedLibraryLoadingOrderTest {
+
+ @Test
+ public void testLoadingOfStdShareLibsShouldBeFirst() {
+ Preconditions.checkArgument(!getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library"));
+ DuplicateClassA clazz = new DuplicateClassA();
+ assertEquals(clazz.toString(), "Standard Shared Lib's Version");
+
+ StdSharedClass stdSharedClass = new StdSharedClass();
+ assertEquals(stdSharedClass.toString(), "Nothing Special Lib");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfShareLibsIsAfter() {
+ Preconditions.checkArgument(getLibsLoadedAfter()
+ .contains("com.android.sharedlibloadingtest.shared_library_after"));
+ DuplicateClassB clazz = new DuplicateClassB();
+ assertEquals(clazz.toString(), "Client's Version B");
+
+ SharedClassAfter stdSharedClass = new SharedClassAfter();
+ assertEquals(stdSharedClass.toString(), "Also Nothing Special");
+
+ ClientClass clientCode = new ClientClass();
+ assertEquals(clientCode.toString(), "Client Code");
+ }
+
+ @Test
+ public void testLoadingOfResource() {
+ // aapt compiler gives each lib their own namespace so this test just confirming
+ // the resources can be loaded from the same context object
+ Context context = ApplicationProvider.getApplicationContext();
+ String clientString = context.getResources().getString(R.string.identical_resource_key);
+ assertEquals(clientString, "client value");
+ assertEquals(StdSharedClass.getResString(context), "std lib value");
+ assertEquals(SharedClassAfter.getResString(context), "loaded after value");
+
+ }
+
+ private HashSet<String> getLibsLoadedAfter() {
+ Resources systemR = Resources.getSystem();
+ HashSet<String> libsToLoadAfter = new HashSet<>();
+ Collections.addAll(libsToLoadAfter, systemR.getStringArray(
+ com.android.internal.R.array.config_sharedLibrariesLoadedAfterApp));
+ return libsToLoadAfter;
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
new file mode 100644
index 000000000000..db9b3edfe6a2
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ // 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: "SharedLibraryLoadingTests_SharedLibraryLoadedAfter",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml
new file mode 100644
index 000000000000..efedfcfeb515
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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="com.android.sharedlibloadingtest.shared_library_after">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library_after" />
+ </application>
+</manifest> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
new file mode 100644
index 000000000000..4525944b060c
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <string name="identical_resource_key">loaded after value</string>
+</resources> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
new file mode 100644
index 000000000000..1e1f5aab5993
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/DuplicateClassB.java
@@ -0,0 +1,24 @@
+/*
+ * 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.sharedlibloadingtest;
+
+public class DuplicateClassB {
+ @Override
+ public String toString() {
+ return "Loaded After Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java
new file mode 100644
index 000000000000..9e5b40fc38d8
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/SharedLibraryLoadedAfter/src/com/android/sharedlibloadingtest/SharedClassAfter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library_after.R;
+
+public class SharedClassAfter {
+ @Override
+ public String toString() {
+ return "Also Nothing Special";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
new file mode 100644
index 000000000000..50456b0439c2
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/Android.bp
@@ -0,0 +1,30 @@
+// 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 {
+ // 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: "SharedLibraryLoadingTests_StandardSharedLibrary",
+ srcs: ["**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "current",
+ aaptflags: ["--shared-lib"],
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml
new file mode 100644
index 000000000000..f1a079feb316
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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="com.android.sharedlibloadingtest.shared_library">
+ <application>
+ <library android:name="com.android.sharedlibloadingtest.shared_library" />
+ </application>
+</manifest> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
new file mode 100644
index 000000000000..941351aaea62
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/res/values/values.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<resources>
+ <string name="identical_resource_key">std lib value</string>
+</resources> \ No newline at end of file
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
new file mode 100644
index 000000000000..a3874aa3ad96
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/DuplicateClassA.java
@@ -0,0 +1,24 @@
+/*
+ * 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.sharedlibloadingtest;
+
+public class DuplicateClassA {
+ @Override
+ public String toString() {
+ return "Standard Shared Lib's Version";
+ }
+}
diff --git a/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java
new file mode 100644
index 000000000000..429d65ca2439
--- /dev/null
+++ b/tests/SharedLibraryLoadingTest/test-apps/StandardSharedLibrary/src/com/android/sharedlibloadingtest/StdSharedClass.java
@@ -0,0 +1,32 @@
+/*
+ * 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.sharedlibloadingtest;
+
+import android.content.Context;
+
+import com.android.sharedlibloadingtest.shared_library.R;
+
+public class StdSharedClass {
+ @Override
+ public String toString() {
+ return "Nothing Special Lib";
+ }
+
+ public static String getResString(Context context) {
+ return context.getResources().getString(R.string.identical_resource_key);
+ }
+}
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index c30d76137f76..21256d8c9d0b 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -20,17 +20,20 @@
<uses-sdk android:minSdkVersion="30"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:label="SilkFX"
android:theme="@android:style/Theme.Material">
<activity android:name=".Main"
android:label="SilkFX Demos"
+ android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
@@ -41,13 +44,16 @@
<activity android:name=".materials.GlassActivity"
android:label="Glass Examples"
- android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".materials.BackgroundBlurActivity"
+ android:theme="@style/Theme.BackgroundBlurTheme"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/tests/SilkFX/res/drawable/background_blur_drawable.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml
new file mode 100644
index 000000000000..173ca99bdfdf
--- /dev/null
+++ b/tests/SilkFX/res/drawable/background_blur_drawable.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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#20FFFFFF"/>
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
new file mode 100644
index 000000000000..bd8942d46383
--- /dev/null
+++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml
new file mode 100644
index 000000000000..f13c0883cb01
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_background_blur.xml
@@ -0,0 +1,173 @@
+<?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"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/background"
+ android:layout_width="390dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="15dp"
+ android:orientation="vertical"
+ tools:context=".materials.BackgroundBlurActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:textColor="#ffffffff"
+ android:text="Hello blurry world!"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background blur"/>
+
+ <SeekBar
+ android:id="@+id/set_background_blur"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/background_blur_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background alpha"/>
+
+ <SeekBar
+ android:id="@+id/set_background_alpha"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/background_alpha"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Blur behind"/>
+
+ <SeekBar
+ android:id="@+id/set_blur_behind"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/blur_behind_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Dim amount"/>
+
+ <SeekBar
+ android:id="@+id/set_dim_amount"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/dim_amount"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="5dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button
+ android:id="@+id/toggle_blur_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Disable blur"
+ android:onClick="toggleForceBlurDisabled"/>
+
+ <Button
+ android:id="@+id/toggle_battery_saving_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TODO"
+ android:onClick="toggleBatterySavingMode"/>
+ </LinearLayout>
+ <requestFocus/>
+
+</LinearLayout>
diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml
new file mode 100644
index 000000000000..66edbb5c9382
--- /dev/null
+++ b/tests/SilkFX/res/values/style.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.
+ -->
+<!-- Styles for immersive actions UI. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBlurBehindEnabled">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowElevation">0dp</item>
+ <item name="buttonStyle">@style/AppTheme.Button</item>
+ <item name="colorAccent">#bbffffff</item>
+ </style>
+ <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
+ <item name="android:textColor">#ffffffff</item>
+ </style>
+
+</resources>
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 9ed8d2f5edf7..7132ae8772ea 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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.
@@ -30,6 +30,7 @@ import com.android.test.silkfx.app.EXTRA_LAYOUT
import com.android.test.silkfx.app.EXTRA_TITLE
import com.android.test.silkfx.hdr.GlowActivity
import com.android.test.silkfx.materials.GlassActivity
+import com.android.test.silkfx.materials.BackgroundBlurActivity
import kotlin.reflect.KClass
class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -51,7 +52,8 @@ private val AllDemos = listOf(
Demo("Blingy Notifications", R.layout.bling_notifications)
)),
DemoGroup("Materials", listOf(
- Demo("Glass", GlassActivity::class)
+ Demo("Glass", GlassActivity::class),
+ Demo("Background Blur", BackgroundBlurActivity::class)
))
)
@@ -126,4 +128,4 @@ class Main : Activity() {
AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
}
-} \ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
new file mode 100644
index 000000000000..9d17d38d4298
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.silkfx.materials
+
+import android.app.Activity
+import android.content.Intent
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.provider.Settings
+import android.util.TypedValue
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable
+import android.widget.LinearLayout
+import android.widget.Button
+
+import android.view.ViewRootImpl
+
+class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener {
+ var mBackgroundDrawable = PaintDrawable(Color.WHITE)
+ var mBackgroundBlurRadius = 50
+ var mAlphaWithBlur = 0.2f
+ var mAlphaNoBlur = 0.5f
+
+ var mBlurBehindRadius = 10
+ var mDimAmountWithBlur = 0.2f
+ var mDimAmountNoBlur = 0.2f
+
+ var mBlurForceDisabled = false
+ var mBatterySavingModeOn = false
+
+ lateinit var blurBackgroundSeekBar: SeekBar
+ lateinit var backgroundAlphaSeekBar : SeekBar
+ lateinit var blurBehindSeekBar : SeekBar
+ lateinit var dimAmountSeekBar : SeekBar
+
+ val blurEnabledListener = { enabled : Boolean ->
+ blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius)
+ blurBehindSeekBar.setProgress(mBlurBehindRadius)
+
+ if (enabled) {
+ setBackgroundBlur(mBackgroundBlurRadius)
+ setBackgroundColorAlpha(mAlphaWithBlur)
+
+ setBlurBehind(mBlurBehindRadius)
+ setDimAmount(mDimAmountWithBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt())
+ } else {
+ setBackgroundColorAlpha(mAlphaNoBlur)
+ setDimAmount(mDimAmountNoBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_background_blur)
+
+ window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
+ window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+ mBackgroundDrawable.setCornerRadius(30f)
+ window.setBackgroundDrawable(mBackgroundDrawable)
+
+ mBatterySavingModeOn =
+ Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1
+ setBatterySavingModeOn(mBatterySavingModeOn)
+
+ blurBackgroundSeekBar = requireViewById(R.id.set_background_blur)
+ backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
+ blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
+ dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
+
+ arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+ .forEach {
+ it.setOnSeekBarChangeListener(this)
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ when (seekBar) {
+ blurBackgroundSeekBar -> setBackgroundBlur(progress)
+ backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f)
+ blurBehindSeekBar -> setBlurBehind(progress)
+ dimAmountSeekBar -> setDimAmount(progress / 100.0f)
+ else -> throw IllegalArgumentException("Unknown seek bar")
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+ fun setBlurDisabled(disabled: Boolean) {
+ mBlurForceDisabled = disabled
+ Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS,
+ if (mBlurForceDisabled) 1 else 0)
+ (findViewById(R.id.toggle_blur_enabled) as Button)
+ .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs")
+ }
+
+ fun toggleForceBlurDisabled(v: View) {
+ setBlurDisabled(!mBlurForceDisabled)
+ }
+
+ fun setBackgroundBlur(radius: Int) {
+ mBackgroundBlurRadius = radius
+ (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString())
+ window.setBackgroundBlurRadius(mBackgroundBlurRadius)
+ }
+
+ fun setBlurBehind(radius: Int) {
+ mBlurBehindRadius = radius
+ (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString())
+ window.getAttributes().setBlurBehindRadius(mBlurBehindRadius)
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setDimAmount(amount: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mDimAmountWithBlur = amount
+ } else {
+ mDimAmountNoBlur = amount
+ }
+ (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount))
+ window.getAttributes().dimAmount = amount
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setBatterySavingModeOn(on: Boolean) {
+ mBatterySavingModeOn = on
+ Settings.Global.putInt(getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, if (on) 1 else 0)
+ (findViewById(R.id.toggle_battery_saving_mode) as Button).setText(
+ if (on) "Exit low power mode" else "Enter low power mode")
+ }
+
+ fun toggleBatterySavingMode(v: View) {
+ setBatterySavingModeOn(!mBatterySavingModeOn)
+ }
+
+ fun setBackgroundColorAlpha(alpha: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mAlphaWithBlur = alpha
+ } else {
+ mAlphaNoBlur = alpha
+ }
+ (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha))
+ mBackgroundDrawable.setAlpha((alpha * 255f).toInt())
+ getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes())
+ }
+}
diff --git a/tests/SoundTriggerTestApp/OWNERS b/tests/SoundTriggerTestApp/OWNERS
index 816bc6bba639..9db19a37812b 100644
--- a/tests/SoundTriggerTestApp/OWNERS
+++ b/tests/SoundTriggerTestApp/OWNERS
@@ -1 +1,2 @@
include /core/java/android/media/soundtrigger/OWNERS
+mdooley@google.com
diff --git a/tests/SoundTriggerTestApp/res/layout/main.xml b/tests/SoundTriggerTestApp/res/layout/main.xml
index 2c6c8d7cae20..1381c0a43c30 100644
--- a/tests/SoundTriggerTestApp/res/layout/main.xml
+++ b/tests/SoundTriggerTestApp/res/layout/main.xml
@@ -73,6 +73,14 @@
android:text="@string/play_trigger"
android:onClick="onPlayTriggerButtonClicked"
android:padding="20dp" />
+
+ <Button
+ android:id="@+id/get_state_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/get_model_state"
+ android:onClick="onGetModelStateButtonClicked"
+ android:padding="20dp" />
</LinearLayout>
<LinearLayout
diff --git a/tests/SoundTriggerTestApp/res/values/strings.xml b/tests/SoundTriggerTestApp/res/values/strings.xml
index c48b64884c5e..adb0fcf8e185 100644
--- a/tests/SoundTriggerTestApp/res/values/strings.xml
+++ b/tests/SoundTriggerTestApp/res/values/strings.xml
@@ -22,6 +22,7 @@
<string name="start_recog">Start</string>
<string name="stop_recog">Stop</string>
<string name="play_trigger">Play Trigger Audio</string>
+ <string name="get_model_state">Get State</string>
<string name="capture">Capture Audio</string>
<string name="stop_capture">Stop Capturing Audio</string>
<string name="play_capture">Play Captured Audio</string>
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
index c3c4cf556986..103d516e5967 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestActivity.java
@@ -97,13 +97,8 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
setVolumeControlStream(AudioManager.STREAM_MUSIC);
- // Make sure that the service is started, so even if our activity goes down, we'll still
- // have a request for it to run.
- startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
-
- // Bind to SoundTriggerTestService.
- Intent intent = new Intent(this, SoundTriggerTestService.class);
- bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+ AUDIO_PERMISSIONS_REQUEST);
}
@Override
@@ -257,14 +252,26 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
}
}
+ public synchronized void onGetModelStateButtonClicked(View v) {
+ if (mService == null) {
+ Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService");
+ } else {
+ mService.getModelState(mSelectedModelUuid);
+ }
+ }
+
public synchronized void onCaptureAudioCheckboxClicked(View v) {
// See if we have the right permissions
- if (!mService.hasMicrophonePermission()) {
- requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
- AUDIO_PERMISSIONS_REQUEST);
- return;
+ if (mService == null) {
+ Log.e(TAG, "Can't set capture audio: not bound to SoundTriggerTestService");
} else {
- mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ if (!mService.hasMicrophonePermission()) {
+ requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
+ AUDIO_PERMISSIONS_REQUEST);
+ return;
+ } else {
+ mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
+ }
}
}
@@ -275,8 +282,15 @@ public class SoundTriggerTestActivity extends Activity implements SoundTriggerTe
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
// Make sure that the check box is set to false.
mCaptureAudioCheckBox.setChecked(false);
+ } else {
+ // After granted Record_Audio permission, start and bind the service.
+ // so we can run that sound trigger capability,
+ // even if our activity goes down, we'll still have a request for it to run.
+ startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
+ // Bind to SoundTriggerTestService.
+ Intent intent = new Intent(this, SoundTriggerTestService.class);
+ bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
- mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
}
}
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index 380e29984c63..6d4ffcff7d45 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
@@ -46,6 +48,7 @@ import java.util.Properties;
import java.util.Random;
import java.util.UUID;
+
public class SoundTriggerTestService extends Service {
private static final String TAG = "SoundTriggerTestSrv";
private static final String INTENT_ACTION = "com.android.intent.action.MANAGE_SOUND_TRIGGER";
@@ -57,6 +60,8 @@ public class SoundTriggerTestService extends Service {
private Random mRandom;
private UserActivity mUserActivity;
+ private static int captureCount;
+
public interface UserActivity {
void addModel(UUID modelUuid, String state);
void setModelState(UUID modelUuid, String state);
@@ -131,6 +136,8 @@ public class SoundTriggerTestService extends Service {
} else if (command.equals("set_capture_timeout")) {
setCaptureAudioTimeout(getModelUuidFromIntent(intent),
intent.getIntExtra("timeout", 5000));
+ } else if (command.equals("get_model_state")) {
+ getModelState(getModelUuidFromIntent(intent));
} else {
Log.e(TAG, "Unknown command '" + command + "'");
}
@@ -432,6 +439,17 @@ public class SoundTriggerTestService extends Service {
return modelInfo != null && modelInfo.captureAudioTrack != null;
}
+ public synchronized void getModelState(UUID modelUuid) {
+ ModelInfo modelInfo = mModelInfoMap.get(modelUuid);
+ if (modelInfo == null) {
+ postError("Could not find model for: " + modelUuid.toString());
+ return;
+ }
+ int status = mSoundTriggerUtil.getModelState(modelUuid);
+ postMessage("GetModelState for: " + modelInfo.name + " returns: "
+ + status);
+ }
+
private void loadModelsInDataDir() {
// Load all the models in the data dir.
boolean loadedModel = false;
@@ -527,18 +545,29 @@ public class SoundTriggerTestService extends Service {
}
}
+
private class CaptureAudioRecorder implements Runnable {
private final ModelInfo mModelInfo;
+
+ // EventPayload and RecognitionEvent are equivalant. Only one will be non-null.
private final SoundTriggerDetector.EventPayload mEvent;
+ private final RecognitionEvent mRecognitionEvent;
public CaptureAudioRecorder(ModelInfo modelInfo, SoundTriggerDetector.EventPayload event) {
mModelInfo = modelInfo;
mEvent = event;
+ mRecognitionEvent = null;
+ }
+
+ public CaptureAudioRecorder(ModelInfo modelInfo, RecognitionEvent event) {
+ mModelInfo = modelInfo;
+ mEvent = null;
+ mRecognitionEvent = event;
}
@Override
public void run() {
- AudioFormat format = mEvent.getCaptureAudioFormat();
+ AudioFormat format = getAudioFormat();
if (format == null) {
postErrorToast("No audio format in recognition event.");
return;
@@ -600,18 +629,21 @@ public class SoundTriggerTestService extends Service {
}
audioRecord = new AudioRecord(attributes, format, bytesRequired,
- mEvent.getCaptureSession());
+ getCaptureSession());
byte[] buffer = new byte[bytesRequired];
// Create a file so we can save the output data there for analysis later.
FileOutputStream fos = null;
try {
- fos = new FileOutputStream( new File(
- getFilesDir() + File.separator
- + mModelInfo.name.replace(' ', '_')
- + "_capture_" + format.getChannelCount() + "ch_"
- + format.getSampleRate() + "hz_" + encoding + ".pcm"));
+ File file = new File(
+ getFilesDir() + File.separator
+ + mModelInfo.name.replace(' ', '_')
+ + "_capture_" + format.getChannelCount() + "ch_"
+ + format.getSampleRate() + "hz_" + encoding
+ + "_" + (++captureCount) + ".pcm");
+ Log.i(TAG, "Writing audio to: " + file);
+ fos = new FileOutputStream(file);
} catch (IOException e) {
Log.e(TAG, "Failed to open output for saving PCM data", e);
postErrorToast("Failed to open output for saving PCM data: "
@@ -635,6 +667,10 @@ public class SoundTriggerTestService extends Service {
bytesRequired -= bytesRead;
}
audioRecord.stop();
+ if (fos != null) {
+ fos.flush();
+ fos.close();
+ }
} catch (Exception e) {
Log.e(TAG, "Error recording trigger audio", e);
postErrorToast("Error recording trigger audio: " + e.getMessage());
@@ -651,6 +687,26 @@ public class SoundTriggerTestService extends Service {
setModelState(mModelInfo, "Recording finished");
}
}
+
+ private AudioFormat getAudioFormat() {
+ if (mEvent != null) {
+ return mEvent.getCaptureAudioFormat();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureFormat;
+ }
+ return null;
+ }
+
+ private int getCaptureSession() {
+ if (mEvent != null) {
+ return mEvent.getCaptureSession();
+ }
+ if (mRecognitionEvent != null) {
+ return mRecognitionEvent.captureSession;
+ }
+ return 0;
+ }
}
// Implementation of SoundTriggerDetector.Callback.
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
index cfe8c855ac81..996a78f2e42a 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerUtil.java
@@ -18,6 +18,8 @@ package com.android.test.soundtrigger;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
import android.media.soundtrigger.SoundTriggerDetector;
import android.media.soundtrigger.SoundTriggerManager;
import android.os.RemoteException;
@@ -27,6 +29,7 @@ import android.util.Log;
import com.android.internal.app.ISoundTriggerService;
+import java.lang.reflect.Method;
import java.lang.RuntimeException;
import java.util.UUID;
@@ -50,13 +53,31 @@ public class SoundTriggerUtil {
* The sound model must contain a valid UUID.
*
* @param soundModel The sound model to add/update.
+ * @return The true if the model was loaded successfully, false otherwise.
*/
public boolean addOrUpdateSoundModel(SoundTriggerManager.Model soundModel) {
if (soundModel == null) {
throw new RuntimeException("Bad sound model");
}
mSoundTriggerManager.updateModel(soundModel);
- return true;
+ // TODO: call loadSoundModel in the soundtrigger manager updateModel method
+ // instead of here. It is needed to keep soundtrigger manager internal
+ // state consistent.
+ return mSoundTriggerManager
+ .loadSoundModel(getGenericSoundModel(soundModel)) == 0;
+ }
+
+ private GenericSoundModel getGenericSoundModel(
+ SoundTriggerManager.Model soundModel) {
+ try {
+ Method method = SoundTriggerManager.Model.class
+ .getDeclaredMethod("getGenericSoundModel");
+ method.setAccessible(true);
+ return (GenericSoundModel) method.invoke(soundModel);
+ } catch (ReflectiveOperationException e) {
+ Log.e(TAG, "Failed to getGenericSoundModel: " + soundModel, e);
+ return null;
+ }
}
/**
@@ -92,6 +113,16 @@ public class SoundTriggerUtil {
return true;
}
+ /**
+ * Get the current model state
+ *
+ * @param modelId The model ID to look-up the sound model for.
+ * @return 0 if the call succeeds, or an error code if it fails.
+ */
+ public int getModelState(UUID modelId) {
+ return mSoundTriggerManager.getModelState(modelId);
+ }
+
public SoundTriggerDetector createSoundTriggerDetector(UUID modelId,
SoundTriggerDetector.Callback callback) {
return mSoundTriggerManager.createSoundTriggerDetector(modelId, callback, null);
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index cac14a72a706..cce0dde9e6b9 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -31,12 +31,14 @@ android_test_helper_app {
],
test_suites: ["general-tests"],
java_resources: [
+ ":apex.apexd_test_classpath",
":com.android.apex.apkrollback.test_v2",
":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
":test.rebootless_apex_v1",
":test.rebootless_apex_v2",
],
+ platform_apis: true,
}
java_test_host {
@@ -53,11 +55,14 @@ java_test_host {
"cts-install-lib-host",
],
data: [
+ ":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
- ":com.android.apex.cts.shim.v2_prebuilt",
+ ":StagedInstallTestApexV2",
":StagedInstallTestApexV2_WrongSha",
":TestAppAv1",
":test.rebootless_apex_v1",
+ ":test.rebootless_apex_v2",
+ ":test_com.android.server",
],
test_suites: ["general-tests"],
test_config: "StagedInstallInternalTest.xml",
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 4684f0182d03..7490d3f2b50c 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -22,23 +22,37 @@ import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.Manifest;
+import android.content.pm.ApexStagedEvent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManagerNative;
+import android.content.pm.IStagedApexObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.StagedApexInfo;
+import android.os.IBinder;
+import android.os.ServiceManager;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.cts.install.lib.Install;
import com.android.cts.install.lib.InstallUtils;
import com.android.cts.install.lib.TestApp;
+import com.android.cts.install.lib.Uninstall;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -61,6 +75,13 @@ public class StagedInstallInternalTest {
"ApexV2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true,
"com.android.apex.cts.shim.v2.apex");
+ private static final String TEST_APEX_PACKAGE_NAME = "com.android.apex.test_package";
+ private static final TestApp TEST_APEX_CLASSPATH = new TestApp("TestApex",
+ TEST_APEX_PACKAGE_NAME, 1, /*isApex=*/true,
+ "apex.apexd_test_classpath.apex");
+
+ private static final String TEST_APEX_SYSTEM_SERVER_PACKAGE_NAME = "test_com.android.server";
+
private File mTestStateFile = new File(
InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(),
"stagedinstall_state");
@@ -90,6 +111,7 @@ public class StagedInstallInternalTest {
@Test
public void cleanUp() throws Exception {
Files.deleteIfExists(mTestStateFile.toPath());
+ Uninstall.packages(TestApp.A, TestApp.B);
}
@Test
@@ -139,8 +161,18 @@ public class StagedInstallInternalTest {
@Test
public void testStagedSessionShouldCleanUpOnVerificationFailure() throws Exception {
+ // APEX verification
InstallUtils.commitExpectingFailure(AssertionError.class, "apexd verification failed",
Install.single(APEX_WRONG_SHA_V2).setStaged());
+ InstallUtils.commitExpectingFailure(AssertionError.class, "apexd verification failed",
+ Install.multi(APEX_WRONG_SHA_V2, TestApp.A1).setStaged());
+ // APK verification
+ Install.single(TestApp.A2).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+ InstallUtils.commitExpectingFailure(AssertionError.class, "Downgrade detected",
+ Install.single(TestApp.A1).setStaged());
+ InstallUtils.commitExpectingFailure(AssertionError.class, "Downgrade detected",
+ Install.multi(TestApp.A1, TestApp.B1).setStaged());
}
@Test
@@ -158,6 +190,12 @@ public class StagedInstallInternalTest {
}
@Test
+ public void testStagedSessionShouldCleanUpOnOnSuccessMultiPackage_Commit() throws Exception {
+ int sessionId = Install.multi(TestApp.A1, TestApp.Apex2).setStaged().commit();
+ storeSessionId(sessionId);
+ }
+
+ @Test
public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
InstallUtils.commitExpectingFailure(AssertionError.class, "INSTALL_FAILED_INVALID_APK",
Install.single(TestApp.AIncompleteSplit).setStaged());
@@ -267,11 +305,14 @@ public class StagedInstallInternalTest {
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -279,11 +320,14 @@ public class StagedInstallInternalTest {
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -291,11 +335,14 @@ public class StagedInstallInternalTest {
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false).setStaged());
}
@Test
@@ -303,11 +350,14 @@ public class StagedInstallInternalTest {
assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1);
TestApp apex = new TestApp("apex", "test.apex.rebootless", 2,
/* isApex= */ true, "test.rebootless_apex_v2.apex");
+ String expectedFailMessage = "Update of APEX package test.apex.rebootless is not allowed "
+ + "for com.android.tests.stagedinstallinternal";
InstallUtils.commitExpectingFailure(
- AssertionError.class,
- "Update of APEX package test.apex.rebootless is not allowed "
- + "for com.android.tests.stagedinstallinternal",
+ AssertionError.class, expectedFailMessage,
Install.single(apex).setBypassAllowedApexUpdateCheck(false));
+ InstallUtils.commitExpectingFailure(
+ AssertionError.class, expectedFailMessage,
+ Install.multi(apex).setBypassAllowedApexUpdateCheck(false));
}
@Test
@@ -401,9 +451,93 @@ public class StagedInstallInternalTest {
AssertionError.class,
"Staged session " + sessionId + " already contains " + SHIM_APEX_PACKAGE_NAME,
Install.single(APEX_V2));
+ }
+
+ @Test
+ public void testGetStagedModuleNames() throws Exception {
+ // Before staging a session
+ String[] result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(0);
+ // Stage an apex
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(1);
+ assertThat(result).isEqualTo(new String[]{SHIM_APEX_PACKAGE_NAME});
+ // Abandon the session
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
+ result = getPackageManagerNative().getStagedApexModuleNames();
+ assertThat(result).hasLength(0);
+ }
+ @Test
+ public void testGetStagedApexInfo() throws Exception {
+ // Ask for non-existing module
+ StagedApexInfo result = getPackageManagerNative().getStagedApexInfo("not found");
+ assertThat(result).isNull();
+ // Stage an apex
+ int sessionId = Install.single(TEST_APEX_CLASSPATH).setStaged().commit();
+ // Query proper module name
+ result = getPackageManagerNative().getStagedApexInfo(TEST_APEX_PACKAGE_NAME);
+ assertThat(result.moduleName).isEqualTo(TEST_APEX_PACKAGE_NAME);
+ assertThat(result.hasClassPathJars).isTrue();
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
}
+ @Test
+ public void testGetAppInfo_flagTestOnlyIsSet() throws Exception {
+ final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ final ApplicationInfo info = pm.getApplicationInfo(TEST_APEX_SYSTEM_SERVER_PACKAGE_NAME,
+ ApplicationInfoFlags.of(PackageManager.MATCH_APEX));
+ assertThat(info).isNotNull();
+ assertThat((info.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0).isTrue();
+ }
+
+ public static class MockStagedApexObserver extends IStagedApexObserver.Stub {
+ @Override
+ public void onApexStaged(ApexStagedEvent event) {
+ assertThat(event).isNotNull();
+ }
+ }
+
+ @Test
+ public void testStagedApexObserver() throws Exception {
+ MockStagedApexObserver realObserver = new MockStagedApexObserver();
+ IStagedApexObserver observer = spy(realObserver);
+ assertThat(observer).isNotNull();
+ getPackageManagerNative().registerStagedApexObserver(observer);
+
+ // Stage an apex and verify observer was called
+ int sessionId = Install.single(APEX_V2).setStaged().commit();
+ ArgumentCaptor<ApexStagedEvent> captor = ArgumentCaptor.forClass(ApexStagedEvent.class);
+ verify(observer, timeout(5000)).onApexStaged(captor.capture());
+ assertThat(captor.getValue().stagedApexModuleNames).isEqualTo(
+ new String[] {SHIM_APEX_PACKAGE_NAME});
+
+ // Abandon and verify observer is called
+ Mockito.clearInvocations(observer);
+ InstallUtils.openPackageInstallerSession(sessionId).abandon();
+ verify(observer, timeout(5000)).onApexStaged(captor.capture());
+ assertThat(captor.getValue().stagedApexModuleNames).hasLength(0);
+ }
+
+ @Test
+ public void testRebootlessDowngrade() throws Exception {
+ final String packageName = "test.apex.rebootless";
+ assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(2);
+ TestApp apex1 = new TestApp("TestRebootlessApexV1", packageName, 1,
+ /* isApex= */ true, "test.rebootless_apex_v1.apex");
+ InstallUtils.commitExpectingFailure(AssertionError.class,
+ "INSTALL_FAILED_VERSION_DOWNGRADE", Install.single(apex1));
+ Install.single(apex1).setRequestDowngrade().commit();
+ assertThat(InstallUtils.getInstalledVersion(packageName)).isEqualTo(1);
+ }
+
+ private IPackageManagerNative getPackageManagerNative() {
+ IBinder binder = ServiceManager.waitForService("package_native");
+ assertThat(binder).isNotNull();
+ return IPackageManagerNative.Stub.asInterface(binder);
+ }
private static void assertSessionApplied(int sessionId) {
assertSessionState(sessionId, (session) -> {
assertThat(session.isStagedSessionApplied()).isTrue();
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 5021009f65ae..f60b4d6aad1e 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -61,6 +61,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
private static final String APEX_WRONG_SHA = "com.android.apex.cts.shim.v2_wrong_sha.apex";
private static final String APK_A = "TestAppAv1.apk";
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 TEST_VENDOR_APEX_ALLOW_LIST =
"/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml";
@@ -91,7 +93,7 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
"/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
"/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex",
- "/system/apex/test.rebootless_apex_v1.apex",
+ "/system/apex/test.rebootless_apex_v*.apex",
"/data/apex/active/test.apex.rebootless*.apex",
TEST_VENDOR_APEX_ALLOW_LIST);
}
@@ -111,6 +113,10 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
* @param files the paths of files which might contain wildcards
*/
private void deleteFiles(String... files) throws Exception {
+ if (!getDevice().isAdbRoot()) {
+ getDevice().enableAdbRoot();
+ }
+
boolean found = false;
for (String file : files) {
CommandResult result = getDevice().executeShellV2Command("ls " + file);
@@ -121,9 +127,6 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
if (found) {
- if (!getDevice().isAdbRoot()) {
- getDevice().enableAdbRoot();
- }
getDevice().remountSystemWritable();
for (String file : files) {
getDevice().executeShellCommand("rm -rf " + file);
@@ -300,6 +303,18 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
}
@Test
+ @LargeTest
+ public void testStagedSessionShouldCleanUpOnOnSuccessMultiPackage() throws Exception {
+ List<String> before = getStagingDirectories();
+ runPhase("testStagedSessionShouldCleanUpOnOnSuccessMultiPackage_Commit");
+ assertThat(getStagingDirectories()).isNotEqualTo(before);
+ getDevice().reboot();
+ runPhase("testStagedSessionShouldCleanUpOnOnSuccess_Verify");
+ List<String> after = getStagingDirectories();
+ assertThat(after).isEqualTo(before);
+ }
+
+ @Test
public void testStagedInstallationShouldCleanUpOnValidationFailure() throws Exception {
List<String> before = getStagingDirectories();
runPhase("testStagedInstallationShouldCleanUpOnValidationFailure");
@@ -478,6 +493,53 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test {
runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails");
}
+ @Test
+ public void testGetStagedModuleNames() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ runPhase("testGetStagedModuleNames");
+ }
+
+ @Test
+ @LargeTest
+ public void testGetStagedApexInfo() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestApex(APEXD_TEST_APEX);
+ getDevice().reboot();
+
+ runPhase("testGetStagedApexInfo");
+ }
+
+ @Test
+ @LargeTest
+ public void testGetAppInfo_flagTestOnlyIsSet() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ pushTestApex(FAKE_APEX_SYSTEM_SERVER_APEX);
+ getDevice().reboot();
+
+ runPhase("testGetAppInfo_flagTestOnlyIsSet");
+ }
+
+ @Test
+ public void testStagedApexObserver() throws Exception {
+ assumeTrue("Device does not support updating APEX",
+ mHostUtils.isApexUpdateSupported());
+
+ runPhase("testStagedApexObserver");
+ }
+
+ @Test
+ public void testRebootlessDowngrade() throws Exception {
+ pushTestApex("test.rebootless_apex_v2.apex");
+ getDevice().reboot();
+ runPhase("testRebootlessDowngrade");
+ }
+
private List<String> getStagingDirectories() throws DeviceNotAvailableException {
String baseDir = "/data/app-staging";
try {
diff --git a/tests/SurfaceViewSyncTest/Android.bp b/tests/SurfaceViewSyncTest/Android.bp
new file mode 100644
index 000000000000..1c6e380aad63
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/Android.bp
@@ -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 {
+ // 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: "SurfaceViewSyncTest",
+ srcs: ["**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/SurfaceViewSyncTest/AndroidManifest.xml b/tests/SurfaceViewSyncTest/AndroidManifest.xml
new file mode 100644
index 000000000000..d085f8c168d3
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.test">
+ <application>
+ <activity android:name="SurfaceViewSyncActivity"
+ android:label="SurfaceView Sync Test"
+ 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/SurfaceViewSyncTest/OWNERS b/tests/SurfaceViewSyncTest/OWNERS
new file mode 100644
index 000000000000..0862c05e0ee4
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
new file mode 100644
index 000000000000..4433b2148384
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
@@ -0,0 +1,47 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/darker_gray"
+ tools:context="com.example.mysurfaceview.MainActivity">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="600dp" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:text="COLLAPSE SV"
+ android:id="@+id/expand_sv"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Switch
+ android:id="@+id/enable_sync_switch"
+ android:text="Enable Sync"
+ android:checked="true"
+ android:layout_alignParentEnd="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </RelativeLayout>
+</LinearLayout> \ 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
new file mode 100644
index 000000000000..ab7f24a8d326
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
@@ -0,0 +1,198 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.window.SurfaceSyncer;
+
+/**
+ * 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.
+ */
+public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.Callback {
+ private static final String TAG = "SurfaceViewSyncActivity";
+
+ private SurfaceView mSurfaceView;
+ private boolean mLastExpanded = true;
+
+ private RenderingThread mRenderingThread;
+
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+
+ private Button mExpandButton;
+ private Switch mEnableSyncSwitch;
+
+ private int mLastSyncId = -1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_surfaceview_sync);
+ mSurfaceView = findViewById(R.id.surface_view);
+ mSurfaceView.getHolder().addCallback(this);
+
+ WindowManager windowManager = getWindowManager();
+ WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
+ Rect bounds = metrics.getBounds();
+
+ LinearLayout container = findViewById(R.id.container);
+ mExpandButton = findViewById(R.id.expand_sv);
+ mEnableSyncSwitch = findViewById(R.id.enable_sync_switch);
+ mExpandButton.setOnClickListener(view -> updateSurfaceViewSize(bounds, container));
+
+ mRenderingThread = new RenderingThread(mSurfaceView.getHolder());
+ }
+
+ private void updateSurfaceViewSize(Rect bounds, View container) {
+ if (mLastSyncId >= 0) {
+ return;
+ }
+
+ final float height;
+ if (mLastExpanded) {
+ height = bounds.height() / 2f;
+ mExpandButton.setText("EXPAND SV");
+ } else {
+ height = bounds.height() / 1.5f;
+ mExpandButton.setText("COLLAPSE SV");
+ }
+ mLastExpanded = !mLastExpanded;
+
+ if (mEnableSyncSwitch.isChecked()) {
+ mLastSyncId = mSurfaceSyncer.setupSync(() -> { });
+ mSurfaceSyncer.addToSync(mLastSyncId, container);
+ }
+
+ ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
+ svParams.height = (int) height;
+ mSurfaceView.setLayoutParams(svParams);
+ }
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ final Canvas canvas = holder.lockCanvas();
+ canvas.drawARGB(255, 255, 0, 0);
+ holder.unlockCanvasAndPost(canvas);
+ mRenderingThread.startRendering();
+ mRenderingThread.renderFrame(null, mSurfaceView.getWidth(), mSurfaceView.getHeight());
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
+ if (mEnableSyncSwitch.isChecked()) {
+ if (mLastSyncId < 0) {
+ mRenderingThread.renderFrame(null, width, height);
+ return;
+ }
+ mSurfaceSyncer.addToSync(mLastSyncId, mSurfaceView, frameCallback ->
+ mRenderingThread.renderFrame(frameCallback, width, height));
+ mSurfaceSyncer.markSyncReady(mLastSyncId);
+ mLastSyncId = -1;
+ } else {
+ mRenderingThread.renderFrame(null, width, height);
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mRenderingThread.stopRendering();
+ }
+
+ private static class RenderingThread extends HandlerThread {
+ private final SurfaceHolder mSurfaceHolder;
+ private Handler mHandler;
+ private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private final Point mSurfaceSize = new Point();
+
+ int mColorValue = 0;
+ int mColorDelta = 10;
+ private final Paint mPaint = new Paint();
+
+ RenderingThread(SurfaceHolder holder) {
+ super("RenderingThread");
+ mSurfaceHolder = holder;
+ mPaint.setColor(Color.BLACK);
+ mPaint.setTextSize(100);
+ }
+
+ public void renderFrame(SurfaceSyncer.SurfaceViewFrameCallback frameCallback, int width,
+ int height) {
+ if (mHandler != null) {
+ mHandler.post(() -> {
+ mFrameCallback = frameCallback;
+ mSurfaceSize.set(width, height);
+ mRunnable.run();
+ });
+ }
+ }
+
+ private final Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mFrameCallback != null) {
+ mFrameCallback.onFrameStarted();
+ }
+
+ try {
+ // Long delay from start to finish to mimic slow draw
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
+ mColorValue += mColorDelta;
+ if (mColorValue > 245 || mColorValue < 10) {
+ mColorDelta *= -1;
+ }
+
+ Canvas c = mSurfaceHolder.lockCanvas();
+ c.drawRGB(255, 0, 0);
+ c.drawText("RENDERED CONTENT", 0, mSurfaceSize.y / 2, mPaint);
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ mFrameCallback = null;
+ }
+ };
+
+ public void startRendering() {
+ start();
+ mHandler = new Handler(getLooper());
+ }
+
+ public void stopRendering() {
+ if (mHandler != null) {
+ mHandler.removeCallbacks(mRunnable);
+ }
+ }
+ }
+}
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 7ee19fb37244..052ce3a902c1 100644
--- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNotNull;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -214,7 +215,7 @@ public class SmsApplicationTest {
ApplicationInfo bluetoothApplicationInfo = new ApplicationInfo();
bluetoothApplicationInfo.uid = FAKE_BT_UID;
bluetoothPackageInfo.applicationInfo = bluetoothApplicationInfo;
- when(mPackageManager.getPackageInfo(eq(SmsApplication.BLUETOOTH_PACKAGE_NAME), anyInt()))
+ when(mPackageManager.getPackageInfo(matches(".*android.bluetooth.services"), anyInt()))
.thenReturn(bluetoothPackageInfo);
PackageInfo telephonyProviderPackageInfo = new PackageInfo();
diff --git a/tests/TrustTests/Android.bp b/tests/TrustTests/Android.bp
new file mode 100644
index 000000000000..77f98e88f1eb
--- /dev/null
+++ b/tests/TrustTests/Android.bp
@@ -0,0 +1,41 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "TrustTests",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "androidx.test.uiautomator",
+ "mockito-target-minus-junit4",
+ "servicestests-utils",
+ "truth-prebuilt",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ test_suites: [
+ "device-tests",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml
new file mode 100644
index 000000000000..8b4cbfd0e44b
--- /dev/null
+++ b/tests/TrustTests/AndroidManifest.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.trust.test"
+ android:targetSandboxVersion="2">
+
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
+ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
+
+ <application>
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name="android.trust.TrustTestActivity"
+ 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=".UserUnlockRequestTrustAgent"
+ 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>
+
+ <service
+ android:name=".LockUserTrustAgent"
+ 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>
+
+ <service
+ android:name=".GrantAndRevokeTrustAgent"
+ 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>
+
+ <service
+ android:name=".TemporaryAndRenewableTrustAgent"
+ 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. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.trust.test">
+ </instrumentation>
+</manifest>
diff --git a/tests/TrustTests/AndroidTest.xml b/tests/TrustTests/AndroidTest.xml
new file mode 100644
index 000000000000..7ed19f5ba5fe
--- /dev/null
+++ b/tests/TrustTests/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="TrustTests configuration">
+ <option name="test-tag" value="TrustTests" />
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- Disable the double-tap power button for camera -->
+ <option name="run-command"
+ value="settings put secure camera_double_tap_power_gesture_disabled 1" />
+ <option name="teardown-command"
+ value="settings delete secure camera_double_tap_power_gesture_disabled" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="TrustTests.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.trust.test" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/TrustTests/OWNERS b/tests/TrustTests/OWNERS
new file mode 100644
index 000000000000..e2c6ce15b51e
--- /dev/null
+++ b/tests/TrustTests/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/trust/OWNERS
diff --git a/tests/TrustTests/README.md b/tests/TrustTests/README.md
new file mode 100644
index 000000000000..3427e30536d9
--- /dev/null
+++ b/tests/TrustTests/README.md
@@ -0,0 +1,40 @@
+# TrustTests framework tests
+
+These tests test the "trust" part of the platform primarily implemented via TrustManagerService in
+the system server and TrustAgentService in system apps.
+
+Tests are separated into separate files based on major groupings. When creating new tests, find a
+_closely_ matching existing test file or create a new test file. Prefer many test files over large
+test files.
+
+Each test file has its own trust agent. To create a new trust agent:
+
+1. Create a new class extending from `BaseTrustAgentService` class in your test file
+2. Add a new `<service>` stanza to `AndroidManifest.xml` in this directory for the new agent
+ following the pattern fo the existing agents.
+
+To run:
+
+```atest TrustTests```
+
+## Testing approach:
+
+1. Test the agent service as a black box; avoid inspecting internal state of the service or
+ modifying the system code outside of this directory.
+2. The primary interface to the system is through these three points:
+ 1. `TrustAgentService`, your agent created by the `TrustAgentRule` and accessible via
+ the `agent` property of the rule.
+ 1. Call command methods (e.g. `grantTrust`) directly on the agent
+ 2. Listen to events (e.g. `onUserRequestedUnlock`) by implementing the method in
+ your test's agent class and tracking invocations. See `UserUnlockRequestTest` for an
+ example.
+ 2. `TrustManager` which is the interface the rest of the system (e.g. SystemUI) has to the
+ service.
+ 1. Through this API, simulate system events that the service cares about
+ (e.g. `reportUnlockAttempt`).
+ 3. `TrustListener` which is the interface the rest of the system (e.g. SystemUI) uses to receive
+ events from the service.
+ 1. Through this, verify behavior that affects the rest of the system. For example,
+ see `LockStateTrackingRule`.
+3. To re-use code between tests, prefer creating new rules alongside the existing rules or adding
+ functionality to a _closely_ matching existing rule.
diff --git a/tests/TrustTests/TEST_MAPPING b/tests/TrustTests/TEST_MAPPING
new file mode 100644
index 000000000000..23923eeb83ee
--- /dev/null
+++ b/tests/TrustTests/TEST_MAPPING
@@ -0,0 +1,28 @@
+{
+ "presubmit": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "trust-tablet": [
+ {
+ "name": "TrustTests",
+ "options": [
+ {
+ "include-filter": "android.trust.test"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
new file mode 100644
index 000000000000..cf4965f1655d
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/BaseTrustAgentService.kt
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+import android.service.trust.TrustAgentService
+import android.util.Log
+import kotlin.reflect.KClass
+
+/**
+ * Base class for test trust agents.
+ */
+abstract class BaseTrustAgentService : TrustAgentService() {
+
+ override fun onCreate() {
+ super.onCreate()
+ Log.d(TAG, "${this::class.simpleName} created")
+ instances[this::class] = this
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ instances.remove(this::class)
+ }
+
+ companion object {
+ private val instances =
+ mutableMapOf<KClass<out BaseTrustAgentService>, BaseTrustAgentService>()
+ private const val TAG = "BaseTrustAgentService"
+
+ fun instance(serviceClass: KClass<out BaseTrustAgentService>): BaseTrustAgentService? {
+ return instances[serviceClass]
+ }
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/TrustTestActivity.kt b/tests/TrustTests/src/android/trust/TrustTestActivity.kt
new file mode 100644
index 000000000000..6c56feace3d7
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/TrustTestActivity.kt
@@ -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 android.trust
+
+import android.app.Activity
+import android.os.Bundle
+
+/**
+ * Activity for testing Trust.
+ */
+class TrustTestActivity : Activity() {
+
+ public override fun onCreate(icicle: Bundle?) {
+ super.onCreate(icicle)
+ setTurnScreenOn(true)
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
new file mode 100644
index 000000000000..f864fedf4e62
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.service.trust.GrantTrustResult
+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.TrustAgentRule
+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.android.server.testutils.mock
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verifyZeroInteractions
+
+/**
+ * Test for testing revokeTrust & grantTrust for non-renewable trust.
+ *
+ * atest TrustTests:GrantAndRevokeTrustTest
+ */
+@RunWith(AndroidJUnit4::class)
+class GrantAndRevokeTrustTest {
+ private val uiDevice = UiDevice.getInstance(getInstrumentation())
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<GrantAndRevokeTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Before
+ fun manageTrust() {
+ trustAgentRule.agent.setManagingTrust(true)
+ }
+
+ // This test serves a baseline for Grant tests, verifying that the default behavior of the
+ // device is to lock when put to sleep
+ @Test
+ fun sleepingDeviceWithoutGrantLocksDevice() {
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantKeepsDeviceUnlocked() {
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) {}
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertUnlocked()
+ }
+
+ @Test
+ fun grantKeepsDeviceUnlocked_untilRevoked() {
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0) {}
+ await()
+ uiDevice.sleep()
+ trustAgentRule.agent.revokeTrust()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantDoesNotCallBack() {
+ val callback = mock<(GrantTrustResult) -> Unit>()
+ trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
+ await()
+
+ verifyZeroInteractions(callback)
+ }
+
+ companion object {
+ private const val TAG = "GrantAndRevokeTrustTest"
+ private const val GRANT_MESSAGE = "granted by test"
+ private fun await() = Thread.sleep(250)
+ }
+}
+
+class GrantAndRevokeTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/LockUserTest.kt b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
new file mode 100644
index 000000000000..1194afa0123f
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/LockUserTest.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.LockStateTrackingRule
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TrustAgentRule
+import android.util.Log
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for testing lockUser.
+ *
+ * atest TrustTests:LockUserTest
+ */
+@RunWith(AndroidJUnit4::class)
+class LockUserTest {
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<LockUserTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Test
+ fun lockUser_locksTheDevice() {
+ Log.i(TAG, "Locking user")
+ trustAgentRule.agent.lockUser()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ companion object {
+ private const val TAG = "LockUserTest"
+ }
+}
+
+class LockUserTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
new file mode 100644
index 000000000000..ae722477a2bc
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/TemporaryAndRenewableTrustTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.service.trust.GrantTrustResult
+import android.service.trust.GrantTrustResult.STATUS_UNLOCKED_BY_GRANT
+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.TrustAgentRule
+import android.util.Log
+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 android.trust.test.lib.wait
+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 revokeTrust & grantTrust for renewable trust.
+ *
+ * atest TrustTests:TemporaryAndRenewableTrustTest
+ */
+@RunWith(AndroidJUnit4::class)
+class TemporaryAndRenewableTrustTest {
+ private val uiDevice = UiDevice.getInstance(getInstrumentation())
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val lockStateTrackingRule = LockStateTrackingRule()
+ private val trustAgentRule = TrustAgentRule<TemporaryAndRenewableTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(lockStateTrackingRule)
+ .around(trustAgentRule)
+
+ @Before
+ fun manageTrust() {
+ trustAgentRule.agent.setManagingTrust(true)
+ }
+
+ // This test serves a baseline for Grant tests, verifying that the default behavior of the
+ // device is to lock when put to sleep
+ @Test
+ fun sleepingDeviceWithoutGrantLocksDevice() {
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantTrustLockedDevice_deviceStaysLocked() {
+ uiDevice.sleep()
+ lockStateTrackingRule.assertLocked()
+
+ uiDevice.wakeUp()
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantTrustUnlockedDevice_deviceLocksOnScreenOff() {
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ @Test
+ fun grantTrustLockedDevice_grantTrustOnLockedDeviceUnlocksDevice() {
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+
+ uiDevice.wakeUp()
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+
+ lockStateTrackingRule.assertUnlocked()
+ }
+
+ @Test
+ fun grantTrustLockedDevice_callsBackWhenUnlocked() {
+ Log.i(TAG, "Granting renewable trust while unlocked")
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ await(1000)
+
+ Log.i(TAG, "Locking device")
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+ uiDevice.wakeUp()
+
+ Log.i(TAG, "Renewing trust and unlocking")
+ var result: GrantTrustResult? = null
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {
+ Log.i(TAG, "Callback received; status=${it.status}")
+ result = it
+ }
+ lockStateTrackingRule.assertUnlocked()
+
+ wait("callback triggered") { result?.status == STATUS_UNLOCKED_BY_GRANT }
+ }
+
+ @Test
+ fun grantTrustLockedDevice_revokeTrustPreventsSubsequentUnlock() {
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+ uiDevice.sleep()
+
+ lockStateTrackingRule.assertLocked()
+
+ trustAgentRule.agent.revokeTrust()
+ await(500)
+ uiDevice.wakeUp()
+
+ trustAgentRule.agent.grantTrust(
+ GRANT_MESSAGE, 0, FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) {}
+
+ lockStateTrackingRule.assertLocked()
+ }
+
+ companion object {
+ private const val TAG = "TemporaryAndRenewableTrustTest"
+ private const val GRANT_MESSAGE = "granted by test"
+ private fun await(millis: Long) = Thread.sleep(millis)
+ }
+}
+
+class TemporaryAndRenewableTrustAgent : BaseTrustAgentService()
diff --git a/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.kt
new file mode 100644
index 000000000000..6a8752abfde7
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/UserUnlockRequestTest.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 android.trust.test
+
+import android.app.trust.TrustManager
+import android.content.Context
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TrustAgentRule
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for the user unlock triggers.
+ *
+ * atest TrustTests:UserUnlockRequestTest
+ */
+@RunWith(AndroidJUnit4::class)
+class UserUnlockRequestTest {
+ private val context = getApplicationContext<Context>()
+ private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ private val userId = context.userId
+ private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+ private val trustAgentRule = TrustAgentRule<UserUnlockRequestTrustAgent>()
+
+ @get:Rule
+ val rule: RuleChain = RuleChain
+ .outerRule(activityScenarioRule)
+ .around(ScreenLockRule())
+ .around(trustAgentRule)
+
+ @Test
+ fun reportUserRequestedUnlock_propagatesToAgent() {
+ val oldCount = trustAgentRule.agent.onUserRequestedUnlockCallCount
+ trustManager.reportUserRequestedUnlock(userId, false)
+ await()
+
+ assertThat(trustAgentRule.agent.onUserRequestedUnlockCallCount)
+ .isEqualTo(oldCount + 1)
+ }
+
+ @Test
+ fun reportUserRequestedUnlock_propagatesToAgentWithDismissKeyguard() {
+ trustManager.reportUserRequestedUnlock(userId, true)
+ await()
+
+ assertThat(trustAgentRule.agent.lastCallDismissKeyguard)
+ .isTrue()
+ }
+
+ @Test
+ fun reportUserMayRequestUnlock_propagatesToAgent() {
+ val oldCount = trustAgentRule.agent.onUserMayRequestUnlockCallCount
+ trustManager.reportUserMayRequestUnlock(userId)
+ await()
+
+ assertThat(trustAgentRule.agent.onUserMayRequestUnlockCallCount)
+ .isEqualTo(oldCount + 1)
+ }
+
+ companion object {
+ private const val TAG = "UserUnlockRequestTest"
+ private fun await() = Thread.sleep(250)
+ }
+}
+
+class UserUnlockRequestTrustAgent : BaseTrustAgentService() {
+ var onUserRequestedUnlockCallCount: Long = 0
+ private set
+ var onUserMayRequestUnlockCallCount: Long = 0
+ private set
+ var lastCallDismissKeyguard: Boolean = false
+ private set
+
+ override fun onUserRequestedUnlock(dismissKeyguard: Boolean) {
+ Log.i(TAG, "onUserRequestedUnlock($dismissKeyguard)")
+ onUserRequestedUnlockCallCount++
+ lastCallDismissKeyguard = dismissKeyguard
+ }
+
+ override fun onUserMayRequestUnlock() {
+ Log.i(TAG, "onUserMayRequestUnlock")
+ onUserMayRequestUnlockCallCount++
+ }
+
+ companion object {
+ private const val TAG = "UserUnlockRequestTrustAgent"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
new file mode 100644
index 000000000000..2031af2cf0c9
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.lib
+
+import android.app.trust.TrustManager
+import android.app.trust.TrustManager.TrustListener
+import android.content.Context
+import android.util.Log
+import android.view.WindowManagerGlobal
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Rule for tracking the lock state of the device based on events emitted to [TrustListener].
+ */
+class LockStateTrackingRule : TestRule {
+ private val context: Context = getApplicationContext()
+ private val windowManager = WindowManagerGlobal.getWindowManagerService()
+
+ @Volatile lateinit var lockState: LockState
+ private set
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ lockState = LockState(locked = windowManager.isKeyguardLocked)
+ val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ val listener = Listener()
+
+ trustManager.registerTrustListener(listener)
+ try {
+ base.evaluate()
+ } finally {
+ trustManager.unregisterTrustListener(listener)
+ }
+ }
+ }
+
+ fun assertLocked() {
+ wait("un-locked per TrustListener") { lockState.locked == true }
+ wait("keyguard lock") { windowManager.isKeyguardLocked }
+ }
+
+ fun assertUnlocked() {
+ wait("locked per TrustListener") { lockState.locked == false }
+ }
+
+ inner class Listener : TrustListener {
+ override fun onTrustChanged(
+ enabled: Boolean,
+ userId: Int,
+ flags: Int,
+ trustGrantedMessages: MutableList<String>
+ ) {
+ 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(
+ val locked: Boolean? = null
+ )
+
+ companion object {
+ private const val TAG = "LockStateTrackingRule"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
new file mode 100644
index 000000000000..4189baae10cb
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -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 android.trust.test.lib
+
+import android.content.Context
+import android.util.Log
+import android.view.KeyEvent
+import android.view.WindowManagerGlobal
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import androidx.test.uiautomator.UiDevice
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Sets a screen lock on the device for the duration of the test.
+ */
+class ScreenLockRule : TestRule {
+ private val context: Context = getApplicationContext()
+ private val uiDevice = UiDevice.getInstance(getInstrumentation())
+ private val windowManager = WindowManagerGlobal.getWindowManagerService()
+ private val lockPatternUtils = LockPatternUtils(context)
+ private var instantLockSavedValue = false
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ verifyNoScreenLockAlreadySet()
+ dismissKeyguard()
+ setScreenLock()
+ setLockOnPowerButton()
+
+ try {
+ base.evaluate()
+ } finally {
+ removeScreenLock()
+ revertLockOnPowerButton()
+ dismissKeyguard()
+ }
+ }
+ }
+
+ private fun verifyNoScreenLockAlreadySet() {
+ assertWithMessage("Screen Lock must not already be set on device")
+ .that(lockPatternUtils.isSecure(context.userId))
+ .isFalse()
+ }
+
+ fun dismissKeyguard() {
+ wait("keyguard dismissed") { count ->
+ if (!uiDevice.isScreenOn) {
+ Log.i(TAG, "Waking device, +500ms")
+ uiDevice.wakeUp()
+ }
+
+ // Bouncer may be shown due to a race; back dismisses it
+ if (count >= 10) {
+ Log.i(TAG, "Pressing back to dismiss Bouncer")
+ uiDevice.pressKeyCode(KeyEvent.KEYCODE_BACK)
+ }
+
+ windowManager.dismissKeyguard(null, null)
+
+ !windowManager.isKeyguardLocked
+ }
+ }
+
+ private fun setScreenLock() {
+ lockPatternUtils.setLockCredential(
+ LockscreenCredential.createPin(PIN),
+ LockscreenCredential.createNone(),
+ context.userId
+ )
+ wait("screen lock set") { lockPatternUtils.isSecure(context.userId) }
+ Log.i(TAG, "Device PIN set to $PIN")
+ }
+
+ private fun setLockOnPowerButton() {
+ instantLockSavedValue = lockPatternUtils.getPowerButtonInstantlyLocks(context.userId)
+ lockPatternUtils.setPowerButtonInstantlyLocks(true, context.userId)
+ }
+
+ private fun removeScreenLock() {
+ var lockCredentialUnset = lockPatternUtils.setLockCredential(
+ LockscreenCredential.createNone(),
+ LockscreenCredential.createPin(PIN),
+ context.userId)
+ Log.i(TAG, "Removing screen lock")
+ assertWithMessage("Lock screen credential should be unset")
+ .that(lockCredentialUnset)
+ .isTrue()
+
+ lockPatternUtils.setLockScreenDisabled(true, context.userId)
+ wait("screen lock un-set") {
+ lockPatternUtils.isLockScreenDisabled(context.userId)
+ }
+ wait("screen lock insecure") { !lockPatternUtils.isSecure(context.userId) }
+ }
+
+ private fun revertLockOnPowerButton() {
+ lockPatternUtils.setPowerButtonInstantlyLocks(instantLockSavedValue, context.userId)
+ }
+
+ companion object {
+ private const val TAG = "ScreenLockRule"
+ private const val PIN = "0000"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
new file mode 100644
index 000000000000..18bc029b6845
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.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 android.trust.test.lib
+
+import android.app.trust.TrustManager
+import android.content.ComponentName
+import android.content.Context
+import android.trust.BaseTrustAgentService
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import com.android.internal.widget.LockPatternUtils
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import kotlin.reflect.KClass
+
+/**
+ * Enables a trust agent and causes the system service to bind to it.
+ *
+ * The enabled agent can be accessed during the test via the [agent] property.
+ *
+ * @constructor Creates the rule. Do not use; instead, use [invoke].
+ */
+class TrustAgentRule<T : BaseTrustAgentService>(
+ private val serviceClass: KClass<T>
+) : TestRule {
+ private val context: Context = getApplicationContext()
+ private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+ private val lockPatternUtils = LockPatternUtils(context)
+
+ val agent get() = BaseTrustAgentService.instance(serviceClass) as T
+
+ override fun apply(base: Statement, description: Description) = object : Statement() {
+ override fun evaluate() {
+ verifyTrustServiceRunning()
+ unlockDeviceWithCredential()
+ enableTrustAgent()
+
+ try {
+ verifyAgentIsRunning()
+ base.evaluate()
+ } finally {
+ disableTrustAgent()
+ }
+ }
+ }
+
+ private fun verifyTrustServiceRunning() {
+ assertWithMessage("Trust service is not running").that(trustManager).isNotNull()
+ }
+
+ private fun unlockDeviceWithCredential() {
+ Log.d(TAG, "Unlocking device with credential")
+ trustManager.reportUnlockAttempt(true, context.userId)
+ }
+
+ private fun enableTrustAgent() {
+ val componentName = ComponentName(context, serviceClass.java)
+ val userId = context.userId
+ Log.i(TAG, "Enabling trust agent ${componentName.flattenToString()} for user $userId")
+ val agents = mutableListOf(componentName)
+ .plus(lockPatternUtils.getEnabledTrustAgents(userId))
+ .distinct()
+ lockPatternUtils.setEnabledTrustAgents(agents, userId)
+ }
+
+ private fun verifyAgentIsRunning() {
+ wait("${serviceClass.simpleName} to be running") {
+ BaseTrustAgentService.instance(serviceClass) != null
+ }
+ }
+
+ private fun disableTrustAgent() {
+ val componentName = ComponentName(context, serviceClass.java)
+ val userId = context.userId
+ Log.i(TAG, "Disabling trust agent ${componentName.flattenToString()} for user $userId")
+ val agents = lockPatternUtils.getEnabledTrustAgents(userId).toMutableList()
+ .distinct()
+ .minus(componentName)
+ lockPatternUtils.setEnabledTrustAgents(agents, userId)
+ }
+
+ companion object {
+ /**
+ * Creates a new rule for the specified agent class. Example usage:
+ * ```
+ * @get:Rule val rule = TrustAgentRule<MyTestAgent>()
+ * ```
+ */
+ inline operator fun <reified T : BaseTrustAgentService> invoke() =
+ TrustAgentRule(T::class)
+
+ private const val TAG = "TrustAgentRule"
+ }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/utils.kt b/tests/TrustTests/src/android/trust/test/lib/utils.kt
new file mode 100644
index 000000000000..e047202f6740
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/lib/utils.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.lib
+
+import android.util.Log
+import com.google.common.truth.Truth.assertWithMessage
+
+private const val TAG = "TrustTestUtils"
+
+/**
+ * Waits for [conditionFunction] to be true with a failed assertion if it is not after [maxWait]
+ * ms.
+ *
+ * The condition function can perform additional logic (for example, logging or attempting to make
+ * the condition become true).
+ *
+ * @param conditionFunction function which takes the attempt count & returns whether the condition
+ * is met
+ */
+internal fun wait(
+ description: String? = null,
+ maxWait: Long = 30000L,
+ rate: Long = 50L,
+ conditionFunction: (count: Int) -> Boolean
+) {
+ var waited = 0L
+ var count = 0
+ while (!conditionFunction.invoke(count)) {
+ assertWithMessage("Condition exceeded maximum wait time of $maxWait ms: $description")
+ .that(waited <= maxWait)
+ .isTrue()
+ waited += rate
+ count++
+ Log.i(TAG, "Waiting for $description ($waited/$maxWait) #$count")
+ Thread.sleep(rate)
+ }
+}
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index e07fbbf7a1c1..9a9e42bfc300 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -37,15 +37,19 @@ android_test {
"vts",
],
data: [
- ":NotoColorEmojiTtf",
":UpdatableSystemFontTestCertDer",
- ":UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- ":UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ ":UpdatableSystemFontTest_NotoColorEmoji.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmoji.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ ":UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+ ":UpdatableSystemFontTest_NotoSerif-Regular.sig",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+ ":UpdatableSystemFontTest_NotoSerif-Bold.sig",
],
sdk_version: "test_current",
}
diff --git a/tests/UpdatableSystemFontTest/AndroidTest.xml b/tests/UpdatableSystemFontTest/AndroidTest.xml
index 4f6487e7e953..9e2a4b643bf6 100644
--- a/tests/UpdatableSystemFontTest/AndroidTest.xml
+++ b/tests/UpdatableSystemFontTest/AndroidTest.xml
@@ -28,14 +28,18 @@
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
- <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf" />
- <option name="push" value="UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmoji.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiV0.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig->/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Regular.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.sig->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig" />
+ <option name="push" value="UpdatableSystemFontTest_NotoSerif-Bold.ttf->/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
index 947e9c2ff56a..a8c27fb0f116 100644
--- a/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
+++ b/tests/UpdatableSystemFontTest/EmojiRenderingTestApp/src/com/android/emojirenderingtestapp/EmojiRenderingTestActivity.java
@@ -20,6 +20,7 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import android.app.Activity;
+import android.graphics.Typeface;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -27,14 +28,20 @@ import android.widget.TextView;
/** Test app to render an emoji. */
public class EmojiRenderingTestActivity extends Activity {
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
- TextView textView = new TextView(this);
- textView.setText("\uD83E\uDD72"); // 🥲
- container.addView(textView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView emojiTextView = new TextView(this);
+ emojiTextView.setText("\uD83E\uDD72"); // 🥲
+ container.addView(emojiTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
+ TextView serifTextView = new TextView(this);
+ serifTextView.setTypeface(Typeface.create(TEST_NOTO_SERIF, Typeface.NORMAL));
+ serifTextView.setText(TEST_NOTO_SERIF);
+ container.addView(serifTextView, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
setContentView(container);
}
}
diff --git a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
index 6bd07d0a84fd..cbe13d9aa149 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -16,6 +16,9 @@
package com.android.updatablesystemfont;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_BOLD;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.graphics.fonts.FontFamilyUpdateRequest;
import android.graphics.fonts.FontFileUpdateRequest;
import android.graphics.fonts.FontManager;
+import android.graphics.fonts.FontStyle;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.RootPermissionTest;
import android.security.FileIntegrityManager;
@@ -77,31 +81,45 @@ public class UpdatableSystemFontTest {
private static final String SYSTEM_FONTS_DIR = "/system/fonts/";
private static final String DATA_FONTS_DIR = "/data/fonts/files/";
private static final String CERT_PATH = "/data/local/tmp/UpdatableSystemFontTestCert.der";
- private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF =
- "/data/local/tmp/NotoColorEmoji.ttf";
- private static final String ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig";
+ private static final String NOTO_COLOR_EMOJI_POSTSCRIPT_NAME = "NotoColorEmoji";
+ private static final String NOTO_COLOR_EMOJI_TTF =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.ttf";
+ private static final String NOTO_COLOR_EMOJI_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmoji.sig";
// A font with revision == 0.
private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_V0_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiV0.sig";
// A font with revision == original + 1
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig";
// A font with revision == original + 2
private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf";
- private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG =
- "/data/local/tmp/UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig";
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf";
+ private static final String TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig";
+
+ private static final String NOTO_SERIF_REGULAR_POSTSCRIPT_NAME = "NotoSerif";
+ private static final String NOTO_SERIF_REGULAR_TTF =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.ttf";
+ private static final String NOTO_SERIF_REGULAR_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Regular.sig";
+
+ private static final String NOTO_SERIF_BOLD_POSTSCRIPT_NAME = "NotoSerif-Bold";
+ private static final String NOTO_SERIF_BOLD_TTF =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.ttf";
+ private static final String NOTO_SERIF_BOLD_SIG =
+ "/data/local/tmp/UpdatableSystemFontTest_NotoSerif-Bold.sig";
private static final String EMOJI_RENDERING_TEST_APP_ID = "com.android.emojirenderingtestapp";
private static final String EMOJI_RENDERING_TEST_ACTIVITY =
EMOJI_RENDERING_TEST_APP_ID + "/.EmojiRenderingTestActivity";
+ // This should be the same as the one in EmojiRenderingTestActivity.
+ private static final String TEST_NOTO_SERIF = "test-noto-serif";
private static final long ACTIVITY_TIMEOUT_MILLIS = SECONDS.toMillis(10);
private static final String GET_AVAILABLE_FONTS_TEST_ACTIVITY =
@@ -141,11 +159,20 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont() throws Exception {
+ FontConfig oldFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
+ // Check that font config is updated.
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
+ FontConfig newFontConfig =
+ SystemUtil.callWithShellPermissionIdentity(mFontManager::getFontConfig);
+ assertThat(newFontConfig.getConfigVersion())
+ .isGreaterThan(oldFontConfig.getConfigVersion());
+ assertThat(newFontConfig.getLastModifiedTimeMillis())
+ .isGreaterThan(oldFontConfig.getLastModifiedTimeMillis());
// The updated font should be readable and unmodifiable.
expectCommandToSucceed("dd status=none if=" + fontPath + " of=/dev/null");
expectCommandToFail("dd status=none if=" + CERT_PATH + " of=" + fontPath);
@@ -154,11 +181,11 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_twice() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath2).startsWith(DATA_FONTS_DIR);
@@ -173,16 +200,16 @@ public class UpdatableSystemFontTest {
public void updateFont_allowSameVersion() throws Exception {
// Update original font to the same version
assertThat(updateFontFile(
- ORIGINAL_NOTO_COLOR_EMOJI_TTF, ORIGINAL_NOTO_COLOR_EMOJI_TTF_FSV_SIG))
+ NOTO_COLOR_EMOJI_TTF, NOTO_COLOR_EMOJI_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath2 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
// Update updated font to the same version
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String fontPath3 = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -195,28 +222,58 @@ public class UpdatableSystemFontTest {
@Test
public void updateFont_invalidCert() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_ERROR_VERIFICATION_FAILURE);
}
@Test
public void updateFont_downgradeFromSystem() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_V0_TTF, TEST_NOTO_COLOR_EMOJI_V0_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
public void updateFont_downgradeFromData() throws Exception {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS2_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS2_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
}
@Test
+ public void updateFontFamily() throws Exception {
+ assertThat(updateNotoSerifAs("serif")).isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(0).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(0).getStyle().getWeight())
+ .isEqualTo(FONT_WEIGHT_NORMAL);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getFile().getAbsolutePath())
+ .startsWith(DATA_FONTS_DIR);
+ assertThat(family.getFontList().get(1).getStyle().getWeight()).isEqualTo(FONT_WEIGHT_BOLD);
+ }
+
+ @Test
+ public void updateFontFamily_asNewFont() throws Exception {
+ assertThat(updateNotoSerifAs("UpdatableSystemFontTest-serif"))
+ .isEqualTo(FontManager.RESULT_SUCCESS);
+ FontConfig.FontFamily family = findFontFamilyOrThrow("UpdatableSystemFontTest-serif");
+ assertThat(family.getFontList()).hasSize(2);
+ assertThat(family.getFontList().get(0).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
+ assertThat(family.getFontList().get(1).getPostScriptName())
+ .isEqualTo(NOTO_SERIF_BOLD_POSTSCRIPT_NAME);
+ }
+
+ @Test
public void launchApp() throws Exception {
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(SYSTEM_FONTS_DIR);
@@ -231,22 +288,25 @@ public class UpdatableSystemFontTest {
String originalFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(originalFontPath).startsWith(SYSTEM_FONTS_DIR);
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
String updatedFontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(updatedFontPath).startsWith(DATA_FONTS_DIR);
+ updateNotoSerifAs(TEST_NOTO_SERIF);
+ String notoSerifPath = getFontPath(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME);
startActivity(EMOJI_RENDERING_TEST_APP_ID, EMOJI_RENDERING_TEST_ACTIVITY);
// The original font should NOT be opened by the app.
SystemUtil.eventually(() -> {
assertThat(isFileOpenedBy(updatedFontPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
assertThat(isFileOpenedBy(originalFontPath, EMOJI_RENDERING_TEST_APP_ID)).isFalse();
+ assertThat(isFileOpenedBy(notoSerifPath, EMOJI_RENDERING_TEST_APP_ID)).isTrue();
}, ACTIVITY_TIMEOUT_MILLIS);
}
@Test
public void reboot() throws Exception {
expectCommandToSucceed(String.format("cmd font update %s %s",
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
String fontPath = getFontPath(NOTO_COLOR_EMOJI_POSTSCRIPT_NAME);
assertThat(fontPath).startsWith(DATA_FONTS_DIR);
@@ -264,7 +324,7 @@ public class UpdatableSystemFontTest {
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
for (int i = 0; i < 10; i++) {
assertThat(updateFontFile(
- TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG))
+ TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF, TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG))
.isEqualTo(FontManager.RESULT_SUCCESS);
List<String> openFiles = getOpenFiles("system_server");
for (Pattern p : Arrays.asList(PATTERN_FONT_FILES, PATTERN_SYSTEM_FONT_FILES,
@@ -285,7 +345,7 @@ public class UpdatableSystemFontTest {
public void fdLeakTest_withoutPermission() throws Exception {
Pattern patternEmojiVPlus1 =
Pattern.compile(Pattern.quote(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF));
- byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF_FSV_SIG));
+ byte[] signature = Files.readAllBytes(Paths.get(TEST_NOTO_COLOR_EMOJI_VPLUS1_SIG));
try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(
new File(TEST_NOTO_COLOR_EMOJI_VPLUS1_TTF), MODE_READ_ONLY)) {
assertThrows(SecurityException.class,
@@ -340,18 +400,56 @@ public class UpdatableSystemFontTest {
configVersion);
}
+ private int updateNotoSerifAs(String familyName) throws IOException {
+ List<FontFamilyUpdateRequest.Font> fonts = Arrays.asList(
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_REGULAR_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT)).build(),
+ new FontFamilyUpdateRequest.Font.Builder(NOTO_SERIF_BOLD_POSTSCRIPT_NAME,
+ new FontStyle(FONT_WEIGHT_BOLD, FONT_SLANT_UPRIGHT)).build());
+ FontFamilyUpdateRequest.FontFamily fontFamily =
+ new FontFamilyUpdateRequest.FontFamily.Builder(familyName, fonts).build();
+ byte[] regularSig = Files.readAllBytes(Paths.get(NOTO_SERIF_REGULAR_SIG));
+ byte[] boldSig = Files.readAllBytes(Paths.get(NOTO_SERIF_BOLD_SIG));
+ try (ParcelFileDescriptor regularFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_REGULAR_TTF), MODE_READ_ONLY);
+ ParcelFileDescriptor boldFd = ParcelFileDescriptor.open(
+ new File(NOTO_SERIF_BOLD_TTF), MODE_READ_ONLY)) {
+ return SystemUtil.runWithShellPermissionIdentity(() -> {
+ FontConfig fontConfig = mFontManager.getFontConfig();
+ return mFontManager.updateFontFamily(new FontFamilyUpdateRequest.Builder()
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(regularFd, regularSig))
+ .addFontFileUpdateRequest(
+ new FontFileUpdateRequest(boldFd, boldSig))
+ .addFontFamily(fontFamily)
+ .build(), fontConfig.getConfigVersion());
+ });
+ }
+ }
+
private String getFontPath(String psName) {
- return SystemUtil.runWithShellPermissionIdentity(() -> {
- FontConfig fontConfig = mFontManager.getFontConfig();
- for (FontConfig.FontFamily family : fontConfig.getFontFamilies()) {
- for (FontConfig.Font font : family.getFontList()) {
- if (psName.equals(font.getPostScriptName())) {
- return font.getFile().getAbsolutePath();
- }
- }
- }
- throw new AssertionError("Font not found: " + psName);
- });
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .flatMap(family -> family.getFontList().stream())
+ .filter(font -> 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)
+ .orElseThrow(() -> new AssertionError("Font not found: " + psName))
+ .getFile()
+ .getAbsolutePath();
+ }
+
+ private FontConfig.FontFamily findFontFamilyOrThrow(String familyName) {
+ FontConfig fontConfig =
+ SystemUtil.runWithShellPermissionIdentity(mFontManager::getFontConfig);
+ return fontConfig.getFontFamilies().stream()
+ .filter(family -> familyName.equals(family.getName()))
+ // Return the last match, because the latter family takes precedence if two families
+ // have the same name.
+ .reduce((first, second) -> second)
+ .orElseThrow(() -> new AssertionError("Family not found: " + familyName));
}
private static void startActivity(String appId, String activityId) throws Exception {
diff --git a/tests/UpdatableSystemFontTest/testdata/Android.bp b/tests/UpdatableSystemFontTest/testdata/Android.bp
index 426464ea574e..0bdb3a8c6b14 100644
--- a/tests/UpdatableSystemFontTest/testdata/Android.bp
+++ b/tests/UpdatableSystemFontTest/testdata/Android.bp
@@ -21,11 +21,19 @@ package {
default_applicable_licenses: ["frameworks_base_license"],
}
-// An existing module name is reused to avoid merge conflicts.
-// TODO: fix the font and module name.
filegroup {
- name: "NotoColorEmojiTtf",
- srcs: ["NotoColorEmoji.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmoji.ttf",
+ srcs: ["UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.ttf",
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+}
+
+filegroup {
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.ttf",
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
}
filegroup {
@@ -43,14 +51,10 @@ filegroup {
srcs: ["UpdatableSystemFontTestCert.der"],
}
-genrule_defaults {
- name: "updatable_system_font_increment_font_revision_default",
-}
-
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -59,9 +63,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -70,9 +74,9 @@ genrule {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf",
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf"],
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf",
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
tools: ["update_font_metadata"],
cmd: "$(location update_font_metadata) " +
"--input=$(in) " +
@@ -94,29 +98,43 @@ genrule_defaults {
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiTtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmoji.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmoji.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmoji.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoColorEmojiV0.sig",
+ defaults: ["updatable_system_font_sig_gen_default"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiV0.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiV0.sig"],
+}
+
+genrule {
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":NotoColorEmojiTtf"],
- out: ["UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus1.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus1.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiV0TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiV0Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiV0.ttf.fsv_sig"],
+ srcs: [":UpdatableSystemFontTest_NotoColorEmojiVPlus2.ttf"],
+ out: ["UpdatableSystemFontTest_NotoColorEmojiVPlus2.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus1TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoSerif-Regular.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus1Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus1.ttf.fsv_sig"],
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Regular.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Regular.sig"],
}
genrule {
- name: "UpdatableSystemFontTestNotoColorEmojiVPlus2TtfFsvSig",
+ name: "UpdatableSystemFontTest_NotoSerif-Bold.sig",
defaults: ["updatable_system_font_sig_gen_default"],
- srcs: [":UpdatableSystemFontTestNotoColorEmojiVPlus2Ttf"],
- out: ["UpdatableSystemFontTestNotoColorEmojiVPlus2.ttf.fsv_sig"],
+ srcs: ["UpdatableSystemFontTest_NotoSerif-Bold.ttf"],
+ out: ["UpdatableSystemFontTest_NotoSerif-Bold.sig"],
}
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
index f71f52cbaa2b..f71f52cbaa2b 100644
--- a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttf
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
index 6540c5898ec5..6540c5898ec5 100644
--- a/tests/UpdatableSystemFontTest/testdata/NotoColorEmoji.ttx
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoColorEmoji.ttx
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
new file mode 100644
index 000000000000..66c1bd2a09b1
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
new file mode 100644
index 000000000000..8c4215eafc60
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Bold.ttx
@@ -0,0 +1,196 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <!-- Currently NotoSerif-Bold.ttf's fontRevision is 1.xx.
+ 100.0 will be sufficiently larger than that. -->
+ <fontRevision value="100.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Feb 16 12:00:00 2022"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="3000" lsb="93"/> <!-- 3em -->
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <!-- length will be calculated by the compiler. -->
+ <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+ <!-- The font must support at least one of the characters used
+ in OtfFontFileParser to validate the font. -->
+ <map code="0x61" name="a" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="0" y="300" on="1" />
+ <pt x="300" y="300" on="1" />
+ <pt x="300" y="0" on="1" />
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2022 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Bold
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ <!-- Android identifies the target font to be updated by PostScript name.
+ To test updating NotoSerif-Bold.ttf, the PostScript needs to be
+ the same as NotoSerif-Bold.ttf here. -->
+ NotoSerif-Bold
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
new file mode 100644
index 000000000000..707ae281d990
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttf
Binary files differ
diff --git a/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
new file mode 100644
index 000000000000..754eae31c7c1
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/testdata/UpdatableSystemFontTest_NotoSerif-Regular.ttx
@@ -0,0 +1,196 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="a"/>
+ </GlyphOrder>
+
+ <head>
+ <tableVersion value="1.0"/>
+ <!-- Currently NotoSerif-Regular.ttf's fontRevision is 1.xx.
+ 100.0 will be sufficiently larger than that. -->
+ <fontRevision value="100.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Feb 16 12:00:00 2022"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="0x00010000"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ </hhea>
+
+ <maxp>
+ <tableVersion value="0x10000"/>
+ <maxZones value="0"/>
+ <maxTwilightPoints value="0"/>
+ <maxStorage value="0"/>
+ <maxFunctionDefs value="0"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="0"/>
+ <maxSizeOfInstructions value="0"/>
+ <maxComponentElements value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="a" width="3000" lsb="93"/> <!-- 3em -->
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <!-- length will be calculated by the compiler. -->
+ <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="0" language="0" nGroups="1">
+ <!-- The font must support at least one of the characters used
+ in OtfFontFileParser to validate the font. -->
+ <map code="0x61" name="a" />
+ </cmap_format_12>
+ </cmap>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+ <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+ <TTGlyph name="a" xMin="0" yMin="0" xMax="300" yMax="300">
+ <contour>
+ <pt x="0" y="0" on="1" />
+ <pt x="0" y="300" on="1" />
+ <pt x="300" y="300" on="1" />
+ <pt x="300" y="0" on="1" />
+ </contour>
+ <instructions />
+ </TTGlyph>
+ </glyf>
+
+ <name>
+ <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+ Copyright (C) 2022 The Android Open Source Project
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Sample Font
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ <!-- Android identifies the target font to be updated by PostScript name.
+ To test updating NotoSerif-Regular.ttf, the PostScript needs to be
+ the same as NotoSerif-Regular.ttf here. -->
+ NotoSerif
+ </namerecord>
+ <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ </namerecord>
+ <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+ http://www.apache.org/licenses/LICENSE-2.0
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="3.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ </post>
+
+</ttFont>
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 7e8a13470c35..f695cbd5daf9 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -62,12 +62,13 @@ public class UsageStatsDatabasePerfTest {
private static final StatCombiner<UsageEvents.Event> sUsageStatsCombiner =
new StatCombiner<UsageEvents.Event>() {
@Override
- public void combine(IntervalStats stats, boolean mutable,
+ public boolean combine(IntervalStats stats, boolean mutable,
List<UsageEvents.Event> accResult) {
final int size = stats.events.size();
for (int i = 0; i < size; i++) {
accResult.add(stats.events.get(i));
}
+ return true;
}
};
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
index e4880fd10d67..1341c85feec3 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -62,7 +62,7 @@ public class TestEnrollmentActivity extends Activity {
public void onEnrollButtonClicked(View v) {
Keyphrase kp = new Keyphrase(KEYPHRASE_ID, RECOGNITION_MODES,
Locale.forLanguageTag(BCP47_LOCALE), TEXT,
- new int[] { UserManager.get(this).getUserHandle() /* current user */});
+ new int[] { UserManager.get(this).getProcessUserId() /* current user */});
UUID modelUuid = UUID.randomUUID();
// Generate a fake model to push.
byte[] data = new byte[1024];
diff --git a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java b/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
deleted file mode 100644
index ef014f0d4e53..000000000000
--- a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
+++ /dev/null
@@ -1,60 +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.server.net;
-
-import android.net.NetworkStats;
-import android.os.SystemClock;
-import com.android.server.net.NetworkStatsFactory;
-import com.google.caliper.AfterExperiment;
-import com.google.caliper.BeforeExperiment;
-import java.io.File;
-
-public class NetworkStatsFactoryBenchmark {
- private File mStats;
-
- // TODO: consider staging stats file with different number of rows
-
- @BeforeExperiment
- protected void setUp() {
- mStats = new File("/proc/net/xt_qtaguid/stats");
- }
-
- @AfterExperiment
- protected void tearDown() {
- mStats = null;
- }
-
- public void timeReadNetworkStatsDetailJava(int reps) throws Exception {
- for (int i = 0; i < reps; i++) {
- NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL,
- // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
- // Fixed compilation problem but needs addressing properly.
- new String[0], 999);
- }
- }
-
- public void timeReadNetworkStatsDetailNative(int reps) {
- for (int i = 0; i < reps; i++) {
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- NetworkStatsFactory.nativeReadNetworkStatsDetail(
- stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL,
- // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
- // Fixed compilation problem but needs addressing properly.
- new String[0], 999, false);
- }
- }
-}
diff --git a/tests/benchmarks/src/com/android/server/net/OWNERS b/tests/benchmarks/src/com/android/server/net/OWNERS
deleted file mode 100644
index aa87958f1d53..000000000000
--- a/tests/benchmarks/src/com/android/server/net/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/net/OWNERS
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
new file mode 100644
index 000000000000..e5eb3c7b6394
--- /dev/null
+++ b/tests/componentalias/Android.bp
@@ -0,0 +1,87 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "ComponentAliasTests_defaults",
+ static_libs: [
+ "androidx.test.rules",
+ "compatibility-device-util-axt",
+ "mockito-target-extended-minus-junit4",
+ "truth-prebuilt",
+ "ub-uiautomator",
+ ],
+ libs: ["android.test.base"],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ 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.xml b/tests/componentalias/AndroidManifest.xml
new file mode 100755
index 000000000000..7bb83a336833
--- /dev/null
+++ b/tests/componentalias/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?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>
+ <uses-library android:name="android.test.runner" />
+ <property android:name="com.android.EXPERIMENTAL_COMPONENT_ALIAS_OPT_IN" android:value="true" />
+ </application>
+</manifest>
diff --git a/tests/componentalias/AndroidManifest_main.xml b/tests/componentalias/AndroidManifest_main.xml
new file mode 100755
index 000000000000..70e817ebf3e7
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_main.xml
@@ -0,0 +1,28 @@
+<?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
new file mode 100644
index 000000000000..c96f1736c684
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_service_aliases.xml
@@ -0,0 +1,81 @@
+<?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
new file mode 100644
index 000000000000..24c0432bcf4c
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_service_targets.xml
@@ -0,0 +1,57 @@
+<?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
new file mode 100755
index 000000000000..21616f5edf00
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_sub1.xml
@@ -0,0 +1,28 @@
+<?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
new file mode 100755
index 000000000000..c11b0cd55ef4
--- /dev/null
+++ b/tests/componentalias/AndroidManifest_sub2.xml
@@ -0,0 +1,28 @@
+<?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
new file mode 100644
index 000000000000..afdfe79ea4a4
--- /dev/null
+++ b/tests/componentalias/AndroidTest-template.xml
@@ -0,0 +1,38 @@
+<?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/OWNERS b/tests/componentalias/OWNERS
new file mode 100644
index 000000000000..1073c817d793
--- /dev/null
+++ b/tests/componentalias/OWNERS
@@ -0,0 +1,2 @@
+omakoto@google.com
+yamasani@google.com \ No newline at end of file
diff --git a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
new file mode 100644
index 000000000000..99322ee46106
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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
new file mode 100644
index 000000000000..7d5e0b9c6d8a
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasBroadcastTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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
new file mode 100644
index 000000000000..ee20379d971a
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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
new file mode 100644
index 000000000000..d41696f27880
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasMessage.java
@@ -0,0 +1,215 @@
+/*
+ * 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
new file mode 100644
index 000000000000..0899886fe951
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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
new file mode 100644
index 000000000000..f0ff088815af
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
@@ -0,0 +1,315 @@
+/*
+ * 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/ComponentAliasTestCommon.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasTestCommon.java
new file mode 100644
index 000000000000..165d728c92a6
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasTestCommon.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+public final class ComponentAliasTestCommon {
+ private ComponentAliasTestCommon() {
+ }
+
+ public static final String TAG = "ComponentAliasTest";
+
+ 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";
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java b/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java
new file mode 100644
index 000000000000..1d05e72a2f3f
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/BaseReceiver.java
@@ -0,0 +1,44 @@
+/*
+ * 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/Target00.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target00.java
new file mode 100644
index 000000000000..8fb4e91f790c
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/Target00.java
@@ -0,0 +1,21 @@
+/*
+ * 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 android.content.componentalias.tests.s.BaseService;
+
+public class Target00 extends BaseReceiver {
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target01.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target01.java
new file mode 100644
index 000000000000..06f7a13f73d7
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/Target01.java
@@ -0,0 +1,19 @@
+/*
+ * 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 Target01 extends BaseReceiver {
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target02.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target02.java
new file mode 100644
index 000000000000..df7579d8304d
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/Target02.java
@@ -0,0 +1,19 @@
+/*
+ * 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 Target02 extends BaseReceiver {
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target03.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target03.java
new file mode 100644
index 000000000000..5ae55215f696
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/Target03.java
@@ -0,0 +1,19 @@
+/*
+ * 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 Target03 extends BaseReceiver {
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java b/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java
new file mode 100644
index 000000000000..f9b9886b0bb2
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/b/Target04.java
@@ -0,0 +1,19 @@
+/*
+ * 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
new file mode 100644
index 000000000000..535d9b80f100
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/BaseService.java
@@ -0,0 +1,70 @@
+/*
+ * 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
new file mode 100644
index 000000000000..64b91f5695f5
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/Target00.java
@@ -0,0 +1,19 @@
+/*
+ * 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
new file mode 100644
index 000000000000..bd589991d7dc
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/Target01.java
@@ -0,0 +1,19 @@
+/*
+ * 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
new file mode 100644
index 000000000000..0ddf8188768b
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/Target02.java
@@ -0,0 +1,19 @@
+/*
+ * 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
new file mode 100644
index 000000000000..0dbc0501b6f9
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/Target03.java
@@ -0,0 +1,19 @@
+/*
+ * 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
new file mode 100644
index 000000000000..099425867f02
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/s/Target04.java
@@ -0,0 +1,19 @@
+/*
+ * 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/notification/src/com/android/frameworks/tests/notification/NotificationTests.java b/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
index 7cda977d2115..5d639f6f6266 100644
--- a/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
+++ b/tests/notification/src/com/android/frameworks/tests/notification/NotificationTests.java
@@ -409,10 +409,10 @@ public class NotificationTests extends AndroidTestCase {
sleepIfYouCan(500);
L("Parceling notifications...");
- // we want to be able to use this test on older OSes that do not have getBlobAshmemSize
- Method getBlobAshmemSize = null;
+ // we want to be able to use this test on older OSes that do not have getOpenAshmemSize
+ Method getOpenAshmemSize = null;
try {
- getBlobAshmemSize = Parcel.class.getMethod("getBlobAshmemSize");
+ getOpenAshmemSize = Parcel.class.getMethod("getOpenAshmemSize");
} catch (NoSuchMethodException ex) {
}
for (int i=0; i<mNotifications.size(); i++) {
@@ -424,8 +424,8 @@ public class NotificationTests extends AndroidTestCase {
time = SystemClock.currentThreadTimeMillis() - time;
L(" %s: write parcel=%dms size=%d ashmem=%s",
summarize(n), time, p.dataPosition(),
- (getBlobAshmemSize != null)
- ? getBlobAshmemSize.invoke(p)
+ (getOpenAshmemSize != null)
+ ? getOpenAshmemSize.invoke(p)
: "???");
p.setDataPosition(0);
}
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index 84448333a8c6..c25ce71207ae 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil;
import org.junit.Assert;
import org.junit.ClassRule;
@@ -207,17 +206,6 @@ public class SystemPreparer extends ExternalResource {
default:
device.executeShellCommand("stop");
device.executeShellCommand("start");
- ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode();
- device.setRecoveryMode(ITestDevice.RecoveryMode.ONLINE);
-
- if (device.isEncryptionSupported()) {
- if (device.isDeviceEncrypted()) {
- LogUtil.CLog.e("Device is encrypted after userspace reboot!");
- device.unlockDevice();
- }
- }
-
- device.setRecoveryMode(cachedRecoveryMode);
device.waitForDeviceAvailable();
break;
}
@@ -380,7 +368,9 @@ public class SystemPreparer extends ExternalResource {
device.executeShellCommand("disable-verity");
device.reboot();
}
- device.executeShellCommand("remount");
+ device.enableAdbRoot();
+ device.remountSystemWritable();
+ device.remountVendorWritable();
device.waitForDeviceAvailable();
}
diff --git a/tests/utils/testutils/Android.bp b/tests/utils/testutils/Android.bp
index af9786b92f40..deff42a27f47 100644
--- a/tests/utils/testutils/Android.bp
+++ b/tests/utils/testutils/Android.bp
@@ -38,6 +38,6 @@ java_library {
"android.test.runner",
"android.test.base",
"android.test.mock",
- "mockito-target-minus-junit4",
+ "mockito-target-extended-minus-junit4",
],
}
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 824f91e1e826..15a6afc5ff7c 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
@@ -62,6 +62,7 @@ public final class FrameworksTestsFilter extends SelectTest {
"android.view.PendingInsetsControllerTest",
"android.window.", // all tests under the package.
"android.app.activity.ActivityThreadTest",
+ "android.app.activity.RegisterComponentCallbacksTest"
};
public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 41f73cd9c706..228520e8545b 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -18,6 +18,7 @@ android_test {
"java/**/*.kt",
],
platform_apis: true,
+ defaults: ["framework-connectivity-test-defaults"],
test_suites: ["device-tests"],
certificate: "platform",
static_libs: [
@@ -28,6 +29,7 @@ android_test {
"net-tests-utils",
"platform-test-annotations",
"services.core",
+ "service-connectivity-tiramisu-pre-jarjar",
],
libs: [
"android.test.runner",
diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml
index 2ad9aac67029..a8f657c89f76 100644
--- a/tests/vcn/AndroidManifest.xml
+++ b/tests/vcn/AndroidManifest.xml
@@ -16,7 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.tests.vcn">
-
+ <uses-sdk android:minSdkVersion="33"
+ android:targetSdkVersion="33"/>
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
index 33b9f0f75f81..2441e772468c 100644
--- a/tests/vcn/OWNERS
+++ b/tests/vcn/OWNERS
@@ -3,5 +3,5 @@ set noparent
benedictwong@google.com
ckesting@google.com
evitayan@google.com
+junyin@google.com
nharold@google.com
-jchalard@google.com \ No newline at end of file
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
new file mode 100644
index 000000000000..2fbcf9d87bd4
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+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.fail;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
+ 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() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+ .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+ .setRoaming(MATCH_FORBIDDEN)
+ .setOpportunistic(MATCH_REQUIRED)
+ .build();
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
+ assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
+ assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
+ }
+
+ @Test
+ public void testBuilderAndGettersForDefaultValues() {
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ new VcnCellUnderlyingNetworkTemplate.Builder().build();
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods.
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+ assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
+ assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
+ assertEquals(MATCH_ANY, networkPriority.getRoaming());
+ assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
+ }
+
+ @Test
+ public void testBuilderRequiresStricterEntryCriteria() {
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dc338ae0fdc7..2aef9ae7ca32 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,6 +17,8 @@
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 org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -30,6 +32,7 @@ import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
+import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -37,7 +40,9 @@ import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -50,9 +55,17 @@ public class VcnGatewayConnectionConfigTest {
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+ private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES =
+ new ArrayList();
+
static {
Arrays.sort(EXPOSED_CAPS);
Arrays.sort(UNDERLYING_CAPS);
+
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ UNDERLYING_NETWORK_TEMPLATES.add(
+ VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -82,7 +95,10 @@ public class VcnGatewayConnectionConfigTest {
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
- return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ final VcnGatewayConnectionConfig.Builder builder =
+ newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
+
+ return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
private static VcnGatewayConnectionConfig.Builder newBuilder() {
@@ -159,6 +175,15 @@ public class VcnGatewayConnectionConfigTest {
}
@Test
+ public void testBuilderRequiresNonNullNetworkTemplates() {
+ try {
+ newBuilder().setVcnUnderlyingNetworkPriorities(null);
+ fail("Expected exception due to invalid underlyingNetworkTemplates");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonNullRetryInterval() {
try {
newBuilder().setRetryIntervalsMillis(null);
@@ -195,6 +220,7 @@ public class VcnGatewayConnectionConfigTest {
Arrays.sort(exposedCaps);
assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
@@ -208,6 +234,16 @@ public class VcnGatewayConnectionConfigTest {
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+ @Test
+ public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
+ PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
+ configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
+
+ final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
+ assertEquals(
+ DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
+ }
+
private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
final IkeSessionParams ikeParams =
IkeSessionParamsUtilsTest.createBuilderMinimum()
@@ -249,4 +285,37 @@ public class VcnGatewayConnectionConfigTest {
assertNotEquals(tunnelParams, anotherTunnelParams);
assertNotEquals(config, anotherConfig);
}
+
+ private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates(
+ List<VcnUnderlyingNetworkTemplate> networkTemplates) {
+ return buildTestConfigWithExposedCaps(
+ new VcnGatewayConnectionConfig.Builder(
+ "buildTestConfigWithVcnUnderlyingNetworkTemplates",
+ TUNNEL_CONNECTION_PARAMS)
+ .setVcnUnderlyingNetworkPriorities(networkTemplates),
+ EXPOSED_CAPS);
+ }
+
+ @Test
+ public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception {
+ final VcnGatewayConnectionConfig config =
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES);
+
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList();
+ networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ final VcnGatewayConnectionConfig configEqual =
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual);
+
+ final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList();
+ networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+ final VcnGatewayConnectionConfig configNotEqual =
+ buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual);
+
+ assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual);
+ assertEquals(config, configEqual);
+
+ assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
+ assertNotEquals(config, configNotEqual);
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 000000000000..399e13600442
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * 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.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+ // Public for use in NetworkPriorityClassifierTest
+ public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+ public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+ public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+ public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
new file mode 100644
index 000000000000..4063178e005d
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
+ private static final String SSID = "TestWifi";
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
+ return new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMetered(MATCH_FORBIDDEN)
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setSsids(Set.of(SSID))
+ .build();
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+ assertEquals(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ networkPriority.getMinExitDownstreamBandwidthKbps());
+ assertEquals(Set.of(SSID), networkPriority.getSsids());
+ }
+
+ @Test
+ public void testBuilderAndGettersForDefaultValues() {
+ final VcnWifiUnderlyingNetworkTemplate networkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder().build();
+ assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+ // Explicitly expect 0, as documented in Javadoc on setter methods..
+ assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+ assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
+ assertTrue(networkPriority.getSsids().isEmpty());
+ }
+
+ @Test
+ public void testBuilderRequiresStricterEntryCriteria() {
+ try {
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ try {
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+ fail("Expected IAE for exit threshold > entry threshold");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkTemplate.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index f3851130c68a..3b201f9d20dd 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -135,11 +135,12 @@ public class IkeSessionParamsUtilsTest {
}
@Test
- public void testEncodeRecodeParamsWithIkeOptions() throws Exception {
+ public void testEncodeDecodeParamsWithIkeOptions() throws Exception {
final IkeSessionParams params =
createBuilderMinimum()
.addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
.addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+ .addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT)
.build();
verifyPersistableBundleEncodeDecodeIsLossless(params);
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 7c7dc4d79e9a..f924b2e9b932 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@ 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;
import android.net.vcn.VcnConfig;
@@ -115,18 +116,24 @@ import java.util.UUID;
public class VcnManagementServiceTest {
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
+ private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
private static final String TEST_CB_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName() + ".callback";
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
+ private static final ParcelUuid TEST_UUID_3 = new ParcelUuid(new UUID(2, 2));
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;
static {
final Context mockConfigContext = mock(Context.class);
- doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
+ doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(mockConfigContext);
+
+ doReturn(TEST_PACKAGE_NAME_2).when(mockConfigContext).getOpPackageName();
+ TEST_VCN_CONFIG_PKG_2 = VcnConfigTest.buildTestConfig(mockConfigContext);
}
private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
@@ -247,18 +254,24 @@ public class VcnManagementServiceTest {
eq(android.Manifest.permission.NETWORK_FACTORY), any());
}
+
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
+ setupMockedCarrierPrivilege(isPrivileged, TEST_PACKAGE_NAME);
+ }
+
+ private void setupMockedCarrierPrivilege(boolean isPrivileged, String pkg) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
.getSubscriptionsInGroup(any());
doReturn(mTelMgr)
.when(mTelMgr)
.createForSubscriptionId(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
- doReturn(isPrivileged
- ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
- : CARRIER_PRIVILEGE_STATUS_NO_ACCESS)
+ doReturn(
+ isPrivileged
+ ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+ : CARRIER_PRIVILEGE_STATUS_NO_ACCESS)
.when(mTelMgr)
- .checkCarrierPrivilegesForPackage(eq(TEST_PACKAGE_NAME));
+ .checkCarrierPrivilegesForPackage(eq(pkg));
}
@Test
@@ -414,7 +427,13 @@ public class VcnManagementServiceTest {
private BroadcastReceiver getPackageChangeReceiver() {
final ArgumentCaptor<BroadcastReceiver> captor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any());
+ verify(mMockContext).registerReceiver(captor.capture(), argThat(filter -> {
+ return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_DATA_CLEARED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ }), any(), any());
return captor.getValue();
}
@@ -627,6 +646,44 @@ public class VcnManagementServiceTest {
}
@Test
+ public void testPackageChangeListener_packageDataCleared() throws Exception {
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
+ final Vcn vcn = mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1);
+
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ intent.setData(Uri.parse("package:" + TEST_PACKAGE_NAME));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(TEST_UID));
+
+ receiver.onReceive(mMockContext, intent);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
+ public void testPackageChangeListener_packageFullyRemoved() throws Exception {
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
+ final Vcn vcn = mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1);
+
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ intent.setData(Uri.parse("package:" + TEST_PACKAGE_NAME));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(TEST_UID));
+
+ receiver.onReceive(mMockContext, intent);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
@@ -666,7 +723,7 @@ public class VcnManagementServiceTest {
@Test
public void testSetVcnConfigMismatchedPackages() throws Exception {
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME_2);
fail("Expected exception due to mismatched packages in config and method call");
} catch (IllegalArgumentException expected) {
verify(mMockPolicyListener, never()).onPolicyChanged();
@@ -766,11 +823,12 @@ public class VcnManagementServiceTest {
}
@Test
- public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception {
+ public void testClearVcnConfigRequiresCarrierPrivilegesOrProvisioningPackage()
+ throws Exception {
setupMockedCarrierPrivilege(false);
try {
- mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
}
@@ -779,20 +837,32 @@ public class VcnManagementServiceTest {
@Test
public void testClearVcnConfigMismatchedPackages() throws Exception {
try {
- mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, "IncorrectPackage");
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
fail("Expected security exception due to mismatched packages");
} catch (SecurityException expected) {
}
}
@Test
- public void testClearVcnConfig() throws Exception {
+ public void testClearVcnConfig_callerIsProvisioningPackage() throws Exception {
+ // Lose carrier privileges to test that provisioning package is sufficient.
+ setupMockedCarrierPrivilege(false);
+
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
+ public void testClearVcnConfig_callerIsCarrierPrivileged() throws Exception {
+ setupMockedCarrierPrivilege(true, TEST_PACKAGE_NAME_2);
+
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
public void testClearVcnConfigNotifiesStatusCallback() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
@@ -843,11 +913,12 @@ public class VcnManagementServiceTest {
@Test
public void testGetConfiguredSubscriptionGroupsMismatchedPackages() throws Exception {
- final String badPackage = "IncorrectPackage";
- doThrow(new SecurityException()).when(mAppOpsMgr).checkPackage(TEST_UID, badPackage);
+ doThrow(new SecurityException())
+ .when(mAppOpsMgr)
+ .checkPackage(TEST_UID, TEST_PACKAGE_NAME_2);
try {
- mVcnMgmtSvc.getConfiguredSubscriptionGroups(badPackage);
+ mVcnMgmtSvc.getConfiguredSubscriptionGroups(TEST_PACKAGE_NAME_2);
fail("Expected security exception due to mismatched packages");
} catch (SecurityException expected) {
}
@@ -855,14 +926,16 @@ public class VcnManagementServiceTest {
@Test
public void testGetConfiguredSubscriptionGroups() throws Exception {
+ setupMockedCarrierPrivilege(true, TEST_PACKAGE_NAME_2);
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_3, TEST_VCN_CONFIG_PKG_2, TEST_PACKAGE_NAME_2);
- // Assert that if both UUID 1 and 2 are provisioned, the caller only gets ones that they are
- // privileged for.
+ // Assert that if UUIDs 1, 2 and 3 are provisioned, the caller only gets ones that they are
+ // privileged for, or are the provisioning package of.
triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
final List<ParcelUuid> subGrps =
mVcnMgmtSvc.getConfiguredSubscriptionGroups(TEST_PACKAGE_NAME);
- assertEquals(Collections.singletonList(TEST_UUID_1), subGrps);
+ assertEquals(Arrays.asList(new ParcelUuid[] {TEST_UUID_1, TEST_UUID_2}), subGrps);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 1f0df62fe72c..09080be9ee41 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -22,9 +22,11 @@ import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
+import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -34,8 +36,10 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
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.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -47,9 +51,11 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
@@ -57,6 +63,8 @@ import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyManager.CarrierPrivilegesCallback;
+import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
@@ -83,7 +91,7 @@ public class TelephonySubscriptionTrackerTest {
private static final String PACKAGE_NAME =
TelephonySubscriptionTrackerTest.class.getPackage().getName();
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
- private static final int TEST_SIM_SLOT_INDEX = 1;
+ private static final int TEST_SIM_SLOT_INDEX = 0;
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
private static final int TEST_SUBSCRIPTION_ID_2 = 3;
@@ -99,6 +107,26 @@ public class TelephonySubscriptionTrackerTest {
TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
+ private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1";
+ private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2";
+ private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle();
+ private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER;
+ private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP;
+
+ static {
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG);
+
+ final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
+ subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER);
+ TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
+ }
+
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@NonNull private final Handler mHandler;
@@ -139,6 +167,9 @@ public class TelephonySubscriptionTrackerTest {
doReturn(mCarrierConfigManager)
.when(mContext)
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ doReturn(TEST_CARRIER_CONFIG)
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
// subId 1, 2 are in same subGrp, only subId 1 is active
doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -151,6 +182,8 @@ public class TelephonySubscriptionTrackerTest {
@Before
public void setUp() throws Exception {
+ doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
mTelephonySubscriptionTracker =
new TelephonySubscriptionTracker(mContext, mHandler, mCallback, mDeps);
@@ -180,6 +213,15 @@ public class TelephonySubscriptionTrackerTest {
return captor.getValue();
}
+ private List<CarrierPrivilegesCallback> getCarrierPrivilegesCallbacks() {
+ final ArgumentCaptor<CarrierPrivilegesCallback> captor =
+ ArgumentCaptor.forClass(CarrierPrivilegesCallback.class);
+ verify(mTelephonyManager, atLeastOnce())
+ .registerCarrierPrivilegesCallback(anyInt(), any(), captor.capture());
+
+ return captor.getAllValues();
+ }
+
private ActiveDataSubscriptionIdListener getActiveDataSubscriptionIdListener() {
final ArgumentCaptor<TelephonyCallback> captor =
ArgumentCaptor.forClass(TelephonyCallback.class);
@@ -188,6 +230,11 @@ public class TelephonySubscriptionTrackerTest {
return (ActiveDataSubscriptionIdListener) captor.getValue();
}
+ private Intent buildTestMultiSimConfigBroadcastIntent() {
+ Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
+ return intent;
+ }
+
private Intent buildTestBroadcastIntent(boolean hasValidSubscription) {
Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED);
intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX);
@@ -206,14 +253,24 @@ public class TelephonySubscriptionTrackerTest {
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
int activeSubId,
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(
+ activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages);
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ int activeSubId,
+ Map<Integer, SubscriptionInfo> subIdToInfoMap,
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
+ Map<ParcelUuid, Set<String>> privilegedPackages) {
+ return new TelephonySubscriptionSnapshot(
+ activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages);
}
private void verifyNoActiveSubscriptions() {
@@ -224,6 +281,8 @@ public class TelephonySubscriptionTrackerTest {
private void setupReadySubIds() {
mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER));
}
private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
@@ -239,12 +298,21 @@ public class TelephonySubscriptionTrackerTest {
any(),
eq(mHandler));
final IntentFilter filter = getIntentFilter();
- assertEquals(1, filter.countActions());
+ assertEquals(2, filter.countActions());
assertTrue(filter.hasAction(ACTION_CARRIER_CONFIG_CHANGED));
+ assertTrue(filter.hasAction(ACTION_MULTI_SIM_CONFIG_CHANGED));
verify(mSubscriptionManager)
.addOnSubscriptionsChangedListener(any(HandlerExecutor.class), any());
assertNotNull(getOnSubscriptionsChangedListener());
+
+ verify(mTelephonyManager, times(2))
+ .registerCarrierPrivilegesCallback(anyInt(), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager)
+ .registerCarrierPrivilegesCallback(eq(0), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager)
+ .registerCarrierPrivilegesCallback(eq(1), any(HandlerExecutor.class), any());
+ assertEquals(2, getCarrierPrivilegesCallbacks().size());
}
@Test
@@ -255,6 +323,50 @@ public class TelephonySubscriptionTrackerTest {
final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(eq(listener));
+
+ for (CarrierPrivilegesCallback carrierPrivilegesCallback :
+ getCarrierPrivilegesCallbacks()) {
+ verify(mTelephonyManager)
+ .unregisterCarrierPrivilegesCallback(eq(carrierPrivilegesCallback));
+ }
+ }
+
+ @Test
+ public void testMultiSimConfigChanged() throws Exception {
+ final ArrayMap<Integer, Integer> readySubIdsBySlotId = new ArrayMap<>();
+ readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1);
+ readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
+
+ mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP);
+ doReturn(1).when(mTelephonyManager).getActiveModemCount();
+
+ List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks =
+ getCarrierPrivilegesCallbacks();
+
+ mTelephonySubscriptionTracker.onReceive(mContext, buildTestMultiSimConfigBroadcastIntent());
+ mTestLooper.dispatchAll();
+
+ for (CarrierPrivilegesCallback carrierPrivilegesCallback : carrierPrivilegesCallbacks) {
+ verify(mTelephonyManager)
+ .unregisterCarrierPrivilegesCallback(eq(carrierPrivilegesCallback));
+ }
+
+ // Expect cache cleared for inactive slots.
+ assertNull(
+ mTelephonySubscriptionTracker
+ .getReadySubIdsBySlotId()
+ .get(TEST_SIM_SLOT_INDEX + 1));
+
+ // Expect a new CarrierPrivilegesListener to have been registered for slot 0, and none other
+ // (2 previously registered during startup, for slots 0 & 1)
+ verify(mTelephonyManager, times(3))
+ .registerCarrierPrivilegesCallback(anyInt(), any(HandlerExecutor.class), any());
+ verify(mTelephonyManager, times(2))
+ .registerCarrierPrivilegesCallback(eq(0), any(HandlerExecutor.class), any());
+
+ // Verify that this triggers a re-evaluation
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
}
@Test
@@ -314,6 +426,17 @@ public class TelephonySubscriptionTrackerTest {
}
@Test
+ public void testOnCarrierPrivilegesChanged() throws Exception {
+ setupReadySubIds();
+
+ final CarrierPrivilegesCallback callback = getCarrierPrivilegesCallbacks().get(0);
+ callback.onCarrierPrivilegesChanged(Collections.emptySet(), Collections.emptySet());
+ mTestLooper.dispatchAll();
+
+ verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
+ }
+
+ @Test
public void testReceiveBroadcast_ConfigReadyWithSubscriptions() throws Exception {
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
mTestLooper.dispatchAll();
@@ -380,8 +503,16 @@ public class TelephonySubscriptionTrackerTest {
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
mTestLooper.dispatchAll();
- verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
+ verify(mCallback)
+ .onNewSnapshot(
+ eq(
+ buildExpectedSnapshot(
+ 0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap())));
assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+ assertNull(
+ mTelephonySubscriptionTracker
+ .getSubIdToCarrierConfigMap()
+ .get(TEST_SUBSCRIPTION_ID_1));
}
@Test
@@ -409,7 +540,7 @@ public class TelephonySubscriptionTrackerTest {
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -419,7 +550,7 @@ public class TelephonySubscriptionTrackerTest {
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index a687bb893c4a..15d4f1097108 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -56,6 +56,7 @@ import android.net.LinkProperties;
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.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -121,7 +122,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
doReturn(false).when(mDeps).isAirplaneModeOn(any());
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
@@ -135,7 +136,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
doReturn(true).when(mDeps).isAirplaneModeOn(any());
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
@@ -146,7 +147,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test
public void testNewNetworkTriggersMigration() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -158,7 +159,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test
public void testSameNetworkDoesNotTriggerMigration() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -216,14 +217,23 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
@Test
public void testMigration() throws Exception {
triggerChildOpened();
+ mTestLooper.dispatchAll();
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+
+ final IkeSessionConnectionInfo newIkeConnectionInfo =
+ new IkeSessionConnectionInfo(
+ TEST_ADDR_V4, TEST_ADDR_V4_2, TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ getIkeSessionCallback().onIkeSessionConnectionInfoChanged(newIkeConnectionInfo);
getChildSessionCallback()
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
+ assertEquals(newIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+
verify(mIpSecSvc, times(2))
.setNetworkForTunnelInterface(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
@@ -246,12 +256,16 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
MtuUtils.getMtu(
saProposals,
mConfig.getMaxMtu(),
- TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu());
+ TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu(),
+ true /* isIpv4 */);
verify(mNetworkAgent).sendLinkProperties(
argThat(lp -> expectedMtu == lp.getMtu()
&& TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes())));
verify(mNetworkAgent)
.setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_2.network)));
+
+ // Verify revalidation is triggered on VCN network
+ verify(mConnMgr).reportNetworkConnectivity(eq(mNetworkAgent.getNetwork()), eq(false));
}
private void triggerChildOpened() {
@@ -266,6 +280,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
.when(mMockChildSessionConfig)
.getInternalDnsServers();
+ getIkeSessionCallback().onOpened(mIkeSessionConfiguration);
getChildSessionCallback().onOpened(mMockChildSessionConfig);
}
@@ -295,6 +310,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
final ArgumentCaptor<LinkProperties> lpCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
@@ -315,8 +331,6 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
any(),
any());
verify(mNetworkAgent).register();
- verify(mNetworkAgent)
- .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_1.network)));
verify(mNetworkAgent).markConnected();
verify(mIpSecSvc)
@@ -331,6 +345,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
final NetworkCapabilities nc = ncCaptor.getValue();
assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertEquals(List.of(TEST_UNDERLYING_NETWORK_RECORD_1.network), nc.getUnderlyingNetworks());
for (int cap : mConfig.getAllExposedCapabilities()) {
assertTrue(nc.hasCapability(cap));
}
@@ -426,6 +441,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
mTestLooper.dispatchAll();
+ verify(mConnMgr)
+ .reportNetworkConnectivity(eq(TEST_UNDERLYING_NETWORK_RECORD_1.network), eq(false));
+
final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mDeps, times(2))
.newWakeupMessage(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index d1f3a210d870..3c70759a2fa6 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -64,7 +64,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
@Test
public void testNullNetworkTriggersDisconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
@@ -76,7 +76,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
@Test
public void testNewNetworkTriggersReconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -89,7 +89,7 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio
@Test
public void testSameNetworkDoesNotTriggerReconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 2056eea42ce6..f3eb82f46de7 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -78,7 +78,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
@Test
public void testNetworkChangesTriggerStateTransitions() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -89,7 +89,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
@Test
public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 1c859790a2fe..6568cdd44377 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -58,7 +58,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
@Test
public void testNewNetworkTriggerRetry() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
mTestLooper.dispatchAll();
@@ -72,7 +72,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
@Test
public void testSameNetworkDoesNotTriggerRetry() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
mTestLooper.dispatchAll();
@@ -86,7 +86,7 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect
@Test
public void testNullNetworkTriggersDisconnect() throws Exception {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index b8eefabea3c6..6a9a1e22cab1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -59,7 +59,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import org.junit.Before;
import org.junit.Test;
@@ -133,8 +133,9 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
capBuilder.setLinkUpstreamBandwidthKbps(TEST_UPSTREAM_BANDWIDTH);
capBuilder.setLinkDownstreamBandwidthKbps(TEST_DOWNSTREAM_BANDWIDTH);
capBuilder.setAdministratorUids(new int[] {TEST_UID});
+ final Network underlyingNetwork = mock(Network.class, CALLS_REAL_METHODS);
UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
- mock(Network.class, CALLS_REAL_METHODS),
+ underlyingNetwork,
capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
@@ -145,6 +146,7 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(vcnCaps.getUnderlyingNetworks().equals(List.of(underlyingNetwork)));
for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) {
@@ -211,7 +213,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
@@ -224,7 +227,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
@@ -236,14 +240,14 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
}
@Test
- public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
+ public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkController() {
verifyWakeLockSetUp();
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
- verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
+ verify(mUnderlyingNetworkController).updateSubscriptionSnapshot(eq(updatedSnapshot));
verifyWakeLockAcquired();
mTestLooper.dispatchAll();
@@ -254,13 +258,13 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
@Test
public void testNonNullUnderlyingNetworkRecordUpdateCancelsAlarm() {
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(null);
verifyDisconnectRequestAlarmAndGetCallback(false /* expectCanceled */);
mGatewayConnection
- .getUnderlyingNetworkTrackerCallback()
+ .getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
verify(mDisconnectRequestAlarm).cancel();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 64d0bca15ce9..785bff167ad2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -16,7 +16,6 @@
package com.android.server.vcn;
-import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
@@ -48,6 +47,8 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -62,6 +63,8 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnWakeLock;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController;
+import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
@@ -79,6 +82,13 @@ public class VcnGatewayConnectionTestBase {
doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
}
+ protected static final InetAddress TEST_ADDR = InetAddresses.parseNumericAddress("2001:db8::1");
+ protected static final InetAddress TEST_ADDR_2 =
+ InetAddresses.parseNumericAddress("2001:db8::2");
+ protected static final InetAddress TEST_ADDR_V4 =
+ InetAddresses.parseNumericAddress("192.0.2.1");
+ protected static final InetAddress TEST_ADDR_V4_2 =
+ InetAddresses.parseNumericAddress("192.0.2.2");
protected static final InetAddress TEST_DNS_ADDR =
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -128,6 +138,7 @@ public class VcnGatewayConnectionTestBase {
new TelephonySubscriptionSnapshot(
TEST_SUB_ID,
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO),
+ Collections.EMPTY_MAP,
Collections.EMPTY_MAP);
@NonNull protected final Context mContext;
@@ -137,7 +148,7 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final VcnGatewayConnectionConfig mConfig;
@NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
- @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ @NonNull protected final UnderlyingNetworkController mUnderlyingNetworkController;
@NonNull protected final VcnWakeLock mWakeLock;
@NonNull protected final WakeupMessage mTeardownTimeoutAlarm;
@NonNull protected final WakeupMessage mDisconnectRequestAlarm;
@@ -147,6 +158,9 @@ public class VcnGatewayConnectionTestBase {
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
+ @NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
+
protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
@@ -158,7 +172,7 @@ public class VcnGatewayConnectionTestBase {
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
- mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+ mUnderlyingNetworkController = mock(UnderlyingNetworkController.class);
mWakeLock = mock(VcnWakeLock.class);
mTeardownTimeoutAlarm = mock(WakeupMessage.class);
mDisconnectRequestAlarm = mock(WakeupMessage.class);
@@ -172,13 +186,17 @@ public class VcnGatewayConnectionTestBase {
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mIkeConnectionInfo =
+ new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
+ mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
+
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
- doReturn(mUnderlyingNetworkTracker)
+ doReturn(mUnderlyingNetworkController)
.when(mDeps)
- .newUnderlyingNetworkTracker(any(), any(), any(), any());
+ .newUnderlyingNetworkController(any(), any(), any(), any(), any());
doReturn(mWakeLock)
.when(mDeps)
.newWakeLock(eq(mContext), eq(PowerManager.PARTIAL_WAKE_LOCK), any());
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
new file mode 100644
index 000000000000..b0d68952c39d
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -0,0 +1,526 @@
+/*
+ * 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.vcn.routeselection;
+
+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;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
+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.checkMatchesCellPriorityRule;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
+import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
+import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnManager;
+import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
+import android.os.ParcelUuid;
+import android.os.PersistableBundle;
+import android.os.test.TestLooper;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+
+import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Set;
+import java.util.UUID;
+
+public class NetworkPriorityClassifierTest {
+ private static final String SSID = "TestWifi";
+ private static final String SSID_OTHER = "TestWifiOther";
+ private static final String PLMN_ID = "123456";
+ private static final String PLMN_ID_OTHER = "234567";
+
+ private static final int SUB_ID = 1;
+ private static final int WIFI_RSSI = -60;
+ private static final int WIFI_RSSI_HIGH = -50;
+ private static final int WIFI_RSSI_LOW = -80;
+ private static final int CARRIER_ID = 1;
+ private static final int CARRIER_ID_OTHER = 2;
+
+ private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+ private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+ private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+ private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
+ private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
+
+ private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSignalStrength(WIFI_RSSI)
+ .setSsid(SSID)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+ .build();
+
+ private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
+ new TelephonyNetworkSpecifier.Builder().setSubscriptionId(SUB_ID).build();
+ private static final NetworkCapabilities CELL_NETWORK_CAPABILITIES =
+ new NetworkCapabilities.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .setSubscriptionIds(Set.of(SUB_ID))
+ .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+ .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+ .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
+ .build();
+
+ private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
+
+ @Mock private Network mNetwork;
+ @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
+ @Mock private TelephonyManager mTelephonyManager;
+
+ private TestLooper mTestLooper;
+ private VcnContext mVcnContext;
+ private UnderlyingNetworkRecord mWifiNetworkRecord;
+ private UnderlyingNetworkRecord mCellNetworkRecord;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final Context mockContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mVcnContext =
+ spy(
+ new VcnContext(
+ mockContext,
+ mTestLooper.getLooper(),
+ mock(VcnNetworkProvider.class),
+ 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);
+ }
+
+ @Test
+ public void testMatchWithoutNotMeteredBit() {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMetered(MATCH_FORBIDDEN)
+ .build();
+
+ assertFalse(
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null /* currentlySelecetd */,
+ null /* carrierConfig */));
+ }
+
+ private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+ int entryUpstreamBandwidth,
+ int exitUpstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+ int entryDownstreamBandwidth,
+ int exitDownstreamBandwidth,
+ UnderlyingNetworkRecord currentlySelected,
+ boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinDownstreamBandwidthKbps(
+ entryDownstreamBandwidth, exitDownstreamBandwidth)
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesPriorityRule(
+ mVcnContext,
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ currentlySelected,
+ null /* carrierConfig */));
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ null /* currentlySelected */,
+ true);
+ }
+
+ @Test
+ public void testMatchWithEntryDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ null /* currentlySelected */,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitUpstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForUpstreamBandwidth(
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthEquals() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+ mWifiNetworkRecord,
+ true);
+ }
+
+ @Test
+ public void testMatchWithExitDownstreamBandwidthTooLow() {
+ verifyMatchesPriorityRuleForDownstreamBandwidth(
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+ mWifiNetworkRecord,
+ false);
+ }
+
+ private void verifyMatchWifi(
+ boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .build();
+ final UnderlyingNetworkRecord selectedNetworkRecord =
+ isSelectedNetwork ? mWifiNetworkRecord : null;
+ assertEquals(
+ expectMatch,
+ checkMatchesWifiPriorityRule(
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ selectedNetworkRecord,
+ carrierConfig == null
+ ? null
+ : new PersistableBundleWrapper(carrierConfig)));
+ }
+
+ @Test
+ public void testMatchSelectedWifi() {
+ verifyMatchWifi(
+ true /* isSelectedNetwork */, null /* carrierConfig */, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchSelectedWifiBelowRssiThreshold() {
+ final PersistableBundle carrierConfig = new PersistableBundle();
+ carrierConfig.putInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, WIFI_RSSI_HIGH);
+ carrierConfig.putInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, WIFI_RSSI_HIGH);
+
+ verifyMatchWifi(true /* isSelectedNetwork */, carrierConfig, false /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchUnselectedWifi() {
+ verifyMatchWifi(
+ false /* isSelectedNetwork */, null /* carrierConfig */, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchUnselectedWifiBelowRssiThreshold() {
+ final PersistableBundle carrierConfig = new PersistableBundle();
+ carrierConfig.putInt(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, WIFI_RSSI_HIGH);
+
+ verifyMatchWifi(false /* isSelectedNetwork */, carrierConfig, false /* expectMatch */);
+ }
+
+ private void verifyMatchWifiWithSsid(boolean useMatchedSsid, boolean expectMatch) {
+ final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
+ final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+ new VcnWifiUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
+ .setSsids(Set.of(nwPrioritySsid))
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesWifiPriorityRule(
+ wifiNetworkPriority,
+ mWifiNetworkRecord,
+ null /* currentlySelecetd */,
+ null /* carrierConfig */));
+ }
+
+ @Test
+ public void testMatchWifiWithSsid() {
+ verifyMatchWifiWithSsid(true /* useMatchedSsid */, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchWifiFailWithWrongSsid() {
+ verifyMatchWifiWithSsid(false /* useMatchedSsid */, false /* expectMatch */);
+ }
+
+ private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
+ return new VcnCellUnderlyingNetworkTemplate.Builder()
+ .setMinUpstreamBandwidthKbps(
+ TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+ .setMinDownstreamBandwidthKbps(
+ TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+ TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
+ }
+
+ @Test
+ public void testMatchMacroCell() {
+ assertTrue(
+ checkMatchesCellPriorityRule(
+ mVcnContext,
+ getCellNetworkPriorityBuilder().build(),
+ mCellNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot));
+ }
+
+ @Test
+ public void testMatchOpportunisticCell() {
+ final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
+ getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build();
+
+ when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true);
+ when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>());
+
+ assertTrue(
+ checkMatchesCellPriorityRule(
+ mVcnContext,
+ opportunisticCellNetworkPriority,
+ mCellNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot));
+ }
+
+ private void verifyMatchMacroCellWithAllowedPlmnIds(
+ boolean useMatchedPlmnId, boolean expectMatch) {
+ final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ getCellNetworkPriorityBuilder()
+ .setOperatorPlmnIds(Set.of(networkPriorityPlmnId))
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesCellPriorityRule(
+ mVcnContext,
+ networkPriority,
+ mCellNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot));
+ }
+
+ @Test
+ public void testMatchMacroCellWithAllowedPlmnIds() {
+ verifyMatchMacroCellWithAllowedPlmnIds(true /* useMatchedPlmnId */, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchMacroCellFailWithDisallowedPlmnIds() {
+ verifyMatchMacroCellWithAllowedPlmnIds(
+ false /* useMatchedPlmnId */, false /* expectMatch */);
+ }
+
+ private void verifyMatchMacroCellWithAllowedSpecificCarrierIds(
+ boolean useMatchedCarrierId, boolean expectMatch) {
+ final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ getCellNetworkPriorityBuilder()
+ .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId))
+ .build();
+
+ assertEquals(
+ expectMatch,
+ checkMatchesCellPriorityRule(
+ mVcnContext,
+ networkPriority,
+ mCellNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot));
+ }
+
+ @Test
+ public void testMatchMacroCellWithAllowedSpecificCarrierIds() {
+ verifyMatchMacroCellWithAllowedSpecificCarrierIds(
+ true /* useMatchedCarrierId */, true /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchMacroCellFailWithDisallowedSpecificCarrierIds() {
+ verifyMatchMacroCellWithAllowedSpecificCarrierIds(
+ false /* useMatchedCarrierId */, false /* expectMatch */);
+ }
+
+ @Test
+ public void testMatchWifiFailWithoutNotRoamingBit() {
+ final VcnCellUnderlyingNetworkTemplate networkPriority =
+ getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build();
+
+ assertFalse(
+ checkMatchesCellPriorityRule(
+ mVcnContext,
+ networkPriority,
+ mCellNetworkRecord,
+ SUB_GROUP,
+ mSubscriptionSnapshot));
+ }
+
+ private void verifyCalculatePriorityClass(
+ UnderlyingNetworkRecord networkRecord, int expectedIndex) {
+ final int priorityIndex =
+ calculatePriorityClass(
+ mVcnContext,
+ networkRecord,
+ VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
+ SUB_GROUP,
+ mSubscriptionSnapshot,
+ null /* currentlySelected */,
+ null /* carrierConfig */);
+
+ assertEquals(expectedIndex, priorityIndex);
+ }
+
+ @Test
+ public void testCalculatePriorityClass() throws Exception {
+ verifyCalculatePriorityClass(mCellNetworkRecord, 2);
+ }
+
+ @Test
+ public void testCalculatePriorityClassFailToMatchAny() throws Exception {
+ final NetworkCapabilities nc =
+ 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 */);
+
+ verifyCalculatePriorityClass(wifiNetworkRecord, PRIORITY_ANY);
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 5af69b5d1bf2..fad9669911bb 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -14,9 +14,11 @@
* limitations under the License.
*/
-package com.android.server.vcn;
+package com.android.server.vcn.routeselection;
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.assertNotEquals;
@@ -40,6 +42,7 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -48,10 +51,11 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
-import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.VcnNetworkProvider;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.NetworkBringupCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
+import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
import org.junit.Before;
import org.junit.Test;
@@ -64,7 +68,7 @@ import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
-public class UnderlyingNetworkTrackerTest {
+public class UnderlyingNetworkControllerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int INITIAL_SUB_ID_1 = 1;
private static final int INITIAL_SUB_ID_2 = 2;
@@ -102,14 +106,14 @@ public class UnderlyingNetworkTrackerTest {
@Mock private TelephonyManager mTelephonyManager;
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
- @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
+ @Mock private UnderlyingNetworkControllerCallback mNetworkControllerCb;
@Mock private Network mNetwork;
@Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
- private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+ private UnderlyingNetworkController mUnderlyingNetworkController;
@Before
public void setUp() {
@@ -140,12 +144,13 @@ public class UnderlyingNetworkTrackerTest {
when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
- mUnderlyingNetworkTracker =
- new UnderlyingNetworkTracker(
+ mUnderlyingNetworkController =
+ new UnderlyingNetworkController(
mVcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(),
SUB_GROUP,
mSubscriptionSnapshot,
- mNetworkTrackerCb);
+ mNetworkControllerCb);
}
private void resetVcnContext() {
@@ -153,7 +158,8 @@ public class UnderlyingNetworkTrackerTest {
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
}
- private static LinkProperties getLinkPropertiesWithName(String iface) {
+ // Package private for use in NetworkPriorityClassifierTest
+ static LinkProperties getLinkPropertiesWithName(String iface) {
LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(iface);
return linkProperties;
@@ -181,11 +187,12 @@ public class UnderlyingNetworkTrackerTest {
mVcnNetworkProvider,
true /* isInTestMode */);
- new UnderlyingNetworkTracker(
+ new UnderlyingNetworkController(
vcnContext,
+ VcnGatewayConnectionConfigTest.buildTestConfig(),
SUB_GROUP,
mSubscriptionSnapshot,
- mNetworkTrackerCb);
+ mNetworkControllerCb);
verify(cm)
.registerNetworkCallback(
@@ -233,7 +240,7 @@ public class UnderlyingNetworkTrackerTest {
mock(TelephonySubscriptionSnapshot.class);
when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
+ mUnderlyingNetworkController.updateSubscriptionSnapshot(subscriptionUpdate);
// verify that initially-filed bringup requests are unregistered (cell + wifi)
verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3))
@@ -255,7 +262,7 @@ public class UnderlyingNetworkTrackerTest {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(netCapsSubIds)
- .setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
+ .setSignalStrength(WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
.build();
}
@@ -264,7 +271,7 @@ public class UnderlyingNetworkTrackerTest {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setSubscriptionIds(netCapsSubIds)
- .setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
+ .setSignalStrength(WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
.build();
}
@@ -304,7 +311,7 @@ public class UnderlyingNetworkTrackerTest {
@Test
public void testTeardown() {
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
// Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for
// each subId), and 1 for each of the Wifi signal strength thresholds
@@ -348,6 +355,17 @@ public class UnderlyingNetworkTrackerTest {
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
}
+ private static NetworkCapabilities buildResponseNwCaps(
+ NetworkCapabilities requestNetworkCaps, Set<Integer> netCapsSubIds) {
+ final TelephonyNetworkSpecifier telephonyNetworkSpecifier =
+ new TelephonyNetworkSpecifier.Builder()
+ .setSubscriptionId(netCapsSubIds.iterator().next())
+ .build();
+ return new NetworkCapabilities.Builder(requestNetworkCaps)
+ .setNetworkSpecifier(telephonyNetworkSpecifier)
+ .build();
+ }
+
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
@@ -358,17 +376,20 @@ public class UnderlyingNetworkTrackerTest {
UnderlyingNetworkListener cb = mUnderlyingNetworkListenerCaptor.getValue();
cb.onAvailable(mNetwork);
- cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
+
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(networkCapabilities, INITIAL_SUB_IDS);
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- networkCapabilities,
+ responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
return cb;
}
@@ -376,15 +397,17 @@ public class UnderlyingNetworkTrackerTest {
public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
- cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(UPDATED_NETWORK_CAPABILITIES, UPDATED_SUB_IDS);
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- UPDATED_NETWORK_CAPABILITIES,
+ responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -396,29 +419,33 @@ public class UnderlyingNetworkTrackerTest {
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- INITIAL_NETWORK_CAPABILITIES,
+ buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
- cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(SUSPENDED_NETWORK_CAPABILITIES, UPDATED_SUB_IDS);
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- SUSPENDED_NETWORK_CAPABILITIES,
+ responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
- cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -426,19 +453,23 @@ public class UnderlyingNetworkTrackerTest {
UnderlyingNetworkListener cb =
verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
- cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
+ final NetworkCapabilities responseNetworkCaps =
+ buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS);
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- INITIAL_NETWORK_CAPABILITIES,
+ responseNetworkCaps,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
// onSelectedUnderlyingNetworkChanged() won't be fired twice if network capabilities doesn't
// change.
- cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ cb.onCapabilitiesChanged(mNetwork, responseNetworkCaps);
+ verify(mNetworkControllerCb, times(1))
+ .onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -450,10 +481,10 @@ public class UnderlyingNetworkTrackerTest {
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
- INITIAL_NETWORK_CAPABILITIES,
+ buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS),
INITIAL_LINK_PROPERTIES,
true /* isBlocked */);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
@@ -462,29 +493,31 @@ public class UnderlyingNetworkTrackerTest {
cb.onLost(mNetwork);
- verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
+ verify(mNetworkControllerCb).onSelectedUnderlyingNetworkChanged(null);
}
@Test
public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
- cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
+ cb.onCapabilitiesChanged(
+ mNetwork, buildResponseNwCaps(INITIAL_NETWORK_CAPABILITIES, INITIAL_SUB_IDS));
- // Verify no more calls to the UnderlyingNetworkTrackerCallback when the
+ // Verify no more calls to the UnderlyingNetworkControllerCallback when the
// UnderlyingNetworkRecord does not actually change
- verifyNoMoreInteractions(mNetworkTrackerCb);
+ verifyNoMoreInteractions(mNetworkControllerCb);
}
@Test
public void testRecordTrackerCallbackNotifiedAfterTeardown() {
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
- mUnderlyingNetworkTracker.teardown();
+ mUnderlyingNetworkController.teardown();
- cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
+ cb.onCapabilitiesChanged(
+ mNetwork, buildResponseNwCaps(UPDATED_NETWORK_CAPABILITIES, INITIAL_SUB_IDS));
// Verify that the only call was during onAvailable()
- verify(mNetworkTrackerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
+ verify(mNetworkControllerCb, times(1)).onSelectedUnderlyingNetworkChanged(any());
}
// TODO (b/187991063): Add tests for network prioritization
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
index 29511f780bf6..e9e70783ebe9 100644
--- a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -46,34 +46,85 @@ import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MtuUtilsTest {
+ private void verifyUnderlyingMtuZero(boolean isIpv4) {
+ assertEquals(
+ IPV6_MIN_MTU,
+ getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */, isIpv4));
+ }
+
+ @Test
+ public void testUnderlyingMtuZeroV4() {
+ verifyUnderlyingMtuZero(true /* isIpv4 */);
+ }
+
@Test
- public void testUnderlyingMtuZero() {
+ public void testUnderlyingMtuZeroV6() {
+ verifyUnderlyingMtuZero(false /* isIpv4 */);
+ }
+
+ private void verifyClampsToMaxMtu(boolean isIpv4) {
assertEquals(
- IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+ 0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */, isIpv4));
}
@Test
- public void testClampsToMaxMtu() {
- assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+ public void testClampsToMaxMtuV4() {
+ verifyClampsToMaxMtu(true /* isIpv4 */);
}
@Test
- public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
- final List<ChildSaProposal> saProposals =
- Arrays.asList(
- new ChildSaProposal.Builder()
- .addEncryptionAlgorithm(
- ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
- .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
- .build());
+ public void testClampsToMaxMtuV6() {
+ verifyClampsToMaxMtu(false /* isIpv4 */);
+ }
+
+ private List<ChildSaProposal> buildChildSaProposalsWithNormalModeAlgo() {
+ return Arrays.asList(
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .build());
+ }
+ private void verifyNormalModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
@Test
- public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
+
+ @Test
+ public void testMtuIpv4LessThanMtuIpv6() {
+ final int actualMtuV4 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ true /* isIpv4 */);
+
+ final int actualMtuV6 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ false /* isIpv4 */);
+
+ assertTrue(actualMtuV4 < actualMtuV6);
+ }
+
+ private void verifyCombinedModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final List<ChildSaProposal> saProposals =
Arrays.asList(
new ChildSaProposal.Builder()
@@ -86,7 +137,17 @@ public class MtuUtilsTest {
.build());
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */, isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
index a44a734a2dce..9c6d85238b77 100644
--- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
@@ -18,6 +18,8 @@ package com.android.server.vcn.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.os.PersistableBundle;
@@ -211,4 +213,93 @@ public class PersistableBundleUtilsTest {
assertEquals(testInt, result);
}
+
+ private PersistableBundle getTestBundle() {
+ final PersistableBundle bundle = new PersistableBundle();
+
+ bundle.putBoolean(TEST_KEY + "Boolean", true);
+ bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false});
+ bundle.putDouble(TEST_KEY + "Double", 0.1);
+ bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3});
+ bundle.putInt(TEST_KEY + "Int", 1);
+ bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2});
+ bundle.putLong(TEST_KEY + "Long", 5L);
+ bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L});
+ bundle.putString(TEST_KEY + "String", "TEST");
+ bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"});
+ bundle.putPersistableBundle(
+ TEST_KEY + "PersistableBundle",
+ new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle())
+ .toPersistableBundle());
+
+ return bundle;
+ }
+
+ @Test
+ public void testMinimizeBundle() throws Exception {
+ final String[] minimizedKeys =
+ new String[] {
+ TEST_KEY + "Boolean",
+ TEST_KEY + "BooleanArray",
+ TEST_KEY + "Double",
+ TEST_KEY + "DoubleArray",
+ TEST_KEY + "Int",
+ TEST_KEY + "IntArray",
+ TEST_KEY + "Long",
+ TEST_KEY + "LongArray",
+ TEST_KEY + "String",
+ TEST_KEY + "StringArray",
+ TEST_KEY + "PersistableBundle"
+ };
+
+ final PersistableBundle testBundle = getTestBundle();
+ testBundle.putBoolean(TEST_KEY + "Boolean2", true);
+
+ final PersistableBundle minimized =
+ PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys);
+
+ // Verify that the minimized bundle is NOT the same in size OR values due to the extra
+ // Boolean2 key
+ assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized));
+
+ // Verify that removing the extra key from the source bundle results in equality.
+ testBundle.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized));
+ }
+
+ @Test
+ public void testToFromDiskStableBytes() throws Exception {
+ final PersistableBundle testBundle = getTestBundle();
+ final PersistableBundle result =
+ PersistableBundleUtils.fromDiskStableBytes(
+ PersistableBundleUtils.toDiskStableBytes(testBundle));
+ assertTrue(PersistableBundleUtils.isEqual(testBundle, result));
+ }
+
+ @Test
+ public void testEquality_identical() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_different() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ left.putBoolean(TEST_KEY + "Boolean2", true);
+ assertFalse(PersistableBundleUtils.isEqual(left, right));
+
+ left.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_null() throws Exception {
+ assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null));
+ assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle()));
+ assertTrue(PersistableBundleUtils.isEqual(null, null));
+ }
}