Revert "Android: Generate JNI code for stats"

This reverts commit aede67a199ae0552074bfec4bb03cc9a6a5fba0f.

Reason for revert: Causes error:
JNI ERROR (app bug): local reference table overflow (max=512)'

Original change's description:
> Android: Generate JNI code for stats
> 
> This CL also unifies the functions for converting from C++ to Java, and
> generates the boiler plate for converting C++ vectors to Java arrays.
> 
> Bug: webrtc:8278
> Change-Id: I262e9162beae8a64ba0e8b6a27e1081207b03961
> Reviewed-on: https://webrtc-review.googlesource.com/26020
> Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#20918}

TBR=magjed@webrtc.org,sakal@webrtc.org

Change-Id: Ieb26ed8577bd489a4dd4f7542d16a7d0e11f409f
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: webrtc:8278
Reviewed-on: https://webrtc-review.googlesource.com/26900
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20926}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index c7427da..c7738cc 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -42,14 +42,7 @@
 }
 
 generate_jar_jni("generated_external_classes_jni") {
-  classes = [
-    "java/lang/Integer.class",
-    "java/lang/Double.class",
-    "java/lang/Long.class",
-    "java/lang/Boolean.class",
-    "java/math/BigInteger.class",
-    "java/lang/String.class",
-  ]
+  classes = [ "java/lang/Integer.class" ]
   jni_package = ""
   jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
 }
@@ -301,8 +294,6 @@
     "api/org/webrtc/RTCStats.java",
     "api/org/webrtc/RTCStatsCollectorCallback.java",
     "api/org/webrtc/RTCStatsReport.java",
-    "api/org/webrtc/StatsObserver.java",
-    "api/org/webrtc/StatsReport.java",
   ]
   jni_package = ""
   jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
@@ -366,7 +357,6 @@
 
   deps = [
     ":base_jni",
-    ":generated_external_classes_jni",
     ":generated_peerconnection_jni",
     "../..:webrtc_common",
     "../../api/video_codecs:video_codecs_api",
diff --git a/sdk/android/api/org/webrtc/IceCandidate.java b/sdk/android/api/org/webrtc/IceCandidate.java
index eebc9d9..51865e9 100644
--- a/sdk/android/api/org/webrtc/IceCandidate.java
+++ b/sdk/android/api/org/webrtc/IceCandidate.java
@@ -49,4 +49,9 @@
   String getSdp() {
     return sdp;
   }
+
+  @CalledByNative
+  static IceCandidate[] createArray(int size) {
+    return new IceCandidate[size];
+  }
 }
diff --git a/sdk/android/api/org/webrtc/StatsObserver.java b/sdk/android/api/org/webrtc/StatsObserver.java
index b9984c1..b1ad0de 100644
--- a/sdk/android/api/org/webrtc/StatsObserver.java
+++ b/sdk/android/api/org/webrtc/StatsObserver.java
@@ -13,5 +13,5 @@
 /** Interface for observing Stats reports (see webrtc::StatsObservers). */
 public interface StatsObserver {
   /** Called when the reports are ready.*/
-  @CalledByNative public void onComplete(StatsReport[] reports);
+  public void onComplete(StatsReport[] reports);
 }
diff --git a/sdk/android/api/org/webrtc/StatsReport.java b/sdk/android/api/org/webrtc/StatsReport.java
index b8f1cf8..b5396fc 100644
--- a/sdk/android/api/org/webrtc/StatsReport.java
+++ b/sdk/android/api/org/webrtc/StatsReport.java
@@ -17,7 +17,6 @@
     public final String name;
     public final String value;
 
-    @CalledByNative("Value")
     public Value(String name, String value) {
       this.name = name;
       this.value = value;
@@ -37,7 +36,6 @@
   public final double timestamp;
   public final Value[] values;
 
-  @CalledByNative
   public StatsReport(String id, String type, double timestamp, Value[] values) {
     this.id = id;
     this.type = type;
diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc
index b1f8fda..a175a0f 100644
--- a/sdk/android/src/jni/classreferenceholder.cc
+++ b/sdk/android/src/jni/classreferenceholder.cc
@@ -51,7 +51,11 @@
 
 ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
   LoadClass(jni, "android/graphics/SurfaceTexture");
+  LoadClass(jni, "java/lang/Boolean");
+  LoadClass(jni, "java/lang/Double");
+  LoadClass(jni, "java/lang/Long");
   LoadClass(jni, "java/lang/String");
+  LoadClass(jni, "java/math/BigInteger");
   LoadClass(jni, "java/nio/ByteBuffer");
   LoadClass(jni, "java/util/ArrayList");
   LoadClass(jni, "java/util/LinkedHashMap");
diff --git a/sdk/android/src/jni/encodedimage.cc b/sdk/android/src/jni/encodedimage.cc
index 81c5caa..966ef93 100644
--- a/sdk/android/src/jni/encodedimage.cc
+++ b/sdk/android/src/jni/encodedimage.cc
@@ -13,7 +13,6 @@
 #include "common_video/include/video_frame.h"
 #include "rtc_base/timeutils.h"
 #include "sdk/android/generated_video_jni/jni/EncodedImage_jni.h"
-#include "sdk/android/src/jni/jni_helpers.h"
 
 namespace webrtc {
 namespace jni {
@@ -25,21 +24,12 @@
 jobject NativeToJavaEncodedImage(JNIEnv* jni, const EncodedImage& image) {
   jobject buffer = jni->NewDirectByteBuffer(image._buffer, image._length);
   jobject frame_type = NativeToJavaFrameType(jni, image._frameType);
-  jobject qp =
-      (image.qp_ == -1) ? nullptr : NativeToJavaInteger(jni, image.qp_);
+  jobject qp = (image.qp_ == -1) ? nullptr : JavaIntegerFromInt(jni, image.qp_);
   return Java_EncodedImage_Constructor(
       jni, buffer, image._encodedWidth, image._encodedHeight,
       image.capture_time_ms_ * rtc::kNumNanosecsPerMillisec, frame_type,
       static_cast<jint>(image.rotation_), image._completeFrame, qp);
 }
 
-jobjectArray NativeToJavaFrameTypeArray(
-    JNIEnv* env,
-    const std::vector<FrameType>& frame_types) {
-  return NativeToJavaObjectArray(
-      env, frame_types, org_webrtc_EncodedImage_00024FrameType_clazz(env),
-      &NativeToJavaFrameType);
-}
-
 }  // namespace jni
 }  // namespace webrtc
diff --git a/sdk/android/src/jni/encodedimage.h b/sdk/android/src/jni/encodedimage.h
index 563b0d0..a84b91e 100644
--- a/sdk/android/src/jni/encodedimage.h
+++ b/sdk/android/src/jni/encodedimage.h
@@ -13,6 +13,7 @@
 
 #include <jni.h>
 
+#include "api/video/video_rotation.h"
 #include "common_types.h"  // NOLINT(build/include)
 
 namespace webrtc {
@@ -23,9 +24,6 @@
 
 jobject NativeToJavaFrameType(JNIEnv* env, FrameType frame_type);
 jobject NativeToJavaEncodedImage(JNIEnv* jni, const EncodedImage& image);
-jobjectArray NativeToJavaFrameTypeArray(
-    JNIEnv* env,
-    const std::vector<FrameType>& frame_types);
 
 }  // namespace jni
 }  // namespace webrtc
diff --git a/sdk/android/src/jni/jni_helpers.cc b/sdk/android/src/jni/jni_helpers.cc
index 694ebea..fa8adc3 100644
--- a/sdk/android/src/jni/jni_helpers.cc
+++ b/sdk/android/src/jni/jni_helpers.cc
@@ -15,10 +15,7 @@
 #include <unistd.h>
 #include <vector>
 
-#include "sdk/android/generated_external_classes_jni/jni/Boolean_jni.h"
-#include "sdk/android/generated_external_classes_jni/jni/Double_jni.h"
 #include "sdk/android/generated_external_classes_jni/jni/Integer_jni.h"
-#include "sdk/android/generated_external_classes_jni/jni/Long_jni.h"
 #include "sdk/android/src/jni/class_loader.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
 
@@ -236,6 +233,13 @@
   return jni->IsSameObject(obj, nullptr);
 }
 
+// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
+jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
+  jstring jstr = jni->NewStringUTF(native.c_str());
+  CHECK_EXCEPTION(jni) << "error during NewStringUTF";
+  return jstr;
+}
+
 // Given a jstring, reinterprets it to a new native string.
 std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
   // Invoke String.getBytes(String charsetName) method to convert |j_string|
@@ -271,39 +275,21 @@
   return converted_list;
 }
 
-rtc::Optional<int32_t> JavaToNativeOptionalInt(JNIEnv* jni, jobject integer) {
+rtc::Optional<int32_t> JavaIntegerToOptionalInt(JNIEnv* jni, jobject integer) {
   if (IsNull(jni, integer))
     return rtc::nullopt;
   return JNI_Integer::Java_Integer_intValue(jni, integer);
 }
 
-jobject NativeToJavaBoolean(JNIEnv* env, bool b) {
-  return JNI_Boolean::Java_Boolean_ConstructorJLB_Z(env, b);
+jobject JavaIntegerFromOptionalInt(JNIEnv* jni,
+                                   const rtc::Optional<int32_t>& optional_int) {
+  return optional_int ? JavaIntegerFromInt(jni, *optional_int) : nullptr;
 }
 
-jobject NativeToJavaInteger(JNIEnv* jni, int32_t i) {
+jobject JavaIntegerFromInt(JNIEnv* jni, int32_t i) {
   return JNI_Integer::Java_Integer_ConstructorJLI_I(jni, i);
 }
 
-jobject NativeToJavaLong(JNIEnv* env, int64_t u) {
-  return JNI_Long::Java_Long_ConstructorJLLO_J(env, u);
-}
-
-jobject NativeToJavaDouble(JNIEnv* env, double d) {
-  return JNI_Double::Java_Double_ConstructorJLD_D(env, d);
-}
-
-jstring NativeToJavaString(JNIEnv* jni, const std::string& native) {
-  jstring jstr = jni->NewStringUTF(native.c_str());
-  CHECK_EXCEPTION(jni) << "error during NewStringUTF";
-  return jstr;
-}
-
-jobject NativeToJavaInteger(JNIEnv* jni,
-                            const rtc::Optional<int32_t>& optional_int) {
-  return optional_int ? NativeToJavaInteger(jni, *optional_int) : nullptr;
-}
-
 // Return the (singleton) Java Enum object corresponding to |index|;
 jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
                           const std::string& state_class_name, int index) {
@@ -467,39 +453,5 @@
   return jni_ == nullptr || IsNull(jni_, iterator_);
 }
 
-jobjectArray NativeToJavaIntegerArray(JNIEnv* env,
-                                      const std::vector<int32_t>& container) {
-  jobject (*convert_function)(JNIEnv*, int32_t) = &NativeToJavaInteger;
-  return NativeToJavaObjectArray(env, container, java_lang_Integer_clazz(env),
-                                 convert_function);
-}
-
-jobjectArray NativeToJavaBooleanArray(JNIEnv* env,
-                                      const std::vector<bool>& container) {
-  return NativeToJavaObjectArray(env, container, java_lang_Boolean_clazz(env),
-                                 &NativeToJavaBoolean);
-}
-
-jobjectArray NativeToJavaDoubleArray(JNIEnv* env,
-                                     const std::vector<double>& container) {
-  return NativeToJavaObjectArray(env, container, java_lang_Double_clazz(env),
-                                 &NativeToJavaDouble);
-}
-
-jobjectArray NativeToJavaLongArray(JNIEnv* env,
-                                   const std::vector<int64_t>& container) {
-  return NativeToJavaObjectArray(env, container, java_lang_Long_clazz(env),
-                                 &NativeToJavaLong);
-}
-
-jobjectArray NativeToJavaStringArray(
-    JNIEnv* env,
-    const std::vector<std::string>& container) {
-  // TODO(magjed): Remove this class when we can generate it from String.class
-  // directly (the script currently chokes on that class).
-  return NativeToJavaObjectArray(
-      env, container, FindClass(env, "java/lang/String"), &NativeToJavaString);
-}
-
 }  // namespace jni
 }  // namespace webrtc
diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h
index bfef3a8..e18edc2 100644
--- a/sdk/android/src/jni/jni_helpers.h
+++ b/sdk/android/src/jni/jni_helpers.h
@@ -98,6 +98,9 @@
 // Returns true if |obj| == null in Java.
 bool IsNull(JNIEnv* jni, jobject obj);
 
+// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
+jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native);
+
 // Given a (UTF-16) jstring return a new UTF-8 native string.
 std::string JavaToStdString(JNIEnv* jni, const jstring& j_string);
 
@@ -105,16 +108,12 @@
 // return a new vector of UTF-8 native strings.
 std::vector<std::string> JavaToStdVectorStrings(JNIEnv* jni, jobject list);
 
-rtc::Optional<int32_t> JavaToNativeOptionalInt(JNIEnv* jni, jobject integer);
+rtc::Optional<int32_t> JavaIntegerToOptionalInt(JNIEnv* jni, jobject integer);
 
-jobject NativeToJavaBoolean(JNIEnv* env, bool b);
-jobject NativeToJavaInteger(JNIEnv* jni, int32_t i);
-jobject NativeToJavaLong(JNIEnv* env, int64_t u);
-jobject NativeToJavaDouble(JNIEnv* env, double d);
-// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
-jstring NativeToJavaString(JNIEnv* jni, const std::string& native);
-jobject NativeToJavaInteger(JNIEnv* jni,
-                            const rtc::Optional<int32_t>& optional_int);
+jobject JavaIntegerFromOptionalInt(JNIEnv* jni,
+                                   const rtc::Optional<int32_t>& optional_int);
+
+jobject JavaIntegerFromInt(JNIEnv* jni, int32_t i);
 
 // Return the (singleton) Java Enum object corresponding to |index|;
 jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
