Moved Asynchronicity From Java to C++ for AndroidVoip Demo App

Moved asynchronicity from Java to C++.

Bug: webrtc:11723
Change-Id: I985693dc7d4312b6072314088716167b9cdd9999
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/180774
Commit-Queue: Tim Na <natim@webrtc.org>
Reviewed-by: Tim Na <natim@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31958}
diff --git a/examples/androidvoip/BUILD.gn b/examples/androidvoip/BUILD.gn
index 74341a7..649e601 100644
--- a/examples/androidvoip/BUILD.gn
+++ b/examples/androidvoip/BUILD.gn
@@ -26,6 +26,7 @@
       ":resources",
       "//modules/audio_device:audio_device_java",
       "//rtc_base:base_java",
+      "//sdk/android:base_java",
       "//sdk/android:java_audio_device_module_java",
       "//sdk/android:video_java",
       "//third_party/android_deps:androidx_core_core_java",
diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java
index d787de5..d06d6ad 100644
--- a/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java
+++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/MainActivity.java
@@ -219,10 +219,12 @@
   }
 
   private void showToast(String message) {
-    toast.cancel();
-    toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
-    toast.setGravity(Gravity.TOP, 0, 200);
-    toast.show();
+    if (toast != null) {
+      toast.cancel();
+      toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
+      toast.setGravity(Gravity.TOP, 0, 200);
+      toast.show();
+    }
   }
 
   @Override
diff --git a/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java
index 2dcbd99..69a993d 100644
--- a/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java
+++ b/examples/androidvoip/java/org/webrtc/examples/androidvoip/VoipClient.java
@@ -15,26 +15,15 @@
 import android.os.HandlerThread;
 import java.util.ArrayList;
 import java.util.List;
