From 711e1a8bebf10f5560b3b374a0260f3d09ad97ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Terelius?= Date: Wed, 4 Dec 2024 15:54:49 +0100 Subject: [PATCH] Create a custom test launcher for android Set use_default_launcher=false in rtc_test on android Bug: webrtc:42223878 Change-Id: If05da40b420d5da8f9e0f39560eb07380ebada14 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/368921 Owners-Override: Jeremy Leconte Reviewed-by: Harald Alvestrand Reviewed-by: Jeremy Leconte Reviewed-by: Mirko Bonadei Commit-Queue: Jeremy Leconte Cr-Commit-Position: refs/heads/main@{#43505} --- BUILD.gn | 14 +- common_audio/BUILD.gn | 2 - common_video/BUILD.gn | 1 - media/BUILD.gn | 1 - modules/BUILD.gn | 7 +- modules/audio_coding/BUILD.gn | 12 +- modules/audio_device/BUILD.gn | 1 - modules/video_coding/BUILD.gn | 2 - modules/video_coding/DEPS | 3 - pc/BUILD.gn | 16 +- pc/DEPS | 3 - rtc_tools/BUILD.gn | 1 - sdk/android/BUILD.gn | 65 +++--- .../native_unittests/test_jni_onload.cc | 10 + sdk/android/src/jni/audio_device/DEPS | 1 - sdk/android/src/jni/jni_onload.cc | 2 + stats/BUILD.gn | 6 +- system_wrappers/BUILD.gn | 2 - test/BUILD.gn | 35 ++- test/DEPS | 2 + test/android/AndroidManifest.xml | 7 +- test/android/native_test_launcher.cc | 123 ++++++++++ test/android/native_test_launcher.h | 26 +++ test/android/native_test_util.cc | 148 ++++++++++++ test/android/native_test_util.h | 63 ++++++ .../webrtc/native_test/NativeTestWebrtc.java | 210 ++++++++++++++++++ .../native_test/RTCNativeTestApplication.java | 26 +++ .../webrtc/native_test/RTCNativeUnitTest.java | 39 +++- .../webrtc/native_test/StrictModeContext.java | 122 ++++++++++ webrtc.gni | 4 + 30 files changed, 851 insertions(+), 103 deletions(-) create mode 100644 test/android/native_test_launcher.cc create mode 100644 test/android/native_test_launcher.h create mode 100644 test/android/native_test_util.cc create mode 100644 test/android/native_test_util.h create mode 100644 test/android/org/webrtc/native_test/NativeTestWebrtc.java create mode 100644 test/android/org/webrtc/native_test/RTCNativeTestApplication.java create mode 100644 test/android/org/webrtc/native_test/StrictModeContext.java diff --git a/BUILD.gn b/BUILD.gn index b3c84aa9b0..d829c0fccc 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -695,7 +695,6 @@ if (rtc_include_tests && !build_with_chromium) { deps += [ "sdk/android:native_unittests", "sdk/android:native_unittests_java", - "//testing/android/native_test:native_test_support", ] shard_timeout = 900 } @@ -744,11 +743,7 @@ if (rtc_include_tests && !build_with_chromium) { data = video_engine_tests_resources if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] shard_timeout = 900 } if (is_ios) { @@ -793,11 +788,7 @@ if (rtc_include_tests && !build_with_chromium) { data = webrtc_perf_tests_resources if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] shard_timeout = 4500 } if (is_ios) { @@ -809,7 +800,6 @@ if (rtc_include_tests && !build_with_chromium) { testonly = true deps = [ "rtc_base:rtc_base_nonparallel_tests" ] if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] shard_timeout = 900 } } diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn index 1342eefd4f..4845bc71ef 100644 --- a/common_audio/BUILD.gn +++ b/common_audio/BUILD.gn @@ -388,8 +388,6 @@ if (rtc_include_tests && !build_with_chromium) { ] if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] - shard_timeout = 900 } } diff --git a/common_video/BUILD.gn b/common_video/BUILD.gn index 4cff5700e4..4c4c8c90d0 100644 --- a/common_video/BUILD.gn +++ b/common_video/BUILD.gn @@ -219,7 +219,6 @@ if (rtc_include_tests && !build_with_chromium) { data = common_video_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] shard_timeout = 900 } diff --git a/media/BUILD.gn b/media/BUILD.gn index 1b8f4cc12d..7a27f40ffe 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -1074,7 +1074,6 @@ if (rtc_include_tests) { data = rtc_media_unittests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] shard_timeout = 900 } diff --git a/modules/BUILD.gn b/modules/BUILD.gn index 52bd86f6d5..76fcf085f2 100644 --- a/modules/BUILD.gn +++ b/modules/BUILD.gn @@ -83,8 +83,6 @@ if (rtc_include_tests && !build_with_chromium) { # rtc_test targets. Therefore we include this target here, instead of # in video_coding_modules_tests, where it is actually used. "../sdk/android:libjingle_peerconnection_java", - "//sdk/android:native_test_jni_onload", - "//testing/android/native_test:native_test_support", ] shard_timeout = 900 } @@ -238,10 +236,7 @@ if (rtc_include_tests && !build_with_chromium) { if (is_android) { use_default_launcher = false - deps += [ - "../sdk/android:libjingle_peerconnection_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "../sdk/android:libjingle_peerconnection_java" ] shard_timeout = 900 } if (is_ios) { diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index bdc4860716..3511b8ffed 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -1003,11 +1003,7 @@ if (rtc_include_tests) { if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] shard_timeout = 900 } if (is_ios) { @@ -1099,11 +1095,7 @@ if (rtc_include_tests) { if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] shard_timeout = 900 } diff --git a/modules/audio_device/BUILD.gn b/modules/audio_device/BUILD.gn index b69ada7f31..aa4d216b28 100644 --- a/modules/audio_device/BUILD.gn +++ b/modules/audio_device/BUILD.gn @@ -493,7 +493,6 @@ if (rtc_include_tests && !build_with_chromium) { "../../sdk/android:internal_jni", "../../sdk/android:libjingle_peerconnection_java", "../../sdk/android:native_api_jni", - "../../sdk/android:native_test_jni_onload", "../utility", ] } diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn index c5c6ce13dd..2318bcdaa3 100644 --- a/modules/video_coding/BUILD.gn +++ b/modules/video_coding/BUILD.gn @@ -1000,8 +1000,6 @@ if (rtc_include_tests) { deps += [ ":android_codec_factory_helper", "../../sdk/android:hwcodecs_java", - "//sdk/android:native_test_jni_onload", - "//testing/android/native_test:native_test_support", ] shard_timeout = 900 } diff --git a/modules/video_coding/DEPS b/modules/video_coding/DEPS index d62707c2f9..49c640bd54 100644 --- a/modules/video_coding/DEPS +++ b/modules/video_coding/DEPS @@ -11,9 +11,6 @@ include_rules = [ ] specific_include_rules = { - "android_codec_factory_helper\.cc": [ - "+base/android", - ], "multiplex_encoder_adapter\.cc": [ "+media/base", ], diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 719334951a..c39cf40fe4 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -2098,11 +2098,7 @@ if (rtc_include_tests && !build_with_chromium) { if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] } } @@ -2448,8 +2444,7 @@ if (rtc_include_tests && !build_with_chromium) { # We need to depend on this one directly, or classloads will fail for # the voice engine BuildInfo, for instance. - "//sdk/android:libjingle_peerconnection_java", - "//sdk/android:native_test_jni_onload", + "../sdk/android:libjingle_peerconnection_java", ] shard_timeout = 900 } @@ -2519,12 +2514,11 @@ if (rtc_include_tests && !build_with_chromium) { "test/android_test_initializer.h", ] deps = [ + "../modules/utility:utility", + "../rtc_base:checks", "../rtc_base:ssl_adapter", "../sdk/android:internal_jni", "../sdk/android:libjingle_peerconnection_jni", - "//modules/utility:utility", - "//rtc_base:checks", - "//testing/android/native_test:native_test_support", ] } } @@ -2818,7 +2812,6 @@ if (rtc_include_tests && !build_with_chromium) { sources = [ "test/svc_e2e_tests.cc" ] data = svc_tests_resources deps = [ - "..//test/network:simulated_network", "../api:create_network_emulation_manager", "../api:create_peer_connection_quality_test_frame_generator", "../api:create_peerconnection_quality_test_fixture", @@ -2843,6 +2836,7 @@ if (rtc_include_tests && !build_with_chromium) { "../test:fileutils", "../test:test_main", "../test:test_support", + "../test/network:simulated_network", "../test/pc/e2e:network_quality_metrics_reporter", "../test/pc/e2e/analyzer/video:default_video_quality_analyzer", ] diff --git a/pc/DEPS b/pc/DEPS index 36439a3e07..227cf27e39 100644 --- a/pc/DEPS +++ b/pc/DEPS @@ -18,9 +18,6 @@ include_rules = [ ] specific_include_rules = { - "androidtestinitializer\.cc": [ - "+base/android", # Allowed only for Android tests. - ], "srtpfilter_unittest\.cc": [ "+crypto", ], diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index 68521ba2bd..8f8e355b77 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -614,7 +614,6 @@ if (rtc_include_tests) { data = tools_unittests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] shard_timeout = 900 } if (is_ios) { diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index a051da0bec..8295d2f8f6 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -91,7 +91,7 @@ if (is_android) { ":surfaceviewrenderer_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -140,6 +140,7 @@ if (is_android) { ":native_api_jni", ":video_egl_jni", "../../pc:libjingle_peerconnection", + "../../rtc_base:logging", "../../rtc_base:ssl_adapter", "//third_party/jni_zero", ] @@ -169,11 +170,13 @@ if (is_android) { "src/java/org/webrtc/WebRtcClassLoader.java", ] + srcjar_deps = [ "//sdk/android:generated_native_api_jni" ] + deps = [ ":generated_base_jni_java", ":generated_native_api_jni_java", ":generated_rtcerror_jni_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -188,7 +191,7 @@ if (is_android) { deps = [ ":base_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -210,7 +213,7 @@ if (is_android) { deps = [ ":base_java", ":generated_video_jni_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] srcjar_deps = [ "//api/video:video_frame_enums" ] @@ -258,7 +261,7 @@ if (is_android) { ":generated_video_egl_jni_java", ":generated_video_jni_java", ":video_api_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -335,7 +338,7 @@ if (is_android) { ":swcodecs_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] srcjar_deps = [ @@ -366,7 +369,7 @@ if (is_android) { ":base_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -398,7 +401,7 @@ if (is_android) { ":base_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -426,7 +429,7 @@ if (is_android) { ":base_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -450,7 +453,7 @@ if (is_android) { ":generated_audio_device_module_base_jni_java", ":generated_java_audio_device_module_native_jni_java", ":generated_java_audio_jni_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -475,7 +478,7 @@ if (is_android) { deps = [ ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -491,7 +494,7 @@ if (is_android) { ":base_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -506,7 +509,7 @@ if (is_android) { ":generated_libvpx_vp8_jni_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -521,7 +524,7 @@ if (is_android) { ":generated_libvpx_vp9_jni_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -533,7 +536,7 @@ if (is_android) { ":generated_libaom_av1_encoder_jni_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -561,7 +564,7 @@ if (is_android) { ":libvpx_vp9_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/androidx:androidx_annotation_annotation_java", ] } @@ -970,9 +973,9 @@ if (current_os == "linux" || is_android) { ":generated_external_classes_jni", ":generated_native_api_jni", ":internal_jni", + "../../api:array_view", "../../api:sequence_checker", - "//api:array_view", - "//rtc_base:checks", + "../../rtc_base:checks", "//third_party/jni_zero", ] } @@ -987,7 +990,7 @@ if (current_os == "linux" || is_android) { deps = [ ":base_jni", ":native_api_jni", - "//rtc_base:checks", + "../../rtc_base:checks", ] } @@ -1035,8 +1038,8 @@ if (current_os == "linux" || is_android) { ":base_jni", ":native_api_jni", ":video_jni", - "//api/video_codecs:video_codecs_api", - "//rtc_base:checks", + "../../api/video_codecs:video_codecs_api", + "../../rtc_base:checks", "//third_party/jni_zero", ] } @@ -1067,9 +1070,9 @@ if (current_os == "linux" || is_android) { deps = [ ":base_jni", ":peerconnection_jni", + "../../api:libjingle_peerconnection_api", + "../../api/video_codecs:video_codecs_api", "../../rtc_base:threading", - "//api:libjingle_peerconnection_api", - "//api/video_codecs:video_codecs_api", ] } @@ -1105,12 +1108,12 @@ if (current_os == "linux" || is_android) { ":native_api_jni", ":video_jni", ":videoframe_jni", + "../../api:libjingle_peerconnection_api", + "../../api:media_stream_interface", + "../../api/video:video_frame", + "../../api/video:video_rtp_headers", "../../rtc_base:refcount", "../../rtc_base:threading", - "//api:libjingle_peerconnection_api", - "//api:media_stream_interface", - "//api/video:video_frame", - "//api/video:video_rtp_headers", "//third_party/jni_zero", ] } @@ -1125,7 +1128,7 @@ if (current_os == "linux" || is_android) { deps = [ ":base_java", ":generated_logging_jni_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", ] } @@ -1571,7 +1574,7 @@ if (is_android) { ":swcodecs_java", ":video_api_java", ":video_java", - "//rtc_base:base_java", + "../../rtc_base:base_java", "//third_party/android_deps:guava_android_java", "//third_party/androidx:androidx_annotation_annotation_java", "//third_party/androidx:androidx_test_monitor_java", @@ -1603,6 +1606,7 @@ if (is_android) { ":libjingle_peerconnection_metrics_default_jni", ":native_api_jni", "../../pc:libjingle_peerconnection", + "../../rtc_base:logging", "../../rtc_base:ssl_adapter", ] output_extension = "so" @@ -1631,11 +1635,13 @@ if (is_android) { sources = [ "native_unittests/test_jni_onload.cc" ] deps = [ + ":base_java", ":base_jni", ":internal_jni", ":native_api_base", ":native_api_jni", "../../rtc_base:checks", + "../../rtc_base:logging", ] } @@ -1668,7 +1674,6 @@ if (is_android) { ":native_api_peerconnection", ":native_api_stacktrace", ":native_api_video", - ":native_test_jni_onload", ":opensles_audio_device_module", ":video_jni", "../../api:enable_media_with_defaults", diff --git a/sdk/android/native_unittests/test_jni_onload.cc b/sdk/android/native_unittests/test_jni_onload.cc index dafe49c474..91b2f2ea4a 100644 --- a/sdk/android/native_unittests/test_jni_onload.cc +++ b/sdk/android/native_unittests/test_jni_onload.cc @@ -13,11 +13,21 @@ #define JNIEXPORT __attribute__((visibility("default"))) #include "rtc_base/checks.h" +#include "rtc_base/logging.h" #include "sdk/android/native_api/base/init.h" #include "sdk/android/native_api/jni/java_types.h" +#include "test/android/native_test_launcher.h" // nogncheck // This is called by the VM when the shared library is first loaded. JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + RTC_LOG(LS_INFO) << "Entering JNI_OnLoad in test_jni_onload.cc"; + + jni_zero::InitVM(vm); + // TODO(webrtc:42223878): Set exception handler? + // jni_zero::SetExceptionHandler(CheckException); + // JNIEnv* env = jni_zero::AttachCurrentThread(); + // TODO(webrtc:42223878): Classloader, OOM error handler? webrtc::InitAndroid(vm); + webrtc::test::android::InstallHandlers(); return JNI_VERSION_1_4; } diff --git a/sdk/android/src/jni/audio_device/DEPS b/sdk/android/src/jni/audio_device/DEPS index 9a3adee687..7ee1155e03 100644 --- a/sdk/android/src/jni/audio_device/DEPS +++ b/sdk/android/src/jni/audio_device/DEPS @@ -1,4 +1,3 @@ include_rules = [ - "+base/android/jni_android.h", "+modules/audio_device", ] diff --git a/sdk/android/src/jni/jni_onload.cc b/sdk/android/src/jni/jni_onload.cc index a1829ad0b1..d23cd9266a 100644 --- a/sdk/android/src/jni/jni_onload.cc +++ b/sdk/android/src/jni/jni_onload.cc @@ -12,6 +12,7 @@ #undef JNIEXPORT #define JNIEXPORT __attribute__((visibility("default"))) +#include "rtc_base/logging.h" #include "rtc_base/ssl_adapter.h" #include "sdk/android/native_api/jni/class_loader.h" #include "sdk/android/src/jni/jni_helpers.h" @@ -20,6 +21,7 @@ namespace webrtc { namespace jni { extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved) { + RTC_LOG(LS_INFO) << "Entering JNI_OnLoad in jni_onload.cc"; jint ret = InitGlobalJniVariables(jvm); RTC_DCHECK_GE(ret, 0); if (ret < 0) diff --git a/stats/BUILD.gn b/stats/BUILD.gn index ab4be8939e..5a48d85c79 100644 --- a/stats/BUILD.gn +++ b/stats/BUILD.gn @@ -68,11 +68,7 @@ if (rtc_include_tests && !build_with_chromium) { if (is_android) { use_default_launcher = false - deps += [ - "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", - "//testing/android/native_test:native_test_java", - "//testing/android/native_test:native_test_support", - ] + deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java" ] } } } diff --git a/system_wrappers/BUILD.gn b/system_wrappers/BUILD.gn index 1afd20a7e6..786c469689 100644 --- a/system_wrappers/BUILD.gn +++ b/system_wrappers/BUILD.gn @@ -154,8 +154,6 @@ if (rtc_include_tests && !build_with_chromium) { ] if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] - shard_timeout = 900 } } diff --git a/test/BUILD.gn b/test/BUILD.gn index 09bfbf8785..bfe3cc3279 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -10,6 +10,7 @@ import("//build/config/ui.gni") import("../webrtc.gni") if (is_android) { import("//build/config/android/rules.gni") + import("//third_party/jni_zero/jni_zero.gni") } if (!build_with_chromium) { @@ -798,7 +799,6 @@ if (rtc_include_tests) { data = test_support_unittests_resources if (is_android) { - deps += [ "//testing/android/native_test:native_test_support" ] shard_timeout = 900 } @@ -1301,14 +1301,43 @@ if (!build_with_chromium && is_android) { rtc_android_library("native_test_java") { testonly = true sources = [ + "android/org/webrtc/native_test/NativeTestWebrtc.java", + "android/org/webrtc/native_test/RTCNativeTestApplication.java", "android/org/webrtc/native_test/RTCNativeUnitTest.java", "android/org/webrtc/native_test/RTCNativeUnitTestActivity.java", + "android/org/webrtc/native_test/StrictModeContext.java", + ] + srcjar_deps = [ ":native_test_jni" ] + deps = [ + ":native_test_support", + "../rtc_base:base_java", + "//build/android:build_java", + "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + "//testing/android/reporter:reporter_java", + "//third_party/jni_zero:jni_zero_java", + ] + } + + source_set("native_test_support") { + testonly = true + sources = [ + "android/native_test_launcher.cc", + "android/native_test_launcher.h", + "android/native_test_util.cc", + "android/native_test_util.h", ] deps = [ - "../rtc_base:base_java", - "//testing/android/native_test:native_test_java", + ":native_test_jni", + "//testing/gtest", + "//third_party/abseil-cpp/absl/strings", + "//third_party/jni_zero:jni_zero", ] } + + generate_jni("native_test_jni") { + testonly = true + sources = [ "android/org/webrtc/native_test/NativeTestWebrtc.java" ] + } } rtc_library("call_config_utils") { diff --git a/test/DEPS b/test/DEPS index 95193e753a..497c79ab9c 100644 --- a/test/DEPS +++ b/test/DEPS @@ -1,6 +1,8 @@ include_rules = [ "+third_party/libjpeg", "+third_party/libjpeg_turbo", + "+third_party/jni_zero", + "+absl/strings/str_split.h", "+call", "+common_audio", "+common_video", diff --git a/test/android/AndroidManifest.xml b/test/android/AndroidManifest.xml index 04ab33c610..478e4dd1f7 100644 --- a/test/android/AndroidManifest.xml +++ b/test/android/AndroidManifest.xml @@ -24,13 +24,18 @@ be found in the AUTHORS file in the root of the source tree. + + + + android:name="org.webrtc.native_test.RTCNativeTestApplication"> diff --git a/test/android/native_test_launcher.cc b/test/android/native_test_launcher.cc new file mode 100644 index 0000000000..89135a007e --- /dev/null +++ b/test/android/native_test_launcher.cc @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Based on Chromium's +// https://source.chromium.org/chromium/chromium/src/+/main:testing/android/native_test/native_test_launcher.cc +// and Angle's +// https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/tests/test_utils/runner/android/AngleNativeTest.cpp + +// This class sets up the environment for running the native tests inside an +// android application. It outputs (to a fifo) markers identifying the +// START/PASSED/CRASH of the test suite, FAILURE/SUCCESS of individual tests, +// etc. +// These markers are read by the test runner script to generate test results. +// It installs signal handlers to detect crashes. + +#include "test/android/native_test_launcher.h" + +#include +#include +#include +#include + +#include +#include + +#include "test/android/native_test_util.h" +#include "test/native_test_jni/NativeTestWebrtc_jni.h" +#include "third_party/jni_zero/jni_zero.h" + +// The main function of the program to be wrapped as a test apk. +extern int main(int argc, char** argv); + +namespace webrtc { +namespace test { +namespace android { + +namespace { + +const char kCrashedMarker[] = "[ CRASHED ]\n"; + +// The list of signals which are considered to be crashes. +const int kExceptionSignals[] = {SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1}; + +struct sigaction g_old_sa[NSIG]; + +// This function runs in a compromised context. It should not allocate memory. +void SignalHandler(int sig, siginfo_t* info, void* reserved) { + // Output the crash marker. + write(STDOUT_FILENO, kCrashedMarker, sizeof(kCrashedMarker) - 1); + g_old_sa[sig].sa_sigaction(sig, info, reserved); +} + +} // namespace + +static void JNI_NativeTestWebrtc_RunTests(JNIEnv* env, + std::string& command_line_flags, + std::string& command_line_file_path, + std::string& stdout_file_path, + std::string& test_data_dir) { + AndroidLog( + ANDROID_LOG_INFO, + "Entering JNI_NativeTestWebrtc_RunTests with command_line_flags=%s, " + "command_line_file_path=%s, stdout_file_path=%s, test_data_dir=%s\n", + command_line_flags.c_str(), command_line_file_path.c_str(), + stdout_file_path.c_str(), test_data_dir.c_str()); + + // Required for DEATH_TESTS. + pthread_atfork(nullptr, nullptr, jni_zero::DisableJvmForTesting); + + std::vector args; + + if (command_line_file_path.empty()) + args.push_back("_"); + else + ParseArgsFromCommandLineFile(command_line_file_path.c_str(), &args); + + ParseArgsFromString(command_line_flags, &args); + + std::vector argv; + int argc = ArgsToArgv(args, &argv); + + // A few options, such "--gtest_list_tests", will just use printf directly + // Always redirect stdout to a known file. + if (freopen(stdout_file_path.c_str(), "a+", stdout) == NULL) { + AndroidLog(ANDROID_LOG_ERROR, "Failed to redirect stream to file: %s: %s\n", + stdout_file_path.c_str(), strerror(errno)); + exit(EXIT_FAILURE); + } + // TODO(jbudorick): Remove this after resolving crbug.com/726880 + AndroidLog(ANDROID_LOG_INFO, "Redirecting stdout to file: %s\n", + stdout_file_path.c_str()); + dup2(STDOUT_FILENO, STDERR_FILENO); + + // TODO(webrtc:42223878): Wait for debugger. + + ScopedMainEntryLogger scoped_main_entry_logger; + main(argc, &argv[0]); +} + +// TODO(nileshagrawal): now that we're using FIFO, test scripts can detect EOF. +// Remove the signal handlers. +void InstallHandlers() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_SIGINFO; + + for (unsigned int i = 0; kExceptionSignals[i] != -1; ++i) { + sigaction(kExceptionSignals[i], &sa, &g_old_sa[kExceptionSignals[i]]); + } +} + +} // namespace android +} // namespace test +} // namespace webrtc diff --git a/test/android/native_test_launcher.h b/test/android/native_test_launcher.h new file mode 100644 index 0000000000..75e476133a --- /dev/null +++ b/test/android/native_test_launcher.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TEST_ANDROID_NATIVE_TEST_LAUNCHER_H_ +#define TEST_ANDROID_NATIVE_TEST_LAUNCHER_H_ + +#include + +namespace webrtc { +namespace test { +namespace android { + +void InstallHandlers(); + +} // namespace android +} // namespace test +} // namespace webrtc + +#endif // TEST_ANDROID_NATIVE_TEST_LAUNCHER_H_ diff --git a/test/android/native_test_util.cc b/test/android/native_test_util.cc new file mode 100644 index 0000000000..529ffbe651 --- /dev/null +++ b/test/android/native_test_util.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Based on Chromium's +// https://source.chromium.org/chromium/chromium/src/+/main:testing/android/native_test/native_test_util.cc +// and Angle's +// https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/tests/test_utils/runner/android/AngleNativeTest.cpp + +#include "test/android/native_test_util.h" + +#include +#include + +#include +#include + +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" + +namespace webrtc { +namespace test { +namespace android { + +namespace { + +const char kLogTag[] = "webrtc"; + +std::optional ReadFileToString(const char* path) { + FILE* file = fopen(path, "rb"); + if (!file) { + AndroidLog(ANDROID_LOG_ERROR, "Failed to open %s\n", path); + return std::nullopt; + } + + if (fseek(file, 0, SEEK_END) != 0) { + return std::nullopt; + } + + auto size = ftell(file); + // Check that `size` fits in a 32-bit int, to avoid any issues with overflow + // in the subsequent casts. (For example, consider the case where long is >32 + // bits while size_t is 32 bits.) We're not expecting the command line to be + // larger than 1 GB, anyway. + if (size < 0 || size > 1'000'000'000) { + AndroidLog(ANDROID_LOG_ERROR, + "Expected size of %s between 0 and 1 GB, got %ld bytes\n", path, + size); + return std::nullopt; + } + + std::string contents; + contents.resize(size); + + fseek(file, 0, SEEK_SET); + + if (fread(contents.data(), 1, size, file) != static_cast(size)) { + return std::nullopt; + } + + if (ferror(file)) { + return std::nullopt; + } + + return contents; +} + +} // namespace + +// Writes printf() style string to Android's logger where |priority| is one of +// the levels defined in . +void AndroidLog(int priority, const char* format, ...) { + va_list args; + va_start(args, format); + __android_log_vprint(priority, kLogTag, format, args); + va_end(args); +} + +std::string ASCIIJavaStringToUTF8(JNIEnv* env, jstring str) { + if (!str) { + return ""; + } + + const jsize length = env->GetStringLength(str); + if (!length) { + return ""; + } + + // JNI's GetStringUTFChars() returns strings in Java "modified" UTF8, so + // instead get the String in UTF16. As the input is ASCII, drop the higher + // bytes. + const jchar* jchars = env->GetStringChars(str, NULL); + const char16_t* chars = reinterpret_cast(jchars); + std::string out(chars, chars + length); + env->ReleaseStringChars(str, jchars); + return out; +} + +void ParseArgsFromString(const std::string& command_line, + std::vector* args) { + std::vector v = + absl::StrSplit(command_line, absl::ByAsciiWhitespace()); + for (absl::string_view arg : v) { + args->push_back(std::string(arg)); + } + + // TODO(webrtc:42223878): Implement tokenization that handle quotes and + // escaped quotes (along the lines of the previous chromium code): + // + // base::StringTokenizer tokenizer(command_line, base::kWhitespaceASCII); + // tokenizer.set_quote_chars("\""); + // while (tokenizer.GetNext()) { + // std::string token; + // base::RemoveChars(tokenizer.token(), "\"", &token); + // args->push_back(token); + // } +} + +void ParseArgsFromCommandLineFile(const char* path, + std::vector* args) { + std::optional command_line_string = ReadFileToString(path); + if (command_line_string.has_value()) { + ParseArgsFromString(*command_line_string, args); + } +} + +int ArgsToArgv(const std::vector& args, std::vector* argv) { + // We need to pass in a non-const char**. + int argc = args.size(); + + argv->resize(argc + 1); + for (int i = 0; i < argc; ++i) { + (*argv)[i] = const_cast(args[i].c_str()); + } + (*argv)[argc] = NULL; // argv must be NULL terminated. + + return argc; +} + +} // namespace android +} // namespace test +} // namespace webrtc diff --git a/test/android/native_test_util.h b/test/android/native_test_util.h new file mode 100644 index 0000000000..ace87589f4 --- /dev/null +++ b/test/android/native_test_util.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef TEST_ANDROID_NATIVE_TEST_UTIL_H_ +#define TEST_ANDROID_NATIVE_TEST_UTIL_H_ + +#include + +#include +#include + +#include "third_party/jni_zero/jni_zero.h" + +// Helper methods for setting up environment for running gtest tests +// inside an APK. +namespace webrtc { +namespace test { +namespace android { + +void AndroidLog(int priority, const char* format, ...); + +std::string ASCIIJavaStringToUTF8(JNIEnv* env, jstring str); + +void ParseArgsFromString(const std::string& command_line, + std::vector* args); +void ParseArgsFromCommandLineFile(const char* path, + std::vector* args); +int ArgsToArgv(const std::vector& args, std::vector* argv); + +class ScopedMainEntryLogger { + public: + ScopedMainEntryLogger() { + AndroidLog(ANDROID_LOG_INFO, ">>ScopedMainEntryLogger\n"); + } + + ~ScopedMainEntryLogger() { + AndroidLog(ANDROID_LOG_INFO, "< +inline std::string FromJniType(JNIEnv* env, + const JavaRef& input) { + return webrtc::test::android::ASCIIJavaStringToUTF8( + env, static_cast(input.obj())); +} +} // namespace jni_zero + +#endif // TEST_ANDROID_NATIVE_TEST_UTIL_H_ diff --git a/test/android/org/webrtc/native_test/NativeTestWebrtc.java b/test/android/org/webrtc/native_test/NativeTestWebrtc.java new file mode 100644 index 0000000000..352f9c8b8e --- /dev/null +++ b/test/android/org/webrtc/native_test/NativeTestWebrtc.java @@ -0,0 +1,210 @@ +/* + * Copyright 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Based on Chromium's https://source.chromium.org/chromium/chromium/src/+/main:testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java +// and Angle's https://source.chromium.org/chromium/chromium/src/+/main:third_party/angle/src/tests/test_utils/runner/android/java/src/com/android/angle/test/AngleNativeTest.java + +package org.webrtc.native_test; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.Process; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import org.jni_zero.JNINamespace; +import org.jni_zero.JniType; +import org.jni_zero.NativeMethods; + +import org.webrtc.native_test.StrictModeContext; +import org.chromium.build.gtest_apk.NativeTestIntent; +import org.chromium.test.reporter.TestStatusReporter; + +import java.io.File; + +/** Helper to run tests inside Activity or NativeActivity. */ +@JNINamespace("webrtc::test::android") +public class NativeTestWebrtc { + private static final String TAG = "NativeTestWebrtc"; + + private String mCommandLineFilePath; + private StringBuilder mCommandLineFlags = new StringBuilder(); + private TestStatusReporter mReporter; + private boolean mRunInSubThread; + private String mStdoutFilePath; + + private static class ReportingUncaughtExceptionHandler + implements Thread.UncaughtExceptionHandler { + + private TestStatusReporter mReporter; + private Thread.UncaughtExceptionHandler mWrappedHandler; + + public ReportingUncaughtExceptionHandler( + TestStatusReporter reporter, Thread.UncaughtExceptionHandler wrappedHandler) { + mReporter = reporter; + mWrappedHandler = wrappedHandler; + } + + @Override + public void uncaughtException(Thread thread, Throwable ex) { + mReporter.uncaughtException(Process.myPid(), ex); + if (mWrappedHandler != null) mWrappedHandler.uncaughtException(thread, ex); + } + } + + public void preCreate(Activity activity) { + String coverageDeviceFile = + activity.getIntent().getStringExtra(NativeTestIntent.EXTRA_COVERAGE_DEVICE_FILE); + if (coverageDeviceFile != null) { + try { + Os.setenv("LLVM_PROFILE_FILE", coverageDeviceFile, true); + } catch (ErrnoException e) { + Log.w(TAG, "failed to set LLVM_PROFILE_FILE" + e.toString()); + } + } + // Set TMPDIR to make perfetto_unittests not to use /data/local/tmp as a tmp directory. + try { + Os.setenv("TMPDIR", activity.getApplicationContext().getCacheDir().getPath(), false); + } catch (ErrnoException e) { + Log.w(TAG, "failed to set TMPDIR" + e.toString()); + } + } + + public void postCreate(Activity activity) { + parseArgumentsFromIntent(activity, activity.getIntent()); + mReporter = new TestStatusReporter(activity); + mReporter.testRunStarted(Process.myPid()); + Thread.setDefaultUncaughtExceptionHandler( + new ReportingUncaughtExceptionHandler( + mReporter, Thread.getDefaultUncaughtExceptionHandler())); + } + + private void parseArgumentsFromIntent(Activity activity, Intent intent) { + Log.i(TAG, "Extras:"); + Bundle extras = intent.getExtras(); + if (extras != null) { + for (String s : extras.keySet()) { + Log.i(TAG, " " + s); + } + } + + mCommandLineFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FILE); + if (mCommandLineFilePath == null) { + mCommandLineFilePath = ""; + } else { + File commandLineFile = new File(mCommandLineFilePath); + if (!commandLineFile.isAbsolute()) { + mCommandLineFilePath = + Environment.getExternalStorageDirectory() + "/" + mCommandLineFilePath; + } + Log.i(TAG, "command line file path: " + mCommandLineFilePath); + } + + String commandLineFlags = intent.getStringExtra(NativeTestIntent.EXTRA_COMMAND_LINE_FLAGS); + if (commandLineFlags != null) mCommandLineFlags.append(commandLineFlags); + + mRunInSubThread = intent.hasExtra(NativeTestIntent.EXTRA_RUN_IN_SUB_THREAD); + + String gtestFilter = intent.getStringExtra(NativeTestIntent.EXTRA_GTEST_FILTER); + if (gtestFilter != null) { + appendCommandLineFlags("--gtest_filter=" + gtestFilter); + } + + mStdoutFilePath = intent.getStringExtra(NativeTestIntent.EXTRA_STDOUT_FILE); + } + + public void appendCommandLineFlags(String flags) { + mCommandLineFlags.append(" ").append(flags); + } + + public void postStart(final Activity activity, boolean forceRunInSubThread) { + final Runnable runTestsTask = + new Runnable() { + @Override + public void run() { + runTests(activity); + } + }; + + if (mRunInSubThread || forceRunInSubThread) { + // Post a task that posts a task that creates a new thread and runs tests on it. + + // On L and M, the system posts a task to the main thread that prints to stdout + // from android::Layout (https://goo.gl/vZA38p). Chaining the subthread creation + // through multiple tasks executed on the main thread ensures that this task + // runs before we start running tests s.t. its output doesn't interfere with + // the test output. See crbug.com/678146 for additional context. + + final Handler handler = new Handler(); + final Runnable startTestThreadTask = + new Runnable() { + @Override + public void run() { + new Thread(runTestsTask).start(); + } + }; + final Runnable postTestStarterTask = + new Runnable() { + @Override + public void run() { + handler.post(startTestThreadTask); + } + }; + handler.post(postTestStarterTask); + } else { + // Post a task to run the tests. This allows us to not block + // onCreate and still run tests on the main thread. + new Handler().post(runTestsTask); + } + } + + private void runTests(Activity activity) { + Natives jni = NativeTestWebrtcJni.get(); + String isolated_test_root = NativeTestWebrtc.getIsolatedTestRoot(); + Log.i(TAG, "Calling into native code"); + jni.runTests( + mCommandLineFlags.toString(), + mCommandLineFilePath, + mStdoutFilePath, + isolated_test_root); + Log.i(TAG, "Call into native code returned"); + activity.finish(); + mReporter.testRunFinished(Process.myPid()); + } + + + // @CalledByNative + public static String getIsolatedTestRoot() { + try (StrictModeContext ignored = StrictModeContext.allowDiskReads()) { + return Environment.getExternalStorageDirectory().getAbsolutePath() + + "/chromium_tests_root"; + } + } + + // Signal a failure of the native test loader to python scripts + // which run tests. For example, we look for + // RUNNER_FAILED build/android/test_package.py. + private void nativeTestFailed() { + Log.e(TAG, "[ RUNNER_FAILED ] could not load native library"); + } + + @NativeMethods + interface Natives { + void runTests( + @JniType("std::string") String commandLineFlags, + @JniType("std::string") String commandLineFilePath, + @JniType("std::string") String stdoutFilePath, + @JniType("std::string") String testDataDir); + } +} \ No newline at end of file diff --git a/test/android/org/webrtc/native_test/RTCNativeTestApplication.java b/test/android/org/webrtc/native_test/RTCNativeTestApplication.java new file mode 100644 index 0000000000..ce2cf4fbc0 --- /dev/null +++ b/test/android/org/webrtc/native_test/RTCNativeTestApplication.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.native_test; + +import android.app.Application; +import android.content.Context; + +/** Application class to be used by native_test apks. */ +public class RTCNativeTestApplication extends Application { + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + assert getBaseContext() != null; + + // This is required for Mockito to initialize mocks without running under Instrumentation. + System.setProperty("org.mockito.android.target", getCacheDir().getPath()); + } +} \ No newline at end of file diff --git a/test/android/org/webrtc/native_test/RTCNativeUnitTest.java b/test/android/org/webrtc/native_test/RTCNativeUnitTest.java index dede7edd1f..329e1e8b38 100644 --- a/test/android/org/webrtc/native_test/RTCNativeUnitTest.java +++ b/test/android/org/webrtc/native_test/RTCNativeUnitTest.java @@ -11,16 +11,39 @@ package org.webrtc.native_test; import android.app.Activity; -import org.chromium.native_test.NativeUnitTest; +import android.util.Log; +import org.chromium.build.NativeLibraries; +import org.webrtc.native_test.NativeTestWebrtc; import org.webrtc.ContextUtils; /** * Native unit test that calls ContextUtils.initialize for WebRTC. */ -public class RTCNativeUnitTest extends NativeUnitTest { - @Override - public void preCreate(Activity activity) { - super.preCreate(activity); - ContextUtils.initialize(activity.getApplicationContext()); - } -} +public class RTCNativeUnitTest extends NativeTestWebrtc { + + private static final String TAG = "RTCNativeUnitTest"; + + private static final String LIBRARY_UNDER_TEST_NAME = + "org.chromium.native_test.NativeTestInstrumentationTestRunner.LibraryUnderTest"; + + @Override + public void preCreate(Activity activity) { + super.preCreate(activity); + + // For NativeActivity based tests, dependency libraries must be loaded before + // NativeActivity::OnCreate, otherwise loading android.app.lib_name will fail + String libraryToLoad = activity.getIntent().getStringExtra(LIBRARY_UNDER_TEST_NAME); + loadLibraries( + libraryToLoad != null ? new String[] {libraryToLoad} : NativeLibraries.LIBRARIES); + + ContextUtils.initialize(activity.getApplicationContext()); + } + + private void loadLibraries(String[] librariesToLoad) { + for (String library : librariesToLoad) { + Log.i(TAG, "loading: " + library); + System.loadLibrary(library); + Log.i(TAG, "loaded: " + library); + } + } +} \ No newline at end of file diff --git a/test/android/org/webrtc/native_test/StrictModeContext.java b/test/android/org/webrtc/native_test/StrictModeContext.java new file mode 100644 index 0000000000..24e036cb79 --- /dev/null +++ b/test/android/org/webrtc/native_test/StrictModeContext.java @@ -0,0 +1,122 @@ +/* + * Copyright 2024 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Based on Chromium's https://source.chromium.org/chromium/chromium/src/+/main:base/android/java/src/org/chromium/base/StrictModeContext.java + +package org.webrtc.native_test; + +import android.os.Build; +import android.os.StrictMode; + +import java.io.Closeable; + +/** + * Enables try-with-resources compatible StrictMode violation allowlisting. + * + *

Prefer "ignored" as the variable name to appease Android Studio's "Unused symbol" inspection. + * + *

Example: + * + *

+ *     try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
+ *         return Example.doThingThatRequiresDiskWrites();
+ *     }
+ * 
+ */ +public class StrictModeContext implements Closeable { + private static class Impl extends StrictModeContext { + private final StrictMode.ThreadPolicy mThreadPolicy; + private final StrictMode.VmPolicy mVmPolicy; + + private Impl(StrictMode.ThreadPolicy threadPolicy, StrictMode.VmPolicy vmPolicy) { + mThreadPolicy = threadPolicy; + mVmPolicy = vmPolicy; + } + + private Impl(StrictMode.ThreadPolicy threadPolicy) { + this(threadPolicy, null); + } + + private Impl(StrictMode.VmPolicy vmPolicy) { + this(null, vmPolicy); + } + + @Override + public void close() { + if (mThreadPolicy != null) { + StrictMode.setThreadPolicy(mThreadPolicy); + } + if (mVmPolicy != null) { + StrictMode.setVmPolicy(mVmPolicy); + } + } + } + + /** + * Convenience method for disabling all VM-level StrictMode checks with try-with-resources. + * Includes everything listed here: + * https://developer.android.com/reference/android/os/StrictMode.VmPolicy.Builder.html + */ + public static StrictModeContext allowAllVmPolicies() { + StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); + StrictMode.setVmPolicy(StrictMode.VmPolicy.LAX); + return new Impl(oldPolicy); + } + + /** + * Convenience method for disabling all thread-level StrictMode checks with try-with-resources. + * Includes everything listed here: + * https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder.html + */ + public static StrictModeContext allowAllThreadPolicies() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX); + return new Impl(oldPolicy); + } + + /** Convenience method for disabling StrictMode for disk-writes with try-with-resources. */ + public static StrictModeContext allowDiskWrites() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); + return new Impl(oldPolicy); + } + + /** Convenience method for disabling StrictMode for disk-reads with try-with-resources. */ + public static StrictModeContext allowDiskReads() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + return new Impl(oldPolicy); + } + + /** Convenience method for disabling StrictMode for slow calls with try-with-resources. */ + public static StrictModeContext allowSlowCalls() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + StrictMode.setThreadPolicy( + new StrictMode.ThreadPolicy.Builder(oldPolicy).permitCustomSlowCalls().build()); + return new Impl(oldPolicy); + } + + /** + * Convenience method for disabling StrictMode for unbuffered input/output operations with + * try-with-resources. For API level 25- this method will do nothing; because + * StrictMode.ThreadPolicy.Builder#permitUnbufferedIo is added in API level 26. + */ + public static StrictModeContext allowUnbufferedIo() { + StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + StrictMode.setThreadPolicy( + new StrictMode.ThreadPolicy.Builder(oldPolicy) + .permitUnbufferedIo() + .build()); + } + return new Impl(oldPolicy); + } + + @Override + public void close() {} +} \ No newline at end of file diff --git a/webrtc.gni b/webrtc.gni index 6a09451900..ba5c39da20 100644 --- a/webrtc.gni +++ b/webrtc.gni @@ -525,13 +525,17 @@ template("rtc_test") { public_configs += invoker.public_configs } if (!build_with_chromium && is_android) { + use_default_launcher = false android_manifest = webrtc_root + "test/android/AndroidManifest.xml" use_raw_android_executable = false min_sdk_version = 21 target_sdk_version = 23 deps += [ "//build/android/gtest_apk:native_test_instrumentation_test_runner_java", + webrtc_root + "sdk/android:native_test_jni_onload", + webrtc_root + "sdk/android:base_java", webrtc_root + "test:native_test_java", + webrtc_root + "test:native_test_support", ] }