@@ -229,37 +228,6 @@
   RTC_DISALLOW_COPY_AND_ASSIGN(Iterable);
 };
 
-// Helper function for converting std::vector<T> into a Java array.
-template <typename T, typename Convert>
-jobjectArray NativeToJavaObjectArray(JNIEnv* env,
-                                     const std::vector<T>& container,
-                                     jclass clazz,
-                                     Convert convert) {
-  jobjectArray j_container =
-      env->NewObjectArray(container.size(), clazz, nullptr);
-  int i = 0;
-  for (const T& element : container) {
-    jobject j_element = convert(env, element);
-    env->SetObjectArrayElement(j_container, i, j_element);
-    // Delete local ref immediately since we might create a lot of local
-    // references in this loop.
-    env->DeleteLocalRef(j_element);
-    ++i;
-  }
-  return j_container;
-}
-
-jobjectArray NativeToJavaIntegerArray(JNIEnv* env,
-                                      const std::vector<int32_t>& container);
-jobjectArray NativeToJavaBooleanArray(JNIEnv* env,
-                                      const std::vector<bool>& container);
-jobjectArray NativeToJavaLongArray(JNIEnv* env,
-                                   const std::vector<int64_t>& container);
-jobjectArray NativeToJavaDoubleArray(JNIEnv* env,
-                                     const std::vector<double>& container);
-jobjectArray NativeToJavaStringArray(JNIEnv* env,
-                                     const std::vector<std::string>& container);
-
 }  // namespace jni
 }  // namespace webrtc
 
diff --git a/sdk/android/src/jni/pc/datachannel.cc b/sdk/android/src/jni/pc/datachannel.cc
index c4be431..758e4b9 100644
--- a/sdk/android/src/jni/pc/datachannel.cc
+++ b/sdk/android/src/jni/pc/datachannel.cc
@@ -111,7 +111,7 @@
                          DataChannel_label,
                          JNIEnv* jni,
                          jobject j_dc) {
-  return NativeToJavaString(jni, ExtractNativeDC(jni, j_dc)->label());
+  return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
 }
 
 JNI_FUNCTION_DECLARATION(jint, DataChannel_id, JNIEnv* jni, jobject j_dc) {
diff --git a/sdk/android/src/jni/pc/dtmfsender_jni.cc b/sdk/android/src/jni/pc/dtmfsender_jni.cc
index c8d5493..a21314a 100644
--- a/sdk/android/src/jni/pc/dtmfsender_jni.cc
+++ b/sdk/android/src/jni/pc/dtmfsender_jni.cc
@@ -40,7 +40,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_dtmf_sender_pointer) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni,
       reinterpret_cast<DtmfSenderInterface*>(j_dtmf_sender_pointer)->tones());
 }
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 3f0e2e1..ee4640b 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -22,14 +22,15 @@
 
 namespace {
 
-jobject CreateJavaIceCandidate(JNIEnv* env,
-                               const std::string& sdp_mid,
-                               int sdp_mline_index,
-                               const std::string& sdp,
-                               const std::string server_url) {
+jobject NativeToJavaCandidate(JNIEnv* env,
+                              const std::string& sdp_mid,
+                              int sdp_mline_index,
+                              const std::string& sdp,
+                              const std::string server_url) {
   return Java_IceCandidate_Constructor(
-      env, NativeToJavaString(env, sdp_mid), sdp_mline_index,
-      NativeToJavaString(env, sdp), NativeToJavaString(env, server_url));
+      env, JavaStringFromStdString(env, sdp_mid), sdp_mline_index,
+      JavaStringFromStdString(env, sdp),
+      JavaStringFromStdString(env, server_url));
 }
 
 }  // namespace
@@ -60,26 +61,31 @@
   std::string sdp = SdpSerializeCandidate(candidate);
   RTC_CHECK(!sdp.empty()) << "got an empty ICE candidate";
   // sdp_mline_index is not used, pass an invalid value -1.
-  return CreateJavaIceCandidate(env, candidate.transport_name(),
-                                -1 /* sdp_mline_index */, sdp,
-                                "" /* server_url */);
+  return NativeToJavaCandidate(env, candidate.transport_name(),
+                               -1 /* sdp_mline_index */, sdp,
+                               "" /* server_url */);
 }
 
