addIceCandidate with callback into Android's SDK.

Bug: webrtc:12609
Change-Id: I059a246f5ade201b6a8decac264a8dd79fef3f9a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/212740
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33681}
diff --git a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index 8cc487e7..0776ccb 100644
--- a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -36,6 +36,7 @@
 import java.util.regex.Pattern;
 import org.appspot.apprtc.AppRTCClient.SignalingParameters;
 import org.appspot.apprtc.RecordedAudioToFileController;
+import org.webrtc.AddIceObserver;
 import org.webrtc.AudioSource;
 import org.webrtc.AudioTrack;
 import org.webrtc.CameraVideoCapturer;
@@ -824,7 +825,16 @@
         if (queuedRemoteCandidates != null) {
           queuedRemoteCandidates.add(candidate);
         } else {
-          peerConnection.addIceCandidate(candidate);
+          peerConnection.addIceCandidate(candidate, new AddIceObserver() {
+            @Override
+            public void onAddSuccess() {
+              Log.d(TAG, "Candidate " + candidate + " successfully added.");
+            }
+            @Override
+            public void onAddFailure(String error) {
+              Log.d(TAG, "Candidate " + candidate + " addition failed: " + error);
+            }
+          });
         }
       }
     });
@@ -1146,7 +1156,16 @@
     if (queuedRemoteCandidates != null) {
       Log.d(TAG, "Add " + queuedRemoteCandidates.size() + " remote candidates");
       for (IceCandidate candidate : queuedRemoteCandidates) {
-        peerConnection.addIceCandidate(candidate);
+        peerConnection.addIceCandidate(candidate, new AddIceObserver() {
+          @Override
+          public void onAddSuccess() {
+            Log.d(TAG, "Candidate " + candidate + " successfully added.");
+          }
+          @Override
+          public void onAddFailure(String error) {
+            Log.d(TAG, "Candidate " + candidate + " addition failed: " + error);
+          }
+        });
       }
       queuedRemoteCandidates = null;
     }
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 0d2ede0..c5c903b 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -262,6 +262,7 @@
   rtc_android_library("peerconnection_java") {
     visibility = [ "*" ]
     sources = [
+      "api/org/webrtc/AddIceObserver.java",
       "api/org/webrtc/AudioProcessingFactory.java",
       "api/org/webrtc/AudioSource.java",
       "api/org/webrtc/AudioTrack.java",
@@ -692,6 +693,8 @@
     visibility = [ "*" ]
 
     sources = [
+      "src/jni/pc/add_ice_candidate_observer.cc",
+      "src/jni/pc/add_ice_candidate_observer.h",
       "src/jni/pc/android_network_monitor.h",
       "src/jni/pc/audio_track.cc",
       "src/jni/pc/call_session_file_rotating_log_sink.cc",
@@ -1264,6 +1267,7 @@
 
   generate_jni("generated_peerconnection_jni") {
     sources = [
+      "api/org/webrtc/AddIceObserver.java",
       "api/org/webrtc/AudioTrack.java",
       "api/org/webrtc/CallSessionFileRotatingLogSink.java",
       "api/org/webrtc/CandidatePairChangeEvent.java",
diff --git a/sdk/android/api/org/webrtc/AddIceObserver.java b/sdk/android/api/org/webrtc/AddIceObserver.java
new file mode 100644
index 0000000..ff2c690
--- /dev/null
+++ b/sdk/android/api/org/webrtc/AddIceObserver.java
@@ -0,0 +1,20 @@
+/*
+ *  Copyright 2021 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;
+
+/** Interface to handle completion of addIceCandidate  */
+public interface AddIceObserver {
+  /** Called when ICE candidate added successfully.*/
+  @CalledByNative public void onAddSuccess();
+
+  /** Called when ICE candidate addition failed.*/
+  @CalledByNative public void onAddFailure(String error);
+}
diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java
index d998f00..b28fbaf 100644
--- a/sdk/android/api/org/webrtc/PeerConnection.java
+++ b/sdk/android/api/org/webrtc/PeerConnection.java
@@ -932,6 +932,11 @@
     return nativeAddIceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
   }
 
+  public void addIceCandidate(IceCandidate candidate, AddIceObserver observer) {
+    nativeAddIceCandidateWithObserver(
+        candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp, observer);
+  }
+
   public boolean removeIceCandidates(final IceCandidate[] candidates) {
     return nativeRemoveIceCandidates(candidates);
   }
@@ -1293,6 +1298,8 @@
   private native boolean nativeSetConfiguration(RTCConfiguration config);
   private native boolean nativeAddIceCandidate(
       String sdpMid, int sdpMLineIndex, String iceCandidateSdp);
+  private native void nativeAddIceCandidateWithObserver(
+      String sdpMid, int sdpMLineIndex, String iceCandidateSdp, AddIceObserver observer);
   private native boolean nativeRemoveIceCandidates(final IceCandidate[] candidates);
   private native boolean nativeAddLocalStream(long stream);
   private native void nativeRemoveLocalStream(long stream);
diff --git a/sdk/android/src/jni/pc/add_ice_candidate_observer.cc b/sdk/android/src/jni/pc/add_ice_candidate_observer.cc
new file mode 100644
index 0000000..7f3dddb
--- /dev/null
+++ b/sdk/android/src/jni/pc/add_ice_candidate_observer.cc
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2021 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.
+ */
+
+#include "sdk/android/src/jni/pc/add_ice_candidate_observer.h"
+
+#include <utility>
+
+#include "sdk/android/generated_peerconnection_jni/AddIceObserver_jni.h"
+#include "sdk/android/native_api/jni/java_types.h"
+#include "sdk/android/src/jni/jni_helpers.h"
+#include "sdk/media_constraints.h"
+
+namespace webrtc {
+namespace jni {
+
+AddIceCandidateObserverJni::AddIceCandidateObserverJni(
+    JNIEnv* env,
+    const JavaRef<jobject>& j_observer)
+    : j_observer_global_(env, j_observer) {}
+
+void AddIceCandidateObserverJni::OnComplete(webrtc::RTCError error) {
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  if (error.ok()) {
+    Java_AddIceObserver_onAddSuccess(env, j_observer_global_);
+  } else {
+    Java_AddIceObserver_onAddFailure(env, j_observer_global_,
+                                     NativeToJavaString(env, error.message()));
+  }
+}
+
+}  // namespace jni
+}  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/add_ice_candidate_observer.h b/sdk/android/src/jni/pc/add_ice_candidate_observer.h
new file mode 100644
index 0000000..ed72de9
--- /dev/null
+++ b/sdk/android/src/jni/pc/add_ice_candidate_observer.h
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2021 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 SDK_ANDROID_SRC_JNI_PC_ADD_ICE_CANDIDATE_OBSERVER_H_
+#define SDK_ANDROID_SRC_JNI_PC_ADD_ICE_CANDIDATE_OBSERVER_H_
+
+#include <memory>
+#include <string>
+
+#include "api/peer_connection_interface.h"
+#include "sdk/android/src/jni/jni_helpers.h"
+
+namespace webrtc {
+namespace jni {
+
+class AddIceCandidateObserverJni final : public rtc::RefCountedBase {
+ public:
+  AddIceCandidateObserverJni(JNIEnv* env, const JavaRef<jobject>& j_observer);
+  ~AddIceCandidateObserverJni() override = default;
+
+  void OnComplete(RTCError error);
+
+ private:
+  const ScopedJavaGlobalRef<jobject> j_observer_global_;
+};
+
+}  // namespace jni
+}  // namespace webrtc
+
+#endif  // SDK_ANDROID_SRC_JNI_PC_ADD_ICE_CANDIDATE_OBSERVER_H_
diff --git a/sdk/android/src/jni/pc/peer_connection.cc b/sdk/android/src/jni/pc/peer_connection.cc
index be1cddd..84263ae 100644
--- a/sdk/android/src/jni/pc/peer_connection.cc
+++ b/sdk/android/src/jni/pc/peer_connection.cc
@@ -44,6 +44,7 @@
 #include "sdk/android/generated_peerconnection_jni/PeerConnection_jni.h"
 #include "sdk/android/native_api/jni/java_types.h"
 #include "sdk/android/src/jni/jni_helpers.h"
+#include "sdk/android/src/jni/pc/add_ice_candidate_observer.h"
 #include "sdk/android/src/jni/pc/crypto_options.h"
 #include "sdk/android/src/jni/pc/data_channel.h"
 #include "sdk/android/src/jni/pc/ice_candidate.h"
@@ -651,6 +652,25 @@
   return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get());
 }
 
+static void JNI_PeerConnection_AddIceCandidateWithObserver(
+    JNIEnv* jni,
+    const JavaParamRef<jobject>& j_pc,
+    const JavaParamRef<jstring>& j_sdp_mid,
+    jint j_sdp_mline_index,
+    const JavaParamRef<jstring>& j_candidate_sdp,
+    const JavaParamRef<jobject>& j_observer) {
+  std::string sdp_mid = JavaToNativeString(jni, j_sdp_mid);
+  std::string sdp = JavaToNativeString(jni, j_candidate_sdp);
+  std::unique_ptr<IceCandidateInterface> candidate(
+      CreateIceCandidate(sdp_mid, j_sdp_mline_index, sdp, nullptr));
+
+  rtc::scoped_refptr<AddIceCandidateObserverJni> observer(
+      new AddIceCandidateObserverJni(jni, j_observer));
+  ExtractNativePC(jni, j_pc)->AddIceCandidate(
+      std::move(candidate),
+      [observer](RTCError error) { observer->OnComplete(error); });
+}
+
 static jboolean JNI_PeerConnection_RemoveIceCandidates(
     JNIEnv* jni,
     const JavaParamRef<jobject>& j_pc,