+import org.webrtc.CalledByNative;
 
 public class VoipClient {
-  private static final String TAG = "VoipClient";
-
-  private final HandlerThread thread;
-  private final Handler handler;
-
   private long nativeClient;
   private OnVoipClientTaskCompleted listener;
 
   public VoipClient(Context applicationContext, OnVoipClientTaskCompleted listener) {
     this.listener = listener;
-    thread = new HandlerThread(TAG + "Thread");
-    thread.start();
-    handler = new Handler(thread.getLooper());
-
-    handler.post(() -> {
-      nativeClient = nativeCreateClient(applicationContext);
-      listener.onVoipClientInitializationCompleted(/* isSuccessful */ nativeClient != 0);
-    });
+    nativeClient = nativeCreateClient(applicationContext, this);
   }
 
   private boolean isInitialized() {
@@ -42,147 +31,161 @@
   }
 
   public void getAndSetUpSupportedCodecs() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onGetSupportedCodecsCompleted(nativeGetSupportedCodecs(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeGetSupportedCodecs(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void getAndSetUpLocalIPAddress() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onGetLocalIPAddressCompleted(nativeGetLocalIPAddress(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeGetLocalIPAddress(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void setEncoder(String encoder) {
-    handler.post(() -> {
-      if (isInitialized()) {
-        nativeSetEncoder(nativeClient, encoder);
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeSetEncoder(nativeClient, encoder);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void setDecoders(List<String> decoders) {
-    handler.post(() -> {
-      if (isInitialized()) {
-        nativeSetDecoders(nativeClient, decoders);
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeSetDecoders(nativeClient, decoders);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void setLocalAddress(String ipAddress, int portNumber) {
-    handler.post(() -> {
-      if (isInitialized()) {
-        nativeSetLocalAddress(nativeClient, ipAddress, portNumber);
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeSetLocalAddress(nativeClient, ipAddress, portNumber);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void setRemoteAddress(String ipAddress, int portNumber) {
-    handler.post(() -> {
-      if (isInitialized()) {
-        nativeSetRemoteAddress(nativeClient, ipAddress, portNumber);
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeSetRemoteAddress(nativeClient, ipAddress, portNumber);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void startSession() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStartSessionCompleted(nativeStartSession(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStartSession(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void stopSession() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStopSessionCompleted(nativeStopSession(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStopSession(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void startSend() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStartSendCompleted(nativeStartSend(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStartSend(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void stopSend() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStopSendCompleted(nativeStopSend(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStopSend(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void startPlayout() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStartPlayoutCompleted(nativeStartPlayout(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStartPlayout(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void stopPlayout() {
-    handler.post(() -> {
-      if (isInitialized()) {
-        listener.onStopPlayoutCompleted(nativeStopPlayout(nativeClient));
-      } else {
-        listener.onUninitializedVoipClient();
-      }
-    });
+    if (isInitialized()) {
+      nativeStopPlayout(nativeClient);
+    } else {
+      listener.onUninitializedVoipClient();
+    }
   }
 
   public void close() {
-    handler.post(() -> {
-      nativeDelete(nativeClient);
-      nativeClient = 0;
-    });
-    thread.quitSafely();
+    nativeDelete(nativeClient);
+    nativeClient = 0;
   }
 
-  private static native long nativeCreateClient(Context applicationContext);
-  private static native List<String> nativeGetSupportedCodecs(long nativeAndroidVoipClient);
-  private static native String nativeGetLocalIPAddress(long nativeAndroidVoipClient);
+  @CalledByNative
+  public void onGetLocalIPAddressCompleted(String localIPAddress) {
+    listener.onGetLocalIPAddressCompleted(localIPAddress);
+  }
+
+  @CalledByNative
+  public void onGetSupportedCodecsCompleted(List<String> supportedCodecs) {
+    listener.onGetSupportedCodecsCompleted(supportedCodecs);
+  }
+
+  @CalledByNative
+  public void onStartSessionCompleted(boolean isSuccessful) {
+    listener.onStartSessionCompleted(isSuccessful);
+  }
+
+  @CalledByNative
+  public void onStopSessionCompleted(boolean isSuccessful) {
+    listener.onStopSessionCompleted(isSuccessful);
+  }
+
+  @CalledByNative
+  public void onStartSendCompleted(boolean isSuccessful) {
+    listener.onStartSendCompleted(isSuccessful);
+  }
+
+  @CalledByNative
+  public void onStopSendCompleted(boolean isSuccessful) {
+    listener.onStopSendCompleted(isSuccessful);
+  }
+
+  @CalledByNative
+  public void onStartPlayoutCompleted(boolean isSuccessful) {
+    listener.onStartPlayoutCompleted(isSuccessful);
+  }
+
+  @CalledByNative
+  public void onStopPlayoutCompleted(boolean isSuccessful) {
+    listener.onStopPlayoutCompleted(isSuccessful);
+  }
+
+  private static native long nativeCreateClient(
+      Context applicationContext, VoipClient javaVoipClient);
+  private static native void nativeGetSupportedCodecs(long nativeAndroidVoipClient);
+  private static native void nativeGetLocalIPAddress(long nativeAndroidVoipClient);
   private static native void nativeSetEncoder(long nativeAndroidVoipClient, String encoder);
   private static native void nativeSetDecoders(long nativeAndroidVoipClient, List<String> decoders);
   private static native void nativeSetLocalAddress(
       long nativeAndroidVoipClient, String ipAddress, int portNumber);
   private static native void nativeSetRemoteAddress(
       long nativeAndroidVoipClient, String ipAddress, int portNumber);
-  private static native boolean nativeStartSession(long nativeAndroidVoipClient);
-  private static native boolean nativeStopSession(long nativeAndroidVoipClient);
-  private static native boolean nativeStartSend(long nativeAndroidVoipClient);
-  private static native boolean nativeStopSend(long nativeAndroidVoipClient);
-  private static native boolean nativeStartPlayout(long nativeAndroidVoipClient);
-  private static native boolean nativeStopPlayout(long nativeAndroidVoipClient);
+  private static native void nativeStartSession(long nativeAndroidVoipClient);
+  private static native void nativeStopSession(long nativeAndroidVoipClient);
+  private static native void nativeStartSend(long nativeAndroidVoipClient);
+  private static native void nativeStopSend(long nativeAndroidVoipClient);
+  private static native void nativeStartPlayout(long nativeAndroidVoipClient);
+  private static native void nativeStopPlayout(long nativeAndroidVoipClient);
   private static native void nativeDelete(long nativeAndroidVoipClient);
 }
diff --git a/examples/androidvoip/jni/android_voip_client.cc b/examples/androidvoip/jni/android_voip_client.cc
index 13cadf2..2ad95bc 100644
--- a/examples/androidvoip/jni/android_voip_client.cc
+++ b/examples/androidvoip/jni/android_voip_client.cc
@@ -33,9 +33,20 @@
 #include "rtc_base/socket_server.h"
 #include "sdk/android/native_api/audio_device_module/audio_device_android.h"
 #include "sdk/android/native_api/jni/java_types.h"
+#include "sdk/android/native_api/jni/jvm.h"
+#include "sdk/android/native_api/jni/scoped_java_ref.h"
 
 namespace {
 
+#define RUN_ON_VOIP_THREAD(method, ...)                              \
+  if (!voip_thread_->IsCurrent()) {                                  \
+    voip_thread_->PostTask(                                          \
+        RTC_FROM_HERE,                                               \
+        std::bind(&AndroidVoipClient::method, this, ##__VA_ARGS__)); \
+    return;                                                          \
+  }                                                                  \
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
 // Connects a UDP socket to a public address and returns the local
 // address associated with it. Since it binds to the "any" address
 // internally, it returns the default local address on a multi-homed
@@ -109,12 +120,9 @@
 
 namespace webrtc_examples {
 
-AndroidVoipClient::AndroidVoipClient(
+bool AndroidVoipClient::Init(
     JNIEnv* env,
     const webrtc::JavaParamRef<jobject>& application_context) {
-  voip_thread_ = rtc::Thread::CreateWithSocketServer();
-  voip_thread_->Start();
-
   webrtc::VoipEngineConfig config;
   config.encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory();
   config.decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory();
@@ -123,137 +131,195 @@
       webrtc::CreateJavaAudioDeviceModule(env, application_context.obj());
   config.audio_processing = webrtc::AudioProcessingBuilder().Create();
 
-  supported_codecs_ = config.encoder_factory->GetSupportedEncoders();
-
+  voip_thread_->Start();
   // Due to consistent thread requirement on
   // modules/audio_device/android/audio_device_template.h,
   // code is invoked in the context of voip_thread_.
-  voip_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
+  return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this, &config] {
+    RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+    supported_codecs_ = config.encoder_factory->GetSupportedEncoders();
+    env_ = webrtc::AttachCurrentThreadIfNeeded();
     voip_engine_ = webrtc::CreateVoipEngine(std::move(config));
     if (!voip_engine_) {
       RTC_LOG(LS_ERROR) << "VoipEngine creation failed";
+      return false;
     }
+    return true;
   });
 }
 
 AndroidVoipClient::~AndroidVoipClient() {
+  voip_thread_->Invoke<void>(RTC_FROM_HERE, [this] {
+    RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+    JavaVM* jvm = nullptr;
+    env_->GetJavaVM(&jvm);
+    if (!jvm) {
+      RTC_LOG(LS_ERROR) << "Failed to retrieve JVM";
+      return;
+    }
+    jint res = jvm->DetachCurrentThread();
+    if (res != JNI_OK) {
+      RTC_LOG(LS_ERROR) << "DetachCurrentThread failed: " << res;
+    }
+  });
+
   voip_thread_->Stop();
 }
 
 AndroidVoipClient* AndroidVoipClient::Create(
     JNIEnv* env,
-    const webrtc::JavaParamRef<jobject>& application_context) {
+    const webrtc::JavaParamRef<jobject>& application_context,
+    const webrtc::JavaParamRef<jobject>& j_voip_client) {
   // Using `new` to access a non-public constructor.
   auto voip_client =
-      absl::WrapUnique(new AndroidVoipClient(env, application_context));
-  if (!voip_client->voip_engine_) {
+      absl::WrapUnique(new AndroidVoipClient(env, j_voip_client));
+  if (!voip_client->Init(env, application_context)) {
     return nullptr;
   }
   return voip_client.release();
 }
 
-webrtc::ScopedJavaLocalRef<jobject> AndroidVoipClient::GetSupportedCodecs(
-    JNIEnv* env) {
+void AndroidVoipClient::GetSupportedCodecs(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(GetSupportedCodecs, env);
+
   std::vector<std::string> names;
   for (const webrtc::AudioCodecSpec& spec : supported_codecs_) {
     names.push_back(spec.format.name);
   }
   webrtc::ScopedJavaLocalRef<jstring> (*convert_function)(
       JNIEnv*, const std::string&) = &webrtc::NativeToJavaString;
-  return NativeToJavaList(env, names, convert_function);
+  Java_VoipClient_onGetSupportedCodecsCompleted(
+      env_, j_voip_client_, NativeToJavaList(env_, names, convert_function));
 }
 
-webrtc::ScopedJavaLocalRef<jstring> AndroidVoipClient::GetLocalIPAddress(
-    JNIEnv* env) {
+void AndroidVoipClient::GetLocalIPAddress(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(GetLocalIPAddress, env);
+
+  std::string local_ip_address;
   rtc::IPAddress ipv4_address = QueryDefaultLocalAddress(AF_INET);
   if (!ipv4_address.IsNil()) {
-    return webrtc::NativeToJavaString(env, ipv4_address.ToString());
+    local_ip_address = ipv4_address.ToString();
+  } else {
+    rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6);
+    if (!ipv6_address.IsNil()) {
+      local_ip_address = ipv6_address.ToString();
+    }
   }
-  rtc::IPAddress ipv6_address = QueryDefaultLocalAddress(AF_INET6);
-  if (!ipv6_address.IsNil()) {
-    return webrtc::NativeToJavaString(env, ipv6_address.ToString());
-  }
-  return webrtc::NativeToJavaString(env, "");
+  Java_VoipClient_onGetLocalIPAddressCompleted(
+      env_, j_voip_client_, webrtc::NativeToJavaString(env_, local_ip_address));
 }
 
-void AndroidVoipClient::SetEncoder(
-    JNIEnv* env,
-    const webrtc::JavaRef<jstring>& j_encoder_string) {
+void AndroidVoipClient::SetEncoder(const std::string& encoder) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
     return;
   }
-  const std::string& chosen_encoder =
-      webrtc::JavaToNativeString(env, j_encoder_string);
-  for (const webrtc::AudioCodecSpec& encoder : supported_codecs_) {
-    if (encoder.format.name == chosen_encoder) {
+  for (const webrtc::AudioCodecSpec& codec : supported_codecs_) {
+    if (codec.format.name == encoder) {
       voip_engine_->Codec().SetSendCodec(
-          *channel_, GetPayloadType(encoder.format.name), encoder.format);
-      break;
+          *channel_, GetPayloadType(codec.format.name), codec.format);
+      return;
     }
   }
 }
 
-void AndroidVoipClient::SetDecoders(
+void AndroidVoipClient::SetEncoder(
     JNIEnv* env,
-    const webrtc::JavaParamRef<jobject>& j_decoder_strings) {
+    const webrtc::JavaParamRef<jstring>& j_encoder_string) {
+  const std::string& chosen_encoder =
+      webrtc::JavaToNativeString(env, j_encoder_string);
+  voip_thread_->PostTask(
+      RTC_FROM_HERE, [this, chosen_encoder] { SetEncoder(chosen_encoder); });
+}
+
+void AndroidVoipClient::SetDecoders(const std::vector<std::string>& decoders) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
     return;
   }
-  std::vector<std::string> chosen_decoders =
-      webrtc::JavaListToNativeVector<std::string, jstring>(
-          env, j_decoder_strings, &webrtc::JavaToNativeString);
   std::map<int, webrtc::SdpAudioFormat> decoder_specs;
-
-  for (const webrtc::AudioCodecSpec& decoder : supported_codecs_) {
-    if (std::find(chosen_decoders.begin(), chosen_decoders.end(),
-                  decoder.format.name) != chosen_decoders.end()) {
-      decoder_specs.insert(
-          {GetPayloadType(decoder.format.name), decoder.format});
+  for (const webrtc::AudioCodecSpec& codec : supported_codecs_) {
+    if (std::find(decoders.begin(), decoders.end(), codec.format.name) !=
+        decoders.end()) {
+      decoder_specs.insert({GetPayloadType(codec.format.name), codec.format});
     }
   }
 
   voip_engine_->Codec().SetReceiveCodecs(*channel_, decoder_specs);
 }
 
+void AndroidVoipClient::SetDecoders(
+    JNIEnv* env,
+    const webrtc::JavaParamRef<jobject>& j_decoder_strings) {
+  const std::vector<std::string>& chosen_decoders =
+      webrtc::JavaListToNativeVector<std::string, jstring>(
+          env, j_decoder_strings, &webrtc::JavaToNativeString);
+  voip_thread_->PostTask(
+      RTC_FROM_HERE, [this, chosen_decoders] { SetDecoders(chosen_decoders); });
+}
+
+void AndroidVoipClient::SetLocalAddress(const std::string& ip_address,
+                                        const int port_number) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+  rtp_local_address_ = rtc::SocketAddress(ip_address, port_number);
+  rtcp_local_address_ = rtc::SocketAddress(ip_address, port_number + 1);
+}
+
 void AndroidVoipClient::SetLocalAddress(
     JNIEnv* env,
-    const webrtc::JavaRef<jstring>& j_ip_address_string,
+    const webrtc::JavaParamRef<jstring>& j_ip_address_string,
     jint j_port_number_int) {
   const std::string& ip_address =
       webrtc::JavaToNativeString(env, j_ip_address_string);
-  rtp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int);
-  rtcp_local_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1);
+  voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] {
+    SetLocalAddress(ip_address, j_port_number_int);
+  });
+}
+
+void AndroidVoipClient::SetRemoteAddress(const std::string& ip_address,
+                                         const int port_number) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+  rtp_remote_address_ = rtc::SocketAddress(ip_address, port_number);
+  rtcp_remote_address_ = rtc::SocketAddress(ip_address, port_number + 1);
 }
 
 void AndroidVoipClient::SetRemoteAddress(
     JNIEnv* env,
-    const webrtc::JavaRef<jstring>& j_ip_address_string,
+    const webrtc::JavaParamRef<jstring>& j_ip_address_string,
     jint j_port_number_int) {
   const std::string& ip_address =
       webrtc::JavaToNativeString(env, j_ip_address_string);
-  rtp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int);
-  rtcp_remote_address_ = rtc::SocketAddress(ip_address, j_port_number_int + 1);
+  voip_thread_->PostTask(RTC_FROM_HERE, [this, ip_address, j_port_number_int] {
+    SetRemoteAddress(ip_address, j_port_number_int);
+  });
 }
 
-jboolean AndroidVoipClient::StartSession(JNIEnv* env) {
-  // Due to consistent thread requirement on
-  // modules/utility/source/process_thread_impl.cc,
-  // code is invoked in the context of voip_thread_.
-  channel_ = voip_thread_->Invoke<absl::optional<webrtc::ChannelId>>(
-      RTC_FROM_HERE,
-      [this] { return voip_engine_->Base().CreateChannel(this, 0); });
+void AndroidVoipClient::StartSession(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StartSession, env);
+
+  channel_ = voip_engine_->Base().CreateChannel(this, absl::nullopt);
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel creation failed";
-    return false;
+    Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
+                                            /*isSuccessful=*/false);
+    return;
   }
 
   rtp_socket_.reset(rtc::AsyncUDPSocket::Create(voip_thread_->socketserver(),
                                                 rtp_local_address_));
   if (!rtp_socket_) {
     RTC_LOG_ERR(LERROR) << "Socket creation failed";
-    return false;
+    Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
+                                            /*isSuccessful=*/false);
+    return;
   }
   rtp_socket_->SignalReadPacket.connect(
       this, &AndroidVoipClient::OnSignalReadRTPPacket);
@@ -262,123 +328,171 @@
                                                  rtcp_local_address_));
   if (!rtcp_socket_) {
     RTC_LOG_ERR(LERROR) << "Socket creation failed";
-    return false;
+    Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
+                                            /*isSuccessful=*/false);
+    return;
   }
   rtcp_socket_->SignalReadPacket.connect(
       this, &AndroidVoipClient::OnSignalReadRTCPPacket);
-
-  return true;
+  Java_VoipClient_onStartSessionCompleted(env_, j_voip_client_,
+                                          /*isSuccessful=*/true);
 }
 
-jboolean AndroidVoipClient::StopSession(JNIEnv* env) {
+void AndroidVoipClient::StopSession(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StopSession, env);
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return false;
+    Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_,
+                                           /*isSuccessful=*/false);
+    return;
   }
-  if (!StopSend(env) || !StopPlayout(env)) {
-    return false;
+  if (!voip_engine_->Base().StopSend(*channel_) ||
+      !voip_engine_->Base().StopPlayout(*channel_)) {
+    Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_,
+                                           /*isSuccessful=*/false);
+    return;
   }
 
   rtp_socket_->Close();
   rtcp_socket_->Close();
-  // Due to consistent thread requirement on
-  // modules/utility/source/process_thread_impl.cc,
-  // code is invoked in the context of voip_thread_.
-  voip_thread_->Invoke<void>(RTC_FROM_HERE, [this] {
-    voip_engine_->Base().ReleaseChannel(*channel_);
-  });
+
+  voip_engine_->Base().ReleaseChannel(*channel_);
   channel_ = absl::nullopt;
-  return true;
+  Java_VoipClient_onStopSessionCompleted(env_, j_voip_client_,
+                                         /*isSuccessful=*/true);
 }
 
-jboolean AndroidVoipClient::StartSend(JNIEnv* env) {
+void AndroidVoipClient::StartSend(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StartSend, env);
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return false;
+    Java_VoipClient_onStartSendCompleted(env_, j_voip_client_,
+                                         /*isSuccessful=*/false);
+    return;
   }
-  // Due to consistent thread requirement on
-  // modules/audio_device/android/opensles_recorder.cc,
-  // code is invoked in the context of voip_thread_.
-  return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
-    return voip_engine_->Base().StartSend(*channel_);
-  });
+  Java_VoipClient_onStartSendCompleted(
+      env_, j_voip_client_, voip_engine_->Base().StartSend(*channel_));
 }
 
-jboolean AndroidVoipClient::StopSend(JNIEnv* env) {
+void AndroidVoipClient::StopSend(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StopSend, env);
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return false;
+    Java_VoipClient_onStopSendCompleted(env_, j_voip_client_,
+                                        /*isSuccessful=*/false);
+    return;
   }
-  // Due to consistent thread requirement on
-  // modules/audio_device/android/opensles_recorder.cc,
-  // code is invoked in the context of voip_thread_.
-  return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
-    return voip_engine_->Base().StopSend(*channel_);
-  });
+  Java_VoipClient_onStopSendCompleted(env_, j_voip_client_,
+                                      voip_engine_->Base().StopSend(*channel_));
 }
 
-jboolean AndroidVoipClient::StartPlayout(JNIEnv* env) {
+void AndroidVoipClient::StartPlayout(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StartPlayout, env);
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return false;
+    Java_VoipClient_onStartPlayoutCompleted(env_, j_voip_client_,
+                                            /*isSuccessful=*/false);
+    return;
   }
-  // Due to consistent thread requirement on
-  // modules/audio_device/android/opensles_player.cc,
-  // code is invoked in the context of voip_thread_.
-  return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
-    return voip_engine_->Base().StartPlayout(*channel_);
-  });
+  Java_VoipClient_onStartPlayoutCompleted(
+      env_, j_voip_client_, voip_engine_->Base().StartPlayout(*channel_));
 }
 
-jboolean AndroidVoipClient::StopPlayout(JNIEnv* env) {
+void AndroidVoipClient::StopPlayout(JNIEnv* env) {
+  RUN_ON_VOIP_THREAD(StopPlayout, env);
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return false;
+    Java_VoipClient_onStopPlayoutCompleted(env_, j_voip_client_,
+                                           /*isSuccessful=*/false);
+    return;
   }
-  // Due to consistent thread requirement on
-  // modules/audio_device/android/opensles_player.cc,
-  // code is invoked in the context of voip_thread_.
-  return voip_thread_->Invoke<bool>(RTC_FROM_HERE, [this] {
-    return voip_engine_->Base().StopPlayout(*channel_);
-  });
+  Java_VoipClient_onStopPlayoutCompleted(
+      env_, j_voip_client_, voip_engine_->Base().StopPlayout(*channel_));
 }
 
 void AndroidVoipClient::Delete(JNIEnv* env) {
   delete this;
 }
 
+void AndroidVoipClient::SendRtpPacket(const std::vector<uint8_t>& packet_copy) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+  if (!rtp_socket_->SendTo(packet_copy.data(), packet_copy.size(),
+                           rtp_remote_address_, rtc::PacketOptions())) {
+    RTC_LOG(LS_ERROR) << "Failed to send RTP packet";
+  }
+}
+
 bool AndroidVoipClient::SendRtp(const uint8_t* packet,
                                 size_t length,
                                 const webrtc::PacketOptions& options) {
-  if (!rtp_socket_->SendTo(packet, length, rtp_remote_address_,
-                           rtc::PacketOptions())) {
-    RTC_LOG(LS_ERROR) << "Failed to send RTP packet";
-    return false;
-  }
+  std::vector<uint8_t> packet_copy(packet, packet + length);
+  voip_thread_->PostTask(RTC_FROM_HERE,
+                         [this, packet_copy = std::move(packet_copy)] {
+                           SendRtpPacket(packet_copy);
+                         });
   return true;
 }
 
-bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) {
-  if (!rtcp_socket_->SendTo(packet, length, rtcp_remote_address_,
-                            rtc::PacketOptions())) {
+void AndroidVoipClient::SendRtcpPacket(
+    const std::vector<uint8_t>& packet_copy) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+  if (!rtcp_socket_->SendTo(packet_copy.data(), packet_copy.size(),
+                            rtcp_remote_address_, rtc::PacketOptions())) {
     RTC_LOG(LS_ERROR) << "Failed to send RTCP packet";
-    return false;
   }
+}
+
+bool AndroidVoipClient::SendRtcp(const uint8_t* packet, size_t length) {
+  std::vector<uint8_t> packet_copy(packet, packet + length);
+  voip_thread_->PostTask(RTC_FROM_HERE,
+                         [this, packet_copy = std::move(packet_copy)] {
+                           SendRtcpPacket(packet_copy);
+                         });
   return true;
 }
 
+void AndroidVoipClient::ReadRTPPacket(const std::vector<uint8_t>& packet_copy) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
+  if (!channel_) {
+    RTC_LOG(LS_ERROR) << "Channel has not been created";
+    return;
+  }
+  voip_engine_->Network().ReceivedRTPPacket(
+      *channel_,
+      rtc::ArrayView<const uint8_t>(packet_copy.data(), packet_copy.size()));
+}
+
 void AndroidVoipClient::OnSignalReadRTPPacket(rtc::AsyncPacketSocket* socket,
                                               const char* rtp_packet,
                                               size_t size,
                                               const rtc::SocketAddress& addr,
                                               const int64_t& timestamp) {
+  std::vector<uint8_t> packet_copy(rtp_packet, rtp_packet + size);
+  voip_thread_->PostTask(RTC_FROM_HERE,
+                         [this, packet_copy = std::move(packet_copy)] {
+                           ReadRTPPacket(packet_copy);
+                         });
+}
+
+void AndroidVoipClient::ReadRTCPPacket(
+    const std::vector<uint8_t>& packet_copy) {
+  RTC_DCHECK_RUN_ON(voip_thread_.get());
+
   if (!channel_) {
     RTC_LOG(LS_ERROR) << "Channel has not been created";
     return;
   }
-  voip_engine_->Network().ReceivedRTPPacket(
-      *channel_, rtc::ArrayView<const uint8_t>(
-                     reinterpret_cast<const uint8_t*>(rtp_packet), size));
+  voip_engine_->Network().ReceivedRTCPPacket(
+      *channel_,
+      rtc::ArrayView<const uint8_t>(packet_copy.data(), packet_copy.size()));
 }
 
 void AndroidVoipClient::OnSignalReadRTCPPacket(rtc::AsyncPacketSocket* socket,
@@ -386,20 +500,19 @@
                                                size_t size,
                                                const rtc::SocketAddress& addr,
                                                const int64_t& timestamp) {
-  if (!channel_) {
-    RTC_LOG(LS_ERROR) << "Channel has not been created";
-    return;
-  }
-  voip_engine_->Network().ReceivedRTCPPacket(
-      *channel_, rtc::ArrayView<const uint8_t>(
-                     reinterpret_cast<const uint8_t*>(rtcp_packet), size));
+  std::vector<uint8_t> packet_copy(rtcp_packet, rtcp_packet + size);
+  voip_thread_->PostTask(RTC_FROM_HERE,
+                         [this, packet_copy = std::move(packet_copy)] {
+                           ReadRTCPPacket(packet_copy);
+                         });
 }
 
 static jlong JNI_VoipClient_CreateClient(
     JNIEnv* env,
-    const webrtc::JavaParamRef<jobject>& application_context) {
+    const webrtc::JavaParamRef<jobject>& application_context,
+    const webrtc::JavaParamRef<jobject>& j_voip_client) {
   return webrtc::NativeToJavaPointer(
-      AndroidVoipClient::Create(env, application_context));
+      AndroidVoipClient::Create(env, application_context, j_voip_client));
 }
 
 }  // namespace webrtc_examples
diff --git a/examples/androidvoip/jni/android_voip_client.h b/examples/androidvoip/jni/android_voip_client.h
index aed652e..4dd0b0a 100644
--- a/examples/androidvoip/jni/android_voip_client.h
+++ b/examples/androidvoip/jni/android_voip_client.h
@@ -36,10 +36,10 @@
 // webrtc::Transport to send RTP/RTCP packets to the remote endpoint.
 // It also creates methods (slots) for sockets to connect to in
 // order to receive RTP/RTCP packets. AndroidVoipClient does all
-// VoipBase related operations with rtc::Thread (voip_thread_), this
-// is to comply with consistent thread usage requirement with
-// ProcessThread used within VoipEngine. AndroidVoipClient is meant
-// to be used by Java through JNI.
+// operations with rtc::Thread (voip_thread_), this is to comply
+// with consistent thread usage requirement with ProcessThread used
+// within VoipEngine, as well as providing asynchronicity to the
+// caller. AndroidVoipClient is meant to be used by Java through JNI.
 class AndroidVoipClient : public webrtc::Transport,
                           public sigslot::has_slots<> {
  public:
@@ -50,22 +50,24 @@
   // they are done with it (this class provides a Delete() method).
   static AndroidVoipClient* Create(
       JNIEnv* env,
-      const webrtc::JavaParamRef<jobject>& application_context);
+      const webrtc::JavaParamRef<jobject>& application_context,
+      const webrtc::JavaParamRef<jobject>& j_voip_client);
 
   ~AndroidVoipClient() override;
 
-  // Returns a Java List of Strings containing names of the built-in
-  // supported codecs.
-  webrtc::ScopedJavaLocalRef<jobject> GetSupportedCodecs(JNIEnv* env);
+  // Provides client with a Java List of Strings containing names of
+  // the built-in supported codecs through callback.
+  void GetSupportedCodecs(JNIEnv* env);
 
-  // Returns a Java String of the default local IPv4 address. If IPv4
-  // address is not found, returns the default local IPv6 address. If
-  // IPv6 address is not found, returns an empty string.
-  webrtc::ScopedJavaLocalRef<jstring> GetLocalIPAddress(JNIEnv* env);
+  // Provides client with a Java String of the default local IPv4 address
+  // through callback. If IPv4 address is not found, provide the default
+  // local IPv6 address. If IPv6 address is not found, provide an empty
+  // string.
+  void GetLocalIPAddress(JNIEnv* env);
 
   // Sets the encoder used by the VoIP API.
   void SetEncoder(JNIEnv* env,
-                  const webrtc::JavaRef<jstring>& j_encoder_string);
+                  const webrtc::JavaParamRef<jstring>& j_encoder_string);
 
   // Sets the decoders used by the VoIP API.
   void SetDecoders(JNIEnv* env,
@@ -76,36 +78,41 @@
   // and port number j_port_number_int, the RTCP address will have IP address
   // j_ip_address_string and port number j_port_number_int+1.
   void SetLocalAddress(JNIEnv* env,
-                       const webrtc::JavaRef<jstring>& j_ip_address_string,
+                       const webrtc::JavaParamRef<jstring>& j_ip_address_string,
                        jint j_port_number_int);
-  void SetRemoteAddress(JNIEnv* env,
-                        const webrtc::JavaRef<jstring>& j_ip_address_string,
-                        jint j_port_number_int);
+  void SetRemoteAddress(
+      JNIEnv* env,
+      const webrtc::JavaParamRef<jstring>& j_ip_address_string,
+      jint j_port_number_int);
 
-  // Starts a VoIP session. The VoIP operations below can only be
-  // used after a session has already started. Returns true if session
-  // started successfully and false otherwise.
-  jboolean StartSession(JNIEnv* env);
+  // Starts a VoIP session, then calls a callback method with a boolean
+  // value indicating if the session has started successfully. The VoIP
+  // operations below can only be used after a session has already started.
+  void StartSession(JNIEnv* env);
 
-  // Stops the current session. Returns true if session stopped
-  // successfully and false otherwise.
-  jboolean StopSession(JNIEnv* env);
+  // Stops the current session, then calls a callback method with a
+  // boolean value indicating if the session has stopped successfully.
+  void StopSession(JNIEnv* env);
 
-  // Starts sending RTP/RTCP packets to the remote endpoint. Returns
-  // the return value of StartSend in api/voip/voip_base.h.
-  jboolean StartSend(JNIEnv* env);
+  // Starts sending RTP/RTCP packets to the remote endpoint, then calls
+  // a callback method with a boolean value indicating if sending
+  // has started successfully.
+  void StartSend(JNIEnv* env);
 
-  // Stops sending RTP/RTCP packets to the remote endpoint. Returns
-  // the return value of StopSend in api/voip/voip_base.h.
-  jboolean StopSend(JNIEnv* env);
+  // Stops sending RTP/RTCP packets to the remote endpoint, then calls
+  // a callback method with a boolean value indicating if sending
+  // has stopped successfully.
+  void StopSend(JNIEnv* env);
 
-  // Starts playing out the voice data received from the remote endpoint.
-  // Returns the return value of StartPlayout in api/voip/voip_base.h.
-  jboolean StartPlayout(JNIEnv* env);
+  // Starts playing out the voice data received from the remote endpoint,
+  // then calls a callback method with a boolean value indicating if
+  // playout has started successfully.
+  void StartPlayout(JNIEnv* env);
 
-  // Stops playing out the voice data received from the remote endpoint.
-  // Returns the return value of StopPlayout in api/voip/voip_base.h.
-  jboolean StopPlayout(JNIEnv* env);
+  // Stops playing out the voice data received from the remote endpoint,
+  // then calls a callback method with a boolean value indicating if
+  // playout has stopped successfully.
+  void StopPlayout(JNIEnv* env);
 
   // Deletes this object. Used by client when they are done.
   void Delete(JNIEnv* env);
@@ -130,25 +137,51 @@
 
  private:
   AndroidVoipClient(JNIEnv* env,
-                    const webrtc::JavaParamRef<jobject>& application_context);
+                    const webrtc::JavaParamRef<jobject>& j_voip_client)
+      : voip_thread_(rtc::Thread::CreateWithSocketServer()),
+        j_voip_client_(env, j_voip_client) {}
 
-  // Used to invoke VoipBase operations and send/receive
-  // RTP/RTCP packets.
+  bool Init(JNIEnv* env,
+            const webrtc::JavaParamRef<jobject>& application_context);
+
+  // Overloaded methods having native C++ variables as arguments.
+  void SetEncoder(const std::string& encoder);
+  void SetDecoders(const std::vector<std::string>& decoders);
+  void SetLocalAddress(const std::string& ip_address, const int port_number);
+  void SetRemoteAddress(const std::string& ip_address, const int port_number);
+
+  // Methods to send and receive RTP/RTCP packets. Takes in a
+  // copy of a packet as a vector to prolong the lifetime of
+  // the packet as these methods will be called asynchronously.
+  void SendRtpPacket(const std::vector<uint8_t>& packet_copy);
+  void SendRtcpPacket(const std::vector<uint8_t>& packet_copy);
+  void ReadRTPPacket(const std::vector<uint8_t>& packet_copy);
+  void ReadRTCPPacket(const std::vector<uint8_t>& packet_copy);
+
+  // Used to invoke operations and send/receive RTP/RTCP packets.
   std::unique_ptr<rtc::Thread> voip_thread_;
+  // Reference to the VoipClient java instance used to
+  // invoke callbacks when operations are finished.
+  webrtc::ScopedJavaGlobalRef<jobject> j_voip_client_
+      RTC_GUARDED_BY(voip_thread_);
   // A list of AudioCodecSpec supported by the built-in
   // encoder/decoder factories.
-  std::vector<webrtc::AudioCodecSpec> supported_codecs_;
+  std::vector<webrtc::AudioCodecSpec> supported_codecs_
+      RTC_GUARDED_BY(voip_thread_);
+  // A JNI context used by the voip_thread_.
+  JNIEnv* env_ RTC_GUARDED_BY(voip_thread_);
   // The entry point to all VoIP APIs.
-  std::unique_ptr<webrtc::VoipEngine> voip_engine_;
+  std::unique_ptr<webrtc::VoipEngine> voip_engine_ RTC_GUARDED_BY(voip_thread_);
   // Used by the VoIP API to facilitate a VoIP session.
-  absl::optional<webrtc::ChannelId> channel_;
+  absl::optional<webrtc::ChannelId> channel_ RTC_GUARDED_BY(voip_thread_);
   // Members below are used for network related operations.
-  std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_;
-  std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_;
-  rtc::SocketAddress rtp_local_address_;
-  rtc::SocketAddress rtcp_local_address_;
-  rtc::SocketAddress rtp_remote_address_;
-  rtc::SocketAddress rtcp_remote_address_;
+  std::unique_ptr<rtc::AsyncUDPSocket> rtp_socket_ RTC_GUARDED_BY(voip_thread_);
+  std::unique_ptr<rtc::AsyncUDPSocket> rtcp_socket_
+      RTC_GUARDED_BY(voip_thread_);
+  rtc::SocketAddress rtp_local_address_ RTC_GUARDED_BY(voip_thread_);
+  rtc::SocketAddress rtcp_local_address_ RTC_GUARDED_BY(voip_thread_);
+  rtc::SocketAddress rtp_remote_address_ RTC_GUARDED_BY(voip_thread_);
+  rtc::SocketAddress rtcp_remote_address_ RTC_GUARDED_BY(voip_thread_);
 };
 
 }  // namespace webrtc_examples