-jobject NativeToJavaIceCandidate(JNIEnv* env,
-                                 const IceCandidateInterface& candidate) {
+jobject NativeToJavaCandidate(JNIEnv* env,
+                              const IceCandidateInterface& candidate) {
   std::string sdp;
   RTC_CHECK(candidate.ToString(&sdp)) << "got so far: " << sdp;
-  return CreateJavaIceCandidate(env, candidate.sdp_mid(),
-                                candidate.sdp_mline_index(), sdp,
-                                candidate.candidate().url());
+  return NativeToJavaCandidate(env, candidate.sdp_mid(),
+                               candidate.sdp_mline_index(), sdp,
+                               candidate.candidate().url());
 }
 
 jobjectArray NativeToJavaCandidateArray(
     JNIEnv* jni,
     const std::vector<cricket::Candidate>& candidates) {
-  return NativeToJavaObjectArray(jni, candidates,
-                                 org_webrtc_IceCandidate_clazz(jni),
-                                 &NativeToJavaCandidate);
+  jobjectArray java_candidates =
+      Java_IceCandidate_createArray(jni, candidates.size());
+  int i = 0;
+  for (const cricket::Candidate& candidate : candidates) {
+    jobject j_candidate = NativeToJavaCandidate(jni, candidate);
+    jni->SetObjectArrayElement(java_candidates, i++, j_candidate);
+  }
+  return java_candidates;
 }
 
 SessionDescriptionInterface* JavaToNativeSessionDescription(JNIEnv* jni,
@@ -108,13 +114,13 @@
     const SessionDescriptionInterface* desc) {
   std::string sdp;
   RTC_CHECK(desc->ToString(&sdp)) << "got so far: " << sdp;
-  jstring j_description = NativeToJavaString(jni, sdp);
+  jstring j_description = JavaStringFromStdString(jni, sdp);
 
   jclass j_type_class = FindClass(jni, "org/webrtc/SessionDescription$Type");
   jmethodID j_type_from_canonical = GetStaticMethodID(
       jni, j_type_class, "fromCanonicalForm",
       "(Ljava/lang/String;)Lorg/webrtc/SessionDescription$Type;");
-  jstring j_type_string = NativeToJavaString(jni, desc->type());
+  jstring j_type_string = JavaStringFromStdString(jni, desc->type());
   jobject j_type = jni->CallStaticObjectMethod(
       j_type_class, j_type_from_canonical, j_type_string);
   CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
@@ -452,7 +458,7 @@
   jobject j_ice_check_min_interval =
       GetNullableObjectField(jni, j_rtc_config, j_ice_check_min_interval_id);
   rtc_config->ice_check_min_interval =
-      JavaToNativeOptionalInt(jni, j_ice_check_min_interval);
+      JavaIntegerToOptionalInt(jni, j_ice_check_min_interval);
   rtc_config->disable_ipv6_on_wifi =
       GetBooleanField(jni, j_rtc_config, j_disable_ipv6_on_wifi_id);
   rtc_config->max_ipv6_networks =
@@ -499,7 +505,7 @@
     encoding.active = GetBooleanField(jni, j_encoding_parameters, active_id);
     jobject j_bitrate =
         GetNullableObjectField(jni, j_encoding_parameters, bitrate_id);
-    encoding.max_bitrate_bps = JavaToNativeOptionalInt(jni, j_bitrate);
+    encoding.max_bitrate_bps = JavaIntegerToOptionalInt(jni, j_bitrate);
     jobject j_ssrc =
         GetNullableObjectField(jni, j_encoding_parameters, ssrc_id);
     if (!IsNull(jni, j_ssrc)) {
@@ -529,10 +535,10 @@
     codec.kind =
         JavaToNativeMediaType(jni, GetObjectField(jni, j_codec, kind_id));
     jobject j_clock_rate = GetNullableObjectField(jni, j_codec, clock_rate_id);
-    codec.clock_rate = JavaToNativeOptionalInt(jni, j_clock_rate);
+    codec.clock_rate = JavaIntegerToOptionalInt(jni, j_clock_rate);
     jobject j_num_channels =
         GetNullableObjectField(jni, j_codec, num_channels_id);
-    codec.num_channels = JavaToNativeOptionalInt(jni, j_num_channels);
+    codec.num_channels = JavaIntegerToOptionalInt(jni, j_num_channels);
     parameters->codecs.push_back(codec);
   }
 }
@@ -568,8 +574,9 @@
     CHECK_EXCEPTION(jni) << "error during NewObject";
     jni->SetBooleanField(j_encoding_parameters, active_id, encoding.active);
     CHECK_EXCEPTION(jni) << "error during SetBooleanField";
-    jni->SetObjectField(j_encoding_parameters, bitrate_id,
-                        NativeToJavaInteger(jni, encoding.max_bitrate_bps));
+    jni->SetObjectField(
+        j_encoding_parameters, bitrate_id,
+        JavaIntegerFromOptionalInt(jni, encoding.max_bitrate_bps));
     if (encoding.ssrc) {
       jobject j_ssrc_value = jni->NewObject(long_class, long_ctor,
                                             static_cast<jlong>(*encoding.ssrc));
@@ -605,15 +612,16 @@
     CHECK_EXCEPTION(jni) << "error during NewObject";
     jni->SetIntField(j_codec, payload_type_id, codec.payload_type);
     CHECK_EXCEPTION(jni) << "error during SetIntField";
-    jni->SetObjectField(j_codec, name_id, NativeToJavaString(jni, codec.name));
+    jni->SetObjectField(j_codec, name_id,
+                        JavaStringFromStdString(jni, codec.name));
     CHECK_EXCEPTION(jni) << "error during SetObjectField";
     jni->SetObjectField(j_codec, kind_id,
                         NativeToJavaMediaType(jni, codec.kind));
     CHECK_EXCEPTION(jni) << "error during SetObjectField";
     jni->SetObjectField(j_codec, clock_rate_id,
-                        NativeToJavaInteger(jni, codec.clock_rate));
+                        JavaIntegerFromOptionalInt(jni, codec.clock_rate));
     jni->SetObjectField(j_codec, num_channels_id,
-                        NativeToJavaInteger(jni, codec.num_channels));
+                        JavaIntegerFromOptionalInt(jni, codec.num_channels));
     jboolean added = jni->CallBooleanMethod(j_codecs, codecs_add, j_codec);
     CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
     RTC_CHECK(added);
diff --git a/sdk/android/src/jni/pc/java_native_conversion.h b/sdk/android/src/jni/pc/java_native_conversion.h
index faa4084..cb9f861 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.h
+++ b/sdk/android/src/jni/pc/java_native_conversion.h
@@ -38,8 +38,8 @@
 
 jobject NativeToJavaCandidate(JNIEnv* env, const cricket::Candidate& candidate);
 
-jobject NativeToJavaIceCandidate(JNIEnv* env,
-                                 const IceCandidateInterface& candidate);
+jobject NativeToJavaCandidate(JNIEnv* env,
+                              const IceCandidateInterface& candidate);
 
 jobjectArray NativeToJavaCandidateArray(
     JNIEnv* jni,
diff --git a/sdk/android/src/jni/pc/mediastream_jni.cc b/sdk/android/src/jni/pc/mediastream_jni.cc
index 92f4c9d..010f355 100644
--- a/sdk/android/src/jni/pc/mediastream_jni.cc
+++ b/sdk/android/src/jni/pc/mediastream_jni.cc
@@ -59,7 +59,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_p) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni, reinterpret_cast<MediaStreamInterface*>(j_p)->label());
 }
 
diff --git a/sdk/android/src/jni/pc/mediastreamtrack_jni.cc b/sdk/android/src/jni/pc/mediastreamtrack_jni.cc
index 52c3794..0983bba 100644
--- a/sdk/android/src/jni/pc/mediastreamtrack_jni.cc
+++ b/sdk/android/src/jni/pc/mediastreamtrack_jni.cc
@@ -19,7 +19,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_p) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());
 }
 
@@ -28,7 +28,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_p) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->kind());
 }
 
diff --git a/sdk/android/src/jni/pc/peerconnection_jni.cc b/sdk/android/src/jni/pc/peerconnection_jni.cc
index c3f2d12..075f382 100644
--- a/sdk/android/src/jni/pc/peerconnection_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnection_jni.cc
@@ -350,9 +350,9 @@
                          jobject j_current,
                          jobject j_max) {
   PeerConnectionInterface::BitrateParameters params;
-  params.min_bitrate_bps = JavaToNativeOptionalInt(jni, j_min);
-  params.current_bitrate_bps = JavaToNativeOptionalInt(jni, j_current);
-  params.max_bitrate_bps = JavaToNativeOptionalInt(jni, j_max);
+  params.min_bitrate_bps = JavaIntegerToOptionalInt(jni, j_min);
+  params.current_bitrate_bps = JavaIntegerToOptionalInt(jni, j_current);
+  params.max_bitrate_bps = JavaIntegerToOptionalInt(jni, j_max);
   return ExtractNativePC(jni, j_pc)->SetBitrate(params).ok();
 }
 
diff --git a/sdk/android/src/jni/pc/peerconnectionfactory_jni.cc b/sdk/android/src/jni/pc/peerconnectionfactory_jni.cc
index 5882243..c42ed54 100644
--- a/sdk/android/src/jni/pc/peerconnectionfactory_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnectionfactory_jni.cc
@@ -104,7 +104,7 @@
                          JNIEnv* jni,
                          jclass,
                          jstring j_name) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni, field_trial::FindFullName(JavaToStdString(jni, j_name)));
 }
 
diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
index 4ad107e..3da15fa 100644
--- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
@@ -16,7 +16,6 @@
 #include "rtc_base/ptr_util.h"
 #include "sdk/android/generated_peerconnection_jni/jni/MediaStream_jni.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
-#include "sdk/android/src/jni/jni_helpers.h"
 #include "sdk/android/src/jni/pc/datachannel.h"
 #include "sdk/android/src/jni/pc/java_native_conversion.h"
 
@@ -49,7 +48,7 @@
     const IceCandidateInterface* candidate) {
   JNIEnv* env = AttachCurrentThreadIfNeeded();
   ScopedLocalRefFrame local_ref_frame(env);
-  jobject j_candidate = NativeToJavaIceCandidate(env, *candidate);
+  jobject j_candidate = NativeToJavaCandidate(env, *candidate);
 
   jmethodID m = GetMethodID(env, *j_observer_class_, "onIceCandidate",
                             "(Lorg/webrtc/IceCandidate;)V");
diff --git a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
index f4d0b81..2e892e5 100644
--- a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
+++ b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
@@ -13,8 +13,6 @@
 #include <string>
 #include <vector>
 
-#include "rtc_base/stringencode.h"
-#include "sdk/android/generated_external_classes_jni/jni/BigInteger_jni.h"
 #include "sdk/android/generated_peerconnection_jni/jni/RTCStatsCollectorCallback_jni.h"
 #include "sdk/android/generated_peerconnection_jni/jni/RTCStatsReport_jni.h"
 #include "sdk/android/generated_peerconnection_jni/jni/RTCStats_jni.h"
@@ -23,86 +21,11 @@
 namespace webrtc {
 namespace jni {
 
-namespace {
-
-jobject NativeToJavaBigInteger(JNIEnv* env, uint64_t u) {
-  return JNI_BigInteger::Java_BigInteger_ConstructorJMBI_JLS(
-      env, NativeToJavaString(env, rtc::ToString(u)));
-}
-
-jobjectArray NativeToJavaBigIntegerArray(
-    JNIEnv* env,
-    const std::vector<uint64_t>& container) {
-  return NativeToJavaObjectArray(
-      env, container, java_math_BigInteger_clazz(env), &NativeToJavaBigInteger);
-}
-
-jobject MemberToJava(JNIEnv* env, const RTCStatsMemberInterface& member) {
-  switch (member.type()) {
-    case RTCStatsMemberInterface::kBool:
-      return NativeToJavaBoolean(env, *member.cast_to<RTCStatsMember<bool>>());
-
-    case RTCStatsMemberInterface::kInt32:
-      return NativeToJavaInteger(env,
-                                 *member.cast_to<RTCStatsMember<int32_t>>());
-
-    case RTCStatsMemberInterface::kUint32:
-      return NativeToJavaLong(env, *member.cast_to<RTCStatsMember<uint32_t>>());
-
-    case RTCStatsMemberInterface::kInt64:
-      return NativeToJavaLong(env, *member.cast_to<RTCStatsMember<int64_t>>());
-
-    case RTCStatsMemberInterface::kUint64:
-      return NativeToJavaBigInteger(
-          env, *member.cast_to<RTCStatsMember<uint64_t>>());
-
-    case RTCStatsMemberInterface::kDouble:
-      return NativeToJavaDouble(env, *member.cast_to<RTCStatsMember<double>>());
-
-    case RTCStatsMemberInterface::kString:
-      return NativeToJavaString(env,
-                                *member.cast_to<RTCStatsMember<std::string>>());
-
-    case RTCStatsMemberInterface::kSequenceBool:
-      return NativeToJavaBooleanArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<bool>>>());
-
-    case RTCStatsMemberInterface::kSequenceInt32:
-      return NativeToJavaIntegerArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<int32_t>>>());
-
-    case RTCStatsMemberInterface::kSequenceUint32: {
-      const std::vector<uint32_t>& v =
-          *member.cast_to<RTCStatsMember<std::vector<uint32_t>>>();
-      return NativeToJavaLongArray(env,
-                                   std::vector<int64_t>(v.begin(), v.end()));
-    }
-    case RTCStatsMemberInterface::kSequenceInt64:
-      return NativeToJavaLongArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<int64_t>>>());
-
-    case RTCStatsMemberInterface::kSequenceUint64:
-      return NativeToJavaBigIntegerArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<uint64_t>>>());
-
-    case RTCStatsMemberInterface::kSequenceDouble:
-      return NativeToJavaDoubleArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<double>>>());
-
-    case RTCStatsMemberInterface::kSequenceString:
-      return NativeToJavaStringArray(
-          env, *member.cast_to<RTCStatsMember<std::vector<std::string>>>());
-  }
-  RTC_NOTREACHED();
-  return nullptr;
-}
-
-}  // namespace
-
 RTCStatsCollectorCallbackWrapper::RTCStatsCollectorCallbackWrapper(
     JNIEnv* jni,
     jobject j_callback)
     : j_callback_global_(jni, j_callback),
+      j_callback_class_(jni, GetObjectClass(jni, j_callback)),
       j_linked_hash_map_class_(FindClass(jni, "java/util/LinkedHashMap")),
       j_linked_hash_map_ctor_(
           GetMethodID(jni, j_linked_hash_map_class_, "<init>", "()V")),
@@ -110,7 +33,20 @@
           jni,
           j_linked_hash_map_class_,
           "put",
-          "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")) {}
+          "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")),
+      j_boolean_class_(FindClass(jni, "java/lang/Boolean")),
+      j_boolean_ctor_(GetMethodID(jni, j_boolean_class_, "<init>", "(Z)V")),
+      j_integer_class_(jni->FindClass("java/lang/Integer")),
+      j_long_class_(FindClass(jni, "java/lang/Long")),
+      j_long_ctor_(GetMethodID(jni, j_long_class_, "<init>", "(J)V")),
+      j_big_integer_class_(FindClass(jni, "java/math/BigInteger")),
+      j_big_integer_ctor_(GetMethodID(jni,
+                                      j_big_integer_class_,
+                                      "<init>",
+                                      "(Ljava/lang/String;)V")),
+      j_double_class_(FindClass(jni, "java/lang/Double")),
+      j_double_ctor_(GetMethodID(jni, j_double_class_, "<init>", "(D)V")),
+      j_string_class_(FindClass(jni, "java/lang/String")) {}
 
 void RTCStatsCollectorCallbackWrapper::OnStatsDelivered(
     const rtc::scoped_refptr<const RTCStatsReport>& report) {
@@ -131,7 +67,7 @@
     // Create a local reference frame for each RTCStats, since there is a
     // maximum number of references that can be created in one frame.
     ScopedLocalRefFrame local_ref_frame(jni);
-    jstring j_id = NativeToJavaString(jni, stats.id());
+    jstring j_id = JavaStringFromStdString(jni, stats.id());
     jobject j_stats = StatsToJava(jni, stats);
     jni->CallObjectMethod(j_stats_map, j_linked_hash_map_put_, j_id, j_stats);
     CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
@@ -143,8 +79,8 @@
 
 jobject RTCStatsCollectorCallbackWrapper::StatsToJava(JNIEnv* jni,
                                                       const RTCStats& stats) {
-  jstring j_type = NativeToJavaString(jni, stats.type());
-  jstring j_id = NativeToJavaString(jni, stats.id());
+  jstring j_type = JavaStringFromStdString(jni, stats.type());
+  jstring j_id = JavaStringFromStdString(jni, stats.id());
   jobject j_members =
       jni->NewObject(j_linked_hash_map_class_, j_linked_hash_map_ctor_);
   for (const RTCStatsMemberInterface* member : stats.Members()) {
@@ -153,8 +89,8 @@
     }
     // Create a local reference frame for each member as well.
     ScopedLocalRefFrame local_ref_frame(jni);
-    jstring j_name = NativeToJavaString(jni, member->name());
-    jobject j_member = MemberToJava(jni, *member);
+    jstring j_name = JavaStringFromStdString(jni, member->name());
+    jobject j_member = MemberToJava(jni, member);
     jni->CallObjectMethod(j_members, j_linked_hash_map_put_, j_name, j_member);
     CHECK_EXCEPTION(jni) << "error during CallObjectMethod";
   }
@@ -164,5 +100,151 @@
   return j_stats;
 }
 
