Android: Generate JNI code for IceCandidate

Bug: webrtc:8278
Change-Id: I4facd1f6babd6e8a9b35c86b6ad7420e52321f49
Reviewed-on: https://webrtc-review.googlesource.com/25960
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20888}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 77b5493..0be6195 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -285,6 +285,7 @@
 generate_jni("generated_peerconnection_jni") {
   sources = [
     "api/org/webrtc/DataChannel.java",
+    "api/org/webrtc/IceCandidate.java",
     "api/org/webrtc/MediaConstraints.java",
     "api/org/webrtc/NetworkMonitor.java",
     "api/org/webrtc/NetworkMonitorAutoDetect.java",
diff --git a/sdk/android/api/org/webrtc/IceCandidate.java b/sdk/android/api/org/webrtc/IceCandidate.java
index b03f1a3..51865e9 100644
--- a/sdk/android/api/org/webrtc/IceCandidate.java
+++ b/sdk/android/api/org/webrtc/IceCandidate.java
@@ -27,8 +27,8 @@
     this.serverUrl = "";
   }
 
-  // Only be called internally from JNI.
-  private IceCandidate(String sdpMid, int sdpMLineIndex, String sdp, String serverUrl) {
+  @CalledByNative
+  IceCandidate(String sdpMid, int sdpMLineIndex, String sdp, String serverUrl) {
     this.sdpMid = sdpMid;
     this.sdpMLineIndex = sdpMLineIndex;
     this.sdp = sdp;
@@ -39,4 +39,19 @@
   public String toString() {
     return sdpMid + ":" + sdpMLineIndex + ":" + sdp + ":" + serverUrl;
   }
+
+  @CalledByNative
+  String getSdpMid() {
+    return sdpMid;
+  }
+
+  @CalledByNative
+  String getSdp() {
+    return sdp;
+  }
+
+  @CalledByNative
+  static IceCandidate[] createArray(int size) {
+    return new IceCandidate[size];
+  }
 }
diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc
index 1187d3e..f578c47 100644
--- a/sdk/android/src/jni/classreferenceholder.cc
+++ b/sdk/android/src/jni/classreferenceholder.cc
@@ -68,7 +68,6 @@
   LoadClass(jni, "org/webrtc/EglBase14$Context");
   LoadClass(jni, "org/webrtc/EncodedImage");
   LoadClass(jni, "org/webrtc/EncodedImage$FrameType");
-  LoadClass(jni, "org/webrtc/IceCandidate");
   LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder");
   LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecodedOutputBuffer");
   LoadClass(jni, "org/webrtc/MediaCodecVideoDecoder$DecodedTextureBuffer");
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 56a1f4e..ca9bbfb 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -13,11 +13,27 @@
 #include <string>
 
 #include "pc/webrtcsdp.h"
+#include "sdk/android/generated_peerconnection_jni/jni/IceCandidate_jni.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
 
 namespace webrtc {
 namespace jni {
 
+namespace {
+
+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, JavaStringFromStdString(env, sdp_mid), sdp_mline_index,
+      JavaStringFromStdString(env, sdp),
+      JavaStringFromStdString(env, server_url));
+}
+
+}  // namespace
+
 jobject NativeToJavaMediaType(JNIEnv* jni, cricket::MediaType media_type) {
   jclass j_media_type_class =
       FindClass(jni, "org/webrtc/MediaStreamTrack$MediaType");
@@ -58,15 +74,10 @@
 }
 
 cricket::Candidate JavaToNativeCandidate(JNIEnv* jni, jobject j_candidate) {
-  jclass j_candidate_class = GetObjectClass(jni, j_candidate);
-  jfieldID j_sdp_mid_id =
-      GetFieldID(jni, j_candidate_class, "sdpMid", "Ljava/lang/String;");
   std::string sdp_mid =
-      JavaToStdString(jni, GetStringField(jni, j_candidate, j_sdp_mid_id));
-  jfieldID j_sdp_id =
-      GetFieldID(jni, j_candidate_class, "sdp", "Ljava/lang/String;");
+      JavaToStdString(jni, Java_IceCandidate_getSdpMid(jni, j_candidate));
   std::string sdp =
-      JavaToStdString(jni, GetStringField(jni, j_candidate, j_sdp_id));
+      JavaToStdString(jni, Java_IceCandidate_getSdp(jni, j_candidate));
   cricket::Candidate candidate;
   if (!SdpDeserializeCandidate(sdp_mid, sdp, &candidate, NULL)) {
     RTC_LOG(LS_ERROR) << "SdpDescrializeCandidate failed with sdp " << sdp;
@@ -74,32 +85,33 @@
   return candidate;
 }
 
-jobject NativeToJavaCandidate(JNIEnv* jni,
-                              jclass* candidate_class,
+jobject NativeToJavaCandidate(JNIEnv* env,
                               const cricket::Candidate& candidate) {
   std::string sdp = SdpSerializeCandidate(candidate);
   RTC_CHECK(!sdp.empty()) << "got an empty ICE candidate";
-  jmethodID ctor = GetMethodID(jni, *candidate_class, "<init>",
-                               "(Ljava/lang/String;ILjava/lang/String;)V");
-  jstring j_mid = JavaStringFromStdString(jni, candidate.transport_name());
-  jstring j_sdp = JavaStringFromStdString(jni, sdp);
   // sdp_mline_index is not used, pass an invalid value -1.
-  jobject j_candidate =
-      jni->NewObject(*candidate_class, ctor, j_mid, -1, j_sdp);
-  CHECK_EXCEPTION(jni) << "error during Java Candidate NewObject";
-  return j_candidate;
+  return NativeToJavaCandidate(env, candidate.transport_name(),
+                               -1 /* sdp_mline_index */, sdp,
+                               "" /* server_url */);
+}
+
+jobject NativeToJavaCandidate(JNIEnv* env,
+                              const IceCandidateInterface& candidate) {
+  std::string sdp;
+  RTC_CHECK(candidate.ToString(&sdp)) << "got so far: " << sdp;
+  return NativeToJavaCandidate(env, candidate.sdp_mid(),
+                               candidate.sdp_mline_index(), sdp,
+                               candidate.candidate().url());
 }
 
 jobjectArray NativeToJavaCandidateArray(
     JNIEnv* jni,
     const std::vector<cricket::Candidate>& candidates) {
-  jclass candidate_class = FindClass(jni, "org/webrtc/IceCandidate");
   jobjectArray java_candidates =
-      jni->NewObjectArray(candidates.size(), candidate_class, NULL);
+      Java_IceCandidate_createArray(jni, candidates.size());
   int i = 0;
   for (const cricket::Candidate& candidate : candidates) {
-    jobject j_candidate =
-        NativeToJavaCandidate(jni, &candidate_class, candidate);
+    jobject j_candidate = NativeToJavaCandidate(jni, candidate);
     jni->SetObjectArrayElement(java_candidates, i++, j_candidate);
   }
   return java_candidates;
diff --git a/sdk/android/src/jni/pc/java_native_conversion.h b/sdk/android/src/jni/pc/java_native_conversion.h
index 9b74ad3..cb9f861 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.h
+++ b/sdk/android/src/jni/pc/java_native_conversion.h
@@ -36,9 +36,10 @@
 
 cricket::Candidate JavaToNativeCandidate(JNIEnv* jni, jobject j_candidate);
 
-jobject NativeToJavaCandidate(JNIEnv* jni,
-                              jclass* candidate_class,
-                              const cricket::Candidate& candidate);
+jobject NativeToJavaCandidate(JNIEnv* env, const cricket::Candidate& candidate);
+
+jobject NativeToJavaCandidate(JNIEnv* env,
+                              const IceCandidateInterface& candidate);
 
 jobjectArray NativeToJavaCandidateArray(
     JNIEnv* jni,
diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
index 03436de..8bb098e 100644
--- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
@@ -61,23 +61,14 @@
 
 void PeerConnectionObserverJni::OnIceCandidate(
     const IceCandidateInterface* candidate) {
-  ScopedLocalRefFrame local_ref_frame(jni());
-  std::string sdp;
-  RTC_CHECK(candidate->ToString(&sdp)) << "got so far: " << sdp;
-  jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate");
-  jmethodID ctor =
-      GetMethodID(jni(), candidate_class, "<init>",
-                  "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V");
-  jstring j_mid = JavaStringFromStdString(jni(), candidate->sdp_mid());
-  jstring j_sdp = JavaStringFromStdString(jni(), sdp);
-  jstring j_url = JavaStringFromStdString(jni(), candidate->candidate().url());
-  jobject j_candidate = jni()->NewObject(
-      candidate_class, ctor, j_mid, candidate->sdp_mline_index(), j_sdp, j_url);
-  CHECK_EXCEPTION(jni()) << "error during NewObject";
-  jmethodID m = GetMethodID(jni(), *j_observer_class_, "onIceCandidate",
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  ScopedLocalRefFrame local_ref_frame(env);
+  jobject j_candidate = NativeToJavaCandidate(env, *candidate);
+
+  jmethodID m = GetMethodID(env, *j_observer_class_, "onIceCandidate",
                             "(Lorg/webrtc/IceCandidate;)V");
-  jni()->CallVoidMethod(*j_observer_global_, m, j_candidate);
-  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
+  env->CallVoidMethod(*j_observer_global_, m, j_candidate);
+  CHECK_EXCEPTION(env) << "error during CallVoidMethod";
 }
 
 void PeerConnectionObserverJni::OnIceCandidatesRemoved(