+jobject RTCStatsCollectorCallbackWrapper::MemberToJava(
+    JNIEnv* jni,
+    const RTCStatsMemberInterface* member) {
+  switch (member->type()) {
+    case RTCStatsMemberInterface::kBool: {
+      jobject value = jni->NewObject(j_boolean_class_, j_boolean_ctor_,
+                                     *member->cast_to<RTCStatsMember<bool>>());
+      CHECK_EXCEPTION(jni) << "error during NewObject";
+      return value;
+    }
+    case RTCStatsMemberInterface::kInt32: {
+      return JavaIntegerFromInt(jni,
+                                *member->cast_to<RTCStatsMember<int32_t>>());
+    }
+    case RTCStatsMemberInterface::kUint32: {
+      jobject value =
+          jni->NewObject(j_long_class_, j_long_ctor_,
+                         (jlong)*member->cast_to<RTCStatsMember<uint32_t>>());
+      CHECK_EXCEPTION(jni) << "error during NewObject";
+      return value;
+    }
+    case RTCStatsMemberInterface::kInt64: {
+      jobject value =
+          jni->NewObject(j_long_class_, j_long_ctor_,
+                         *member->cast_to<RTCStatsMember<int64_t>>());
+      CHECK_EXCEPTION(jni) << "error during NewObject";
+      return value;
+    }
+    case RTCStatsMemberInterface::kUint64: {
+      jobject value =
+          jni->NewObject(j_big_integer_class_, j_big_integer_ctor_,
+                         JavaStringFromStdString(jni, member->ValueToString()));
+      CHECK_EXCEPTION(jni) << "error during NewObject";
+      return value;
+    }
+    case RTCStatsMemberInterface::kDouble: {
+      jobject value =
+          jni->NewObject(j_double_class_, j_double_ctor_,
+                         *member->cast_to<RTCStatsMember<double>>());
+      CHECK_EXCEPTION(jni) << "error during NewObject";
+      return value;
+    }
+    case RTCStatsMemberInterface::kString: {
+      return JavaStringFromStdString(
+          jni, *member->cast_to<RTCStatsMember<std::string>>());
+    }
+    case RTCStatsMemberInterface::kSequenceBool: {
+      const std::vector<bool>& values =
+          *member->cast_to<RTCStatsMember<std::vector<bool>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_boolean_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jobject value =
+            jni->NewObject(j_boolean_class_, j_boolean_ctor_, values[i]);
+        jni->SetObjectArrayElement(j_values, i, value);
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceInt32: {
+      const std::vector<int32_t>& values =
+          *member->cast_to<RTCStatsMember<std::vector<int32_t>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_integer_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jni->SetObjectArrayElement(j_values, i,
+                                   JavaIntegerFromInt(jni, values[i]));
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceUint32: {
+      const std::vector<uint32_t>& values =
+          *member->cast_to<RTCStatsMember<std::vector<uint32_t>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_long_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jobject value = jni->NewObject(j_long_class_, j_long_ctor_, values[i]);
+        jni->SetObjectArrayElement(j_values, i, value);
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceInt64: {
+      const std::vector<int64_t>& values =
+          *member->cast_to<RTCStatsMember<std::vector<int64_t>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_long_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jobject value = jni->NewObject(j_long_class_, j_long_ctor_, values[i]);
+        jni->SetObjectArrayElement(j_values, i, value);
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceUint64: {
+      const std::vector<uint64_t>& values =
+          *member->cast_to<RTCStatsMember<std::vector<uint64_t>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_big_integer_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jobject value = jni->NewObject(
+            j_big_integer_class_, j_big_integer_ctor_,
+            JavaStringFromStdString(jni, rtc::ToString(values[i])));
+        jni->SetObjectArrayElement(j_values, i, value);
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceDouble: {
+      const std::vector<double>& values =
+          *member->cast_to<RTCStatsMember<std::vector<double>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_double_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jobject value =
+            jni->NewObject(j_double_class_, j_double_ctor_, values[i]);
+        jni->SetObjectArrayElement(j_values, i, value);
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+    case RTCStatsMemberInterface::kSequenceString: {
+      const std::vector<std::string>& values =
+          *member->cast_to<RTCStatsMember<std::vector<std::string>>>();
+      jobjectArray j_values =
+          jni->NewObjectArray(values.size(), j_string_class_, nullptr);
+      CHECK_EXCEPTION(jni) << "error during NewObjectArray";
+      for (size_t i = 0; i < values.size(); ++i) {
+        jni->SetObjectArrayElement(j_values, i,
+                                   JavaStringFromStdString(jni, values[i]));
+        CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
+      }
+      return j_values;
+    }
+  }
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
 }  // namespace jni
 }  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
index d9cc692..8914a5d 100644
--- a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
+++ b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
@@ -34,11 +34,23 @@
   jobject ReportToJava(JNIEnv* jni,
                        const rtc::scoped_refptr<const RTCStatsReport>& report);
   jobject StatsToJava(JNIEnv* jni, const RTCStats& stats);
+  jobject MemberToJava(JNIEnv* jni, const RTCStatsMemberInterface* member);
 
   const ScopedGlobalRef<jobject> j_callback_global_;
+  const ScopedGlobalRef<jclass> j_callback_class_;
   const jclass j_linked_hash_map_class_;
   const jmethodID j_linked_hash_map_ctor_;
   const jmethodID j_linked_hash_map_put_;
+  const jclass j_boolean_class_;
+  const jmethodID j_boolean_ctor_;
+  const jclass j_integer_class_;
+  const jclass j_long_class_;
+  const jmethodID j_long_ctor_;
+  const jclass j_big_integer_class_;
+  const jmethodID j_big_integer_ctor_;
+  const jclass j_double_class_;
+  const jmethodID j_double_ctor_;
+  const jclass j_string_class_;
 };
 
 }  // namespace jni
diff --git a/sdk/android/src/jni/pc/rtpreceiver_jni.cc b/sdk/android/src/jni/pc/rtpreceiver_jni.cc
index eb60cc0..521ea60 100644
--- a/sdk/android/src/jni/pc/rtpreceiver_jni.cc
+++ b/sdk/android/src/jni/pc/rtpreceiver_jni.cc
@@ -59,7 +59,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_rtp_receiver_pointer) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni,
       reinterpret_cast<RtpReceiverInterface*>(j_rtp_receiver_pointer)->id());
 }
diff --git a/sdk/android/src/jni/pc/rtpsender_jni.cc b/sdk/android/src/jni/pc/rtpsender_jni.cc
index a30bc6c..e307480 100644
--- a/sdk/android/src/jni/pc/rtpsender_jni.cc
+++ b/sdk/android/src/jni/pc/rtpsender_jni.cc
@@ -78,7 +78,7 @@
                          JNIEnv* jni,
                          jclass,
                          jlong j_rtp_sender_pointer) {
-  return NativeToJavaString(
+  return JavaStringFromStdString(
       jni, reinterpret_cast<RtpSenderInterface*>(j_rtp_sender_pointer)->id());
 }
 
diff --git a/sdk/android/src/jni/pc/sdpobserver_jni.h b/sdk/android/src/jni/pc/sdpobserver_jni.h
index 5ed3b59..9b8db63 100644
--- a/sdk/android/src/jni/pc/sdpobserver_jni.h
+++ b/sdk/android/src/jni/pc/sdpobserver_jni.h
@@ -64,7 +64,7 @@
   void DoOnFailure(const std::string& op, const std::string& error) {
     jmethodID m = GetMethodID(jni(), *j_observer_class_, "on" + op + "Failure",
                               "(Ljava/lang/String;)V");
-    jstring j_error_string = NativeToJavaString(jni(), error);
+    jstring j_error_string = JavaStringFromStdString(jni(), error);
     jni()->CallVoidMethod(*j_observer_global_, m, j_error_string);
     CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
   }
diff --git a/sdk/android/src/jni/pc/statsobserver_jni.cc b/sdk/android/src/jni/pc/statsobserver_jni.cc
index f91a491..1c7f873 100644
--- a/sdk/android/src/jni/pc/statsobserver_jni.cc
+++ b/sdk/android/src/jni/pc/statsobserver_jni.cc
@@ -10,60 +10,75 @@
 
 #include "sdk/android/src/jni/pc/statsobserver_jni.h"
 
-#include "sdk/android/generated_peerconnection_jni/jni/StatsObserver_jni.h"
-#include "sdk/android/generated_peerconnection_jni/jni/StatsReport_jni.h"
-#include "sdk/android/src/jni/jni_helpers.h"
+#include "sdk/android/src/jni/classreferenceholder.h"
 
 namespace webrtc {
 namespace jni {
 
-namespace {
-
-jobject NativeToJavaStatsReportValue(
-    JNIEnv* env,
-    const rtc::scoped_refptr<StatsReport::Value>& value_ptr) {
-  // Should we use the '.name' enum value here instead of converting the
-  // name to a string?
-  jstring j_name = NativeToJavaString(env, value_ptr->display_name());
-  jstring j_value = NativeToJavaString(env, value_ptr->ToString());
-  return Java_Value_Constructor(env, j_name, j_value);
+// Convenience, used since callbacks occur on the signaling thread, which may
+// be a non-Java thread.
+static JNIEnv* jni() {
+  return AttachCurrentThreadIfNeeded();
 }
 
-jobjectArray NativeToJavaStatsReportValueArray(
-    JNIEnv* env,
-    const StatsReport::Values& value_map) {
-  // Ignore the keys and make an array out of the values.
-  std::vector<StatsReport::ValuePtr> values;
-  for (const auto& it : value_map)
-    values.push_back(it.second);
-  return NativeToJavaObjectArray(env, values,
-                                 org_webrtc_StatsReport_00024Value_clazz(env),
-                                 &NativeToJavaStatsReportValue);
-}
-
-jobject NativeToJavaStatsReport(JNIEnv* env, const StatsReport& report) {
-  jstring j_id = NativeToJavaString(env, report.id()->ToString());
-  jstring j_type = NativeToJavaString(env, report.TypeToString());
-  jobjectArray j_values =
-      NativeToJavaStatsReportValueArray(env, report.values());
-  return Java_StatsReport_Constructor(env, j_id, j_type, report.timestamp(),
-                                      j_values);
-}
-
-}  // namespace
-
 StatsObserverJni::StatsObserverJni(JNIEnv* jni, jobject j_observer)
-    : j_observer_global_(jni, j_observer) {}
+    : j_observer_global_(jni, j_observer),
+      j_observer_class_(jni, GetObjectClass(jni, j_observer)),
+      j_stats_report_class_(jni, FindClass(jni, "org/webrtc/StatsReport")),
+      j_stats_report_ctor_(GetMethodID(jni,
+                                       *j_stats_report_class_,
+                                       "<init>",
+                                       "(Ljava/lang/String;Ljava/lang/String;D"
+                                       "[Lorg/webrtc/StatsReport$Value;)V")),
+      j_value_class_(jni, FindClass(jni, "org/webrtc/StatsReport$Value")),
+      j_value_ctor_(GetMethodID(jni,
+                                *j_value_class_,
+                                "<init>",
+                                "(Ljava/lang/String;Ljava/lang/String;)V")) {}
 
 void StatsObserverJni::OnComplete(const StatsReports& reports) {
-  JNIEnv* env = AttachCurrentThreadIfNeeded();
-  ScopedLocalRefFrame local_ref_frame(env);
-  jobjectArray j_reports =
-      NativeToJavaObjectArray(env, reports, org_webrtc_StatsReport_clazz(env),
-                              [](JNIEnv* env, const StatsReport* report) {
-                                return NativeToJavaStatsReport(env, *report);
-                              });
-  Java_StatsObserver_onComplete(env, *j_observer_global_, j_reports);
+  ScopedLocalRefFrame local_ref_frame(jni());
+  jobjectArray j_reports = ReportsToJava(jni(), reports);
+  jmethodID m = GetMethodID(jni(), *j_observer_class_, "onComplete",
+                            "([Lorg/webrtc/StatsReport;)V");
+  jni()->CallVoidMethod(*j_observer_global_, m, j_reports);
+  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
+}
+
+jobjectArray StatsObserverJni::ReportsToJava(JNIEnv* jni,
+                                             const StatsReports& reports) {
+  jobjectArray reports_array =
+      jni->NewObjectArray(reports.size(), *j_stats_report_class_, NULL);
+  int i = 0;
+  for (const auto* report : reports) {
+    ScopedLocalRefFrame local_ref_frame(jni);
+    jstring j_id = JavaStringFromStdString(jni, report->id()->ToString());
+    jstring j_type = JavaStringFromStdString(jni, report->TypeToString());
+    jobjectArray j_values = ValuesToJava(jni, report->values());
+    jobject j_report =
+        jni->NewObject(*j_stats_report_class_, j_stats_report_ctor_, j_id,
+                       j_type, report->timestamp(), j_values);
+    jni->SetObjectArrayElement(reports_array, i++, j_report);
+  }
+  return reports_array;
+}
+
+jobjectArray StatsObserverJni::ValuesToJava(JNIEnv* jni,
+                                            const StatsReport::Values& values) {
+  jobjectArray j_values =
+      jni->NewObjectArray(values.size(), *j_value_class_, NULL);
+  int i = 0;
+  for (const auto& it : values) {
+    ScopedLocalRefFrame local_ref_frame(jni);
+    // Should we use the '.name' enum value here instead of converting the
+    // name to a string?
+    jstring j_name = JavaStringFromStdString(jni, it.second->display_name());
+    jstring j_value = JavaStringFromStdString(jni, it.second->ToString());
+    jobject j_element_value =
+        jni->NewObject(*j_value_class_, j_value_ctor_, j_name, j_value);
+    jni->SetObjectArrayElement(j_values, i++, j_element_value);
+  }
+  return j_values;
 }
 
 }  // namespace jni
diff --git a/sdk/android/src/jni/pc/statsobserver_jni.h b/sdk/android/src/jni/pc/statsobserver_jni.h
index ec5d4fd..d1e2224 100644
--- a/sdk/android/src/jni/pc/statsobserver_jni.h
+++ b/sdk/android/src/jni/pc/statsobserver_jni.h
@@ -26,7 +26,16 @@
   void OnComplete(const StatsReports& reports) override;
 
  private:
+  jobjectArray ReportsToJava(JNIEnv* jni, const StatsReports& reports);
+
+  jobjectArray ValuesToJava(JNIEnv* jni, const StatsReport::Values& values);
+
   const ScopedGlobalRef<jobject> j_observer_global_;
+  const ScopedGlobalRef<jclass> j_observer_class_;
+  const ScopedGlobalRef<jclass> j_stats_report_class_;
+  const jmethodID j_stats_report_ctor_;
+  const ScopedGlobalRef<jclass> j_value_class_;
+  const jmethodID j_value_ctor_;
 };
 
 }  // namespace jni
diff --git a/sdk/android/src/jni/videocodecinfo.cc b/sdk/android/src/jni/videocodecinfo.cc
index 2412649..2566673 100644
--- a/sdk/android/src/jni/videocodecinfo.cc
+++ b/sdk/android/src/jni/videocodecinfo.cc
@@ -46,11 +46,11 @@
   jobject j_params = jni->NewObject(hash_map_class, hash_map_constructor);
   for (auto const& param : format.parameters) {
     jni->CallObjectMethod(j_params, put_method,
-                          NativeToJavaString(jni, param.first),
-                          NativeToJavaString(jni, param.second));
+                          JavaStringFromStdString(jni, param.first),
+                          JavaStringFromStdString(jni, param.second));
   }
   return jni->NewObject(video_codec_info_class, video_codec_info_constructor,
-                        NativeToJavaString(jni, format.name), j_params);
+                        JavaStringFromStdString(jni, format.name), j_params);
 }
 
 }  // namespace jni
diff --git a/sdk/android/src/jni/videodecoderfactorywrapper.cc b/sdk/android/src/jni/videodecoderfactorywrapper.cc
index e687289..cda9c4b 100644
--- a/sdk/android/src/jni/videodecoderfactorywrapper.cc
+++ b/sdk/android/src/jni/videodecoderfactorywrapper.cc
@@ -32,7 +32,7 @@
     const SdpVideoFormat& format) {
   JNIEnv* jni = AttachCurrentThreadIfNeeded();
   ScopedLocalRefFrame local_ref_frame(jni);
-  jstring name = NativeToJavaString(jni, format.name);
+  jstring name = JavaStringFromStdString(jni, format.name);
   jobject decoder =
       jni->CallObjectMethod(*decoder_factory_, create_decoder_method_, name);
   return decoder != nullptr ? JavaToNativeVideoDecoder(jni, decoder) : nullptr;
diff --git a/sdk/android/src/jni/videodecoderwrapper.cc b/sdk/android/src/jni/videodecoderwrapper.cc
index 30377db..6b29c0e 100644
--- a/sdk/android/src/jni/videodecoderwrapper.cc
+++ b/sdk/android/src/jni/videodecoderwrapper.cc
@@ -160,10 +160,10 @@
   frame.set_ntp_time_ms(frame_extra_info.timestamp_ntp);
 
   rtc::Optional<int32_t> decoding_time_ms =
-      JavaToNativeOptionalInt(env, j_decode_time_ms);
+      JavaIntegerToOptionalInt(env, j_decode_time_ms);
 
   rtc::Optional<uint8_t> decoder_qp =
-      cast_optional<uint8_t, int32_t>(JavaToNativeOptionalInt(env, j_qp));
+      cast_optional<uint8_t, int32_t>(JavaIntegerToOptionalInt(env, j_qp));
   // If the decoder provides QP values itself, no need to parse the bitstream.
   // Enable QP parsing if decoder does not provide QP values itself.
   qp_parsing_enabled_ = !decoder_qp.has_value();
diff --git a/sdk/android/src/jni/videoencoderwrapper.cc b/sdk/android/src/jni/videoencoderwrapper.cc
index 13b0e37..19c00ec 100644
--- a/sdk/android/src/jni/videoencoderwrapper.cc
+++ b/sdk/android/src/jni/videoencoderwrapper.cc
@@ -121,7 +121,12 @@
   ScopedLocalRefFrame local_ref_frame(jni);
 
   // Construct encode info.
-  jobjectArray j_frame_types = NativeToJavaFrameTypeArray(jni, *frame_types);
+  jobjectArray j_frame_types =
+      jni->NewObjectArray(frame_types->size(), *frame_type_class_, nullptr);
+  for (size_t i = 0; i < frame_types->size(); ++i) {
+    jobject j_frame_type = NativeToJavaFrameType(jni, (*frame_types)[i]);
+    jni->SetObjectArrayElement(j_frame_types, i, j_frame_type);
+  }
   jobject encode_info = Java_EncodeInfo_Constructor(jni, j_frame_types);
 
   FrameExtraInfo info;
@@ -164,10 +169,10 @@
   bool isOn =
       Java_VideoEncoderWrapper_getScalingSettingsOn(jni, j_scaling_settings);
 
-  rtc::Optional<int> low = JavaToNativeOptionalInt(
+  rtc::Optional<int> low = JavaIntegerToOptionalInt(
       jni,
       Java_VideoEncoderWrapper_getScalingSettingsLow(jni, j_scaling_settings));
-  rtc::Optional<int> high = JavaToNativeOptionalInt(
+  rtc::Optional<int> high = JavaIntegerToOptionalInt(
       jni,
       Java_VideoEncoderWrapper_getScalingSettingsHigh(jni, j_scaling_settings));
 
@@ -195,7 +200,7 @@
 
   std::vector<uint8_t> buffer_copy(buffer_size);
   memcpy(buffer_copy.data(), buffer, buffer_size);
-  const int qp = JavaToNativeOptionalInt(jni, j_qp).value_or(-1);
+  const int qp = JavaIntegerToOptionalInt(jni, j_qp).value_or(-1);
 
   encoder_queue_->PostTask(
       [