Android: Generate JNI code for DataChannel

Bug: webrtc:8278
Change-Id: I107c839656500971cbd3da7557e14776759c318a
Reviewed-on: https://webrtc-review.googlesource.com/25820
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20873}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index ecf8a1e..c346563 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -278,6 +278,7 @@
 
 generate_jni("generated_peerconnection_jni") {
   sources = [
+    "api/org/webrtc/DataChannel.java",
     "api/org/webrtc/MediaConstraints.java",
     "api/org/webrtc/NetworkMonitor.java",
     "api/org/webrtc/NetworkMonitorAutoDetect.java",
@@ -293,9 +294,8 @@
     "src/jni/pc/androidnetworkmonitor_jni.h",
     "src/jni/pc/audiotrack_jni.cc",
     "src/jni/pc/callsessionfilerotatinglogsink_jni.cc",
-    "src/jni/pc/datachannel_jni.cc",
-    "src/jni/pc/datachannelobserver_jni.cc",
-    "src/jni/pc/datachannelobserver_jni.h",
+    "src/jni/pc/datachannel.cc",
+    "src/jni/pc/datachannel.h",
     "src/jni/pc/dtmfsender_jni.cc",
     "src/jni/pc/java_native_conversion.cc",
     "src/jni/pc/java_native_conversion.h",
diff --git a/sdk/android/api/org/webrtc/DataChannel.java b/sdk/android/api/org/webrtc/DataChannel.java
index dc64ccc..883025e 100644
--- a/sdk/android/api/org/webrtc/DataChannel.java
+++ b/sdk/android/api/org/webrtc/DataChannel.java
@@ -26,17 +26,34 @@
     // Optional unsigned short in WebIDL, -1 means unspecified.
     public int id = -1;
 
-    public Init() {}
+    @CalledByNative("Init")
+    boolean getOrdered() {
+      return ordered;
+    }
 
-    // Called only by native code.
-    private Init(boolean ordered, int maxRetransmitTimeMs, int maxRetransmits, String protocol,
-        boolean negotiated, int id) {
-      this.ordered = ordered;
-      this.maxRetransmitTimeMs = maxRetransmitTimeMs;
-      this.maxRetransmits = maxRetransmits;
-      this.protocol = protocol;
-      this.negotiated = negotiated;
-      this.id = id;
+    @CalledByNative("Init")
+    int getMaxRetransmitTimeMs() {
+      return maxRetransmitTimeMs;
+    }
+
+    @CalledByNative("Init")
+    int getMaxRetransmits() {
+      return maxRetransmits;
+    }
+
+    @CalledByNative("Init")
+    String getProtocol() {
+      return protocol;
+    }
+
+    @CalledByNative("Init")
+    boolean getNegotiated() {
+      return negotiated;
+    }
+
+    @CalledByNative("Init")
+    int getId() {
+      return id;
     }
   }
 
@@ -51,6 +68,7 @@
      */
     public final boolean binary;
 
+    @CalledByNative("Buffer")
     public Buffer(ByteBuffer data, boolean binary) {
       this.data = data;
       this.binary = binary;
@@ -60,23 +78,34 @@
   /** Java version of C++ DataChannelObserver. */
   public interface Observer {
     /** The data channel's bufferedAmount has changed. */
-    public void onBufferedAmountChange(long previousAmount);
+    @CalledByNative("Observer") public void onBufferedAmountChange(long previousAmount);
     /** The data channel state has changed. */
-    public void onStateChange();
+    @CalledByNative("Observer") public void onStateChange();
     /**
      * A data buffer was successfully received.  NOTE: |buffer.data| will be
      * freed once this function returns so callers who want to use the data
      * asynchronously must make sure to copy it first.
      */
-    public void onMessage(Buffer buffer);
+    @CalledByNative("Observer") public void onMessage(Buffer buffer);
   }
 
   /** Keep in sync with DataChannelInterface::DataState. */
-  public enum State { CONNECTING, OPEN, CLOSING, CLOSED }
+  public enum State {
+    CONNECTING,
+    OPEN,
+    CLOSING,
+    CLOSED;
+
+    @CalledByNative("State")
+    static State fromNativeIndex(int nativeIndex) {
+      return values()[nativeIndex];
+    }
+  }
 
   private final long nativeDataChannel;
   private long nativeObserver;
 
+  @CalledByNative
   public DataChannel(long nativeDataChannel) {
     this.nativeDataChannel = nativeDataChannel;
   }
@@ -123,5 +152,12 @@
   private native boolean sendNative(byte[] data, boolean binary);
 
   /** Dispose of native resources attached to this channel. */
-  public native void dispose();
+  public void dispose() {
+    JniCommon.nativeReleaseRef(nativeDataChannel);
+  }
+
+  @CalledByNative
+  long getNativeDataChannel() {
+    return nativeDataChannel;
+  }
 };
diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc
index 910aa56..1187d3e 100644
--- a/sdk/android/src/jni/classreferenceholder.cc
+++ b/sdk/android/src/jni/classreferenceholder.cc
@@ -63,10 +63,6 @@
   LoadClass(jni, "org/webrtc/Camera1Enumerator");
   LoadClass(jni, "org/webrtc/Camera2Enumerator");
   LoadClass(jni, "org/webrtc/CameraEnumerationAndroid");
-  LoadClass(jni, "org/webrtc/DataChannel");
-  LoadClass(jni, "org/webrtc/DataChannel$Buffer");
-  LoadClass(jni, "org/webrtc/DataChannel$Init");
-  LoadClass(jni, "org/webrtc/DataChannel$State");
   LoadClass(jni, "org/webrtc/EglBase");
   LoadClass(jni, "org/webrtc/EglBase$Context");
   LoadClass(jni, "org/webrtc/EglBase14$Context");
diff --git a/sdk/android/src/jni/pc/datachannel.cc b/sdk/android/src/jni/pc/datachannel.cc
new file mode 100644
index 0000000..758e4b9
--- /dev/null
+++ b/sdk/android/src/jni/pc/datachannel.cc
@@ -0,0 +1,159 @@
+/*
+ *  Copyright 2017 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 <memory>
+
+#include <limits>
+
+#include "api/datachannelinterface.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/ptr_util.h"
+#include "sdk/android/generated_peerconnection_jni/jni/DataChannel_jni.h"
+#include "sdk/android/src/jni/jni_helpers.h"
+#include "sdk/android/src/jni/pc/datachannel.h"
+
+namespace webrtc {
+namespace jni {
+
+namespace {
+// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver
+// and dispatching the callback from C++ back to Java.
+class DataChannelObserverJni : public DataChannelObserver {
+ public:
+  DataChannelObserverJni(JNIEnv* jni, jobject j_observer);
+  virtual ~DataChannelObserverJni() {}
+
+  void OnBufferedAmountChange(uint64_t previous_amount) override;
+  void OnStateChange() override;
+  void OnMessage(const DataBuffer& buffer) override;
+
+ private:
+  const ScopedGlobalRef<jobject> j_observer_global_;
+};
+
+DataChannelObserverJni::DataChannelObserverJni(JNIEnv* jni, jobject j_observer)
+    : j_observer_global_(jni, j_observer) {}
+
+void DataChannelObserverJni::OnBufferedAmountChange(uint64_t previous_amount) {
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  Java_Observer_onBufferedAmountChange(env, *j_observer_global_,
+                                       previous_amount);
+}
+
+void DataChannelObserverJni::OnStateChange() {
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  Java_Observer_onStateChange(env, *j_observer_global_);
+}
+
+void DataChannelObserverJni::OnMessage(const DataBuffer& buffer) {
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  ScopedLocalRefFrame local_ref_frame(env);
+  jobject byte_buffer = env->NewDirectByteBuffer(
+      const_cast<char*>(buffer.data.data<char>()), buffer.data.size());
+  jobject j_buffer = Java_Buffer_Constructor(env, byte_buffer, buffer.binary);
+  Java_Observer_onMessage(env, *j_observer_global_, j_buffer);
+}
+
+DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
+  jlong j_d = Java_DataChannel_getNativeDataChannel(jni, j_dc);
+  return reinterpret_cast<DataChannelInterface*>(j_d);
+}
+
+}  // namespace
+
+DataChannelInit JavaToNativeDataChannelInit(JNIEnv* env, jobject j_init) {
+  DataChannelInit init;
+  init.ordered = Java_Init_getOrdered(env, j_init);
+  init.maxRetransmitTime = Java_Init_getMaxRetransmitTimeMs(env, j_init);
+  init.maxRetransmits = Java_Init_getMaxRetransmits(env, j_init);
+  init.protocol = JavaToStdString(env, Java_Init_getProtocol(env, j_init));
+  init.negotiated = Java_Init_getNegotiated(env, j_init);
+  init.id = Java_Init_getId(env, j_init);
+  return init;
+}
+
+jobject WrapNativeDataChannel(
+    JNIEnv* env,
+    rtc::scoped_refptr<DataChannelInterface> channel) {
+  // Channel is now owned by Java object, and will be freed from there.
+  return channel ? Java_DataChannel_Constructor(
+                       env, jlongFromPointer(channel.release()))
+                 : nullptr;
+}
+
+JNI_FUNCTION_DECLARATION(jlong,
+                         DataChannel_registerObserverNative,
+                         JNIEnv* jni,
+                         jobject j_dc,
+                         jobject j_observer) {
+  auto observer = rtc::MakeUnique<DataChannelObserverJni>(jni, j_observer);
+  ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
+  return jlongFromPointer(observer.release());
+}
+
+JNI_FUNCTION_DECLARATION(void,
+                         DataChannel_unregisterObserverNative,
+                         JNIEnv* jni,
+                         jobject j_dc,
+                         jlong native_observer) {
+  ExtractNativeDC(jni, j_dc)->UnregisterObserver();
+  delete reinterpret_cast<DataChannelObserverJni*>(native_observer);
+}
+
+JNI_FUNCTION_DECLARATION(jstring,
+                         DataChannel_label,
+                         JNIEnv* jni,
+                         jobject j_dc) {
+  return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
+}
+
+JNI_FUNCTION_DECLARATION(jint, DataChannel_id, JNIEnv* jni, jobject j_dc) {
+  int id = ExtractNativeDC(jni, j_dc)->id();
+  RTC_CHECK_LE(id, std::numeric_limits<int32_t>::max())
+      << "id overflowed jint!";
+  return static_cast<jint>(id);
+}
+
+JNI_FUNCTION_DECLARATION(jobject,
+                         DataChannel_state,
+                         JNIEnv* jni,
+                         jobject j_dc) {
+  return Java_State_fromNativeIndex(jni, ExtractNativeDC(jni, j_dc)->state());
+}
+
+JNI_FUNCTION_DECLARATION(jlong,
+                         DataChannel_bufferedAmount,
+                         JNIEnv* jni,
+                         jobject j_dc) {
+  uint64_t buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount();
+  RTC_CHECK_LE(buffered_amount, std::numeric_limits<int64_t>::max())
+      << "buffered_amount overflowed jlong!";
+  return static_cast<jlong>(buffered_amount);
+}
+
+JNI_FUNCTION_DECLARATION(void, DataChannel_close, JNIEnv* jni, jobject j_dc) {
+  ExtractNativeDC(jni, j_dc)->Close();
+}
+
+JNI_FUNCTION_DECLARATION(jboolean,
+                         DataChannel_sendNative,
+                         JNIEnv* jni,
+                         jobject j_dc,
+                         jbyteArray data,
+                         jboolean binary) {
+  jbyte* bytes = jni->GetByteArrayElements(data, nullptr);
+  bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer(
+      rtc::CopyOnWriteBuffer(bytes, jni->GetArrayLength(data)), binary));
+  jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
+  return ret;
+}
+
+}  // namespace jni
+}  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/datachannel.h b/sdk/android/src/jni/pc/datachannel.h
new file mode 100644
index 0000000..557520a
--- /dev/null
+++ b/sdk/android/src/jni/pc/datachannel.h
@@ -0,0 +1,25 @@
+/*
+ *  Copyright 2017 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_DATACHANNEL_H_
+#define SDK_ANDROID_SRC_JNI_PC_DATACHANNEL_H_
+
+namespace webrtc {
+namespace jni {
+
+DataChannelInit JavaToNativeDataChannelInit(JNIEnv* env, jobject j_init);
+
+jobject WrapNativeDataChannel(JNIEnv* env,
+                              rtc::scoped_refptr<DataChannelInterface> channel);
+
+}  // namespace jni
+}  // namespace webrtc
+
+#endif  // SDK_ANDROID_SRC_JNI_PC_DATACHANNEL_H_
diff --git a/sdk/android/src/jni/pc/datachannel_jni.cc b/sdk/android/src/jni/pc/datachannel_jni.cc
deleted file mode 100644
index a4e3cb6..0000000
--- a/sdk/android/src/jni/pc/datachannel_jni.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Copyright 2017 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 <memory>
-
-#include "api/datachannelinterface.h"
-#include "sdk/android/src/jni/jni_helpers.h"
-#include "sdk/android/src/jni/pc/datachannelobserver_jni.h"
-
-namespace webrtc {
-namespace jni {
-
-static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
-  jfieldID native_dc_id =
-      GetFieldID(jni, GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
-  jlong j_d = GetLongField(jni, j_dc, native_dc_id);
-  return reinterpret_cast<DataChannelInterface*>(j_d);
-}
-
-JNI_FUNCTION_DECLARATION(jlong,
-                         DataChannel_registerObserverNative,
-                         JNIEnv* jni,
-                         jobject j_dc,
-                         jobject j_observer) {
-  std::unique_ptr<DataChannelObserverJni> observer(
-      new DataChannelObserverJni(jni, j_observer));
-  ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
-  return jlongFromPointer(observer.release());
-}
-
-JNI_FUNCTION_DECLARATION(void,
-                         DataChannel_unregisterObserverNative,
-                         JNIEnv* jni,
-                         jobject j_dc,
-                         jlong native_observer) {
-  ExtractNativeDC(jni, j_dc)->UnregisterObserver();
-  delete reinterpret_cast<DataChannelObserverJni*>(native_observer);
-}
-
-JNI_FUNCTION_DECLARATION(jstring,
-                         DataChannel_label,
-                         JNIEnv* jni,
-                         jobject j_dc) {
-  return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
-}
-
-JNI_FUNCTION_DECLARATION(jint, DataChannel_id, JNIEnv* jni, jobject j_dc) {
-  int id = ExtractNativeDC(jni, j_dc)->id();
-  RTC_CHECK_LE(id, std::numeric_limits<int32_t>::max())
-      << "id overflowed jint!";
-  return static_cast<jint>(id);
-}
-
-JNI_FUNCTION_DECLARATION(jobject,
-                         DataChannel_state,
-                         JNIEnv* jni,
-                         jobject j_dc) {
-  return JavaEnumFromIndexAndClassName(jni, "DataChannel$State",
-                                       ExtractNativeDC(jni, j_dc)->state());
-}
-
-JNI_FUNCTION_DECLARATION(jlong,
-                         DataChannel_bufferedAmount,
-                         JNIEnv* jni,
-                         jobject j_dc) {
-  uint64_t buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount();
-  RTC_CHECK_LE(buffered_amount, std::numeric_limits<int64_t>::max())
-      << "buffered_amount overflowed jlong!";
-  return static_cast<jlong>(buffered_amount);
-}
-
-JNI_FUNCTION_DECLARATION(void, DataChannel_close, JNIEnv* jni, jobject j_dc) {
-  ExtractNativeDC(jni, j_dc)->Close();
-}
-
-JNI_FUNCTION_DECLARATION(jboolean,
-                         DataChannel_sendNative,
-                         JNIEnv* jni,
-                         jobject j_dc,
-                         jbyteArray data,
-                         jboolean binary) {
-  jbyte* bytes = jni->GetByteArrayElements(data, NULL);
-  bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer(
-      rtc::CopyOnWriteBuffer(bytes, jni->GetArrayLength(data)), binary));
-  jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
-  return ret;
-}
-
-JNI_FUNCTION_DECLARATION(void, DataChannel_dispose, JNIEnv* jni, jobject j_dc) {
-  CHECK_RELEASE(ExtractNativeDC(jni, j_dc));
-}
-
-}  // namespace jni
-}  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/datachannelobserver_jni.cc b/sdk/android/src/jni/pc/datachannelobserver_jni.cc
deleted file mode 100644
index 1ca8932..0000000
--- a/sdk/android/src/jni/pc/datachannelobserver_jni.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- *  Copyright 2017 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/datachannelobserver_jni.h"
-
-#include "sdk/android/src/jni/classreferenceholder.h"
-
-namespace webrtc {
-namespace jni {
-
-// Convenience, used since callbacks occur on the signaling thread, which may
-// be a non-Java thread.
-static JNIEnv* jni() {
-  return AttachCurrentThreadIfNeeded();
-}
-
-DataChannelObserverJni::DataChannelObserverJni(JNIEnv* jni, jobject j_observer)
-    : j_observer_global_(jni, j_observer),
-      j_observer_class_(jni, GetObjectClass(jni, j_observer)),
-      j_buffer_class_(jni, FindClass(jni, "org/webrtc/DataChannel$Buffer")),
-      j_on_buffered_amount_change_mid_(GetMethodID(jni,
-                                                   *j_observer_class_,
-                                                   "onBufferedAmountChange",
-                                                   "(J)V")),
-      j_on_state_change_mid_(
-          GetMethodID(jni, *j_observer_class_, "onStateChange", "()V")),
-      j_on_message_mid_(GetMethodID(jni,
-                                    *j_observer_class_,
-                                    "onMessage",
-                                    "(Lorg/webrtc/DataChannel$Buffer;)V")),
-      j_buffer_ctor_(GetMethodID(jni,
-                                 *j_buffer_class_,
-                                 "<init>",
-                                 "(Ljava/nio/ByteBuffer;Z)V")) {}
-
-void DataChannelObserverJni::OnBufferedAmountChange(uint64_t previous_amount) {
-  ScopedLocalRefFrame local_ref_frame(jni());
-  jni()->CallVoidMethod(*j_observer_global_, j_on_buffered_amount_change_mid_,
-                        previous_amount);
-  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
-}
-
-void DataChannelObserverJni::OnStateChange() {
-  ScopedLocalRefFrame local_ref_frame(jni());
-  jni()->CallVoidMethod(*j_observer_global_, j_on_state_change_mid_);
-  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
-}
-
-void DataChannelObserverJni::OnMessage(const DataBuffer& buffer) {
-  ScopedLocalRefFrame local_ref_frame(jni());
-  jobject byte_buffer = jni()->NewDirectByteBuffer(
-      const_cast<char*>(buffer.data.data<char>()), buffer.data.size());
-  jobject j_buffer = jni()->NewObject(*j_buffer_class_, j_buffer_ctor_,
-                                      byte_buffer, buffer.binary);
-  jni()->CallVoidMethod(*j_observer_global_, j_on_message_mid_, j_buffer);
-  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
-}
-
-}  // namespace jni
-}  // namespace webrtc
diff --git a/sdk/android/src/jni/pc/datachannelobserver_jni.h b/sdk/android/src/jni/pc/datachannelobserver_jni.h
deleted file mode 100644
index dcea9de..0000000
--- a/sdk/android/src/jni/pc/datachannelobserver_jni.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  Copyright 2017 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_DATACHANNELOBSERVER_JNI_H_
-#define SDK_ANDROID_SRC_JNI_PC_DATACHANNELOBSERVER_JNI_H_
-
-#include "api/datachannelinterface.h"
-#include "sdk/android/src/jni/jni_helpers.h"
-
-namespace webrtc {
-namespace jni {
-
-// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver
-// and dispatching the callback from C++ back to Java.
-class DataChannelObserverJni : public DataChannelObserver {
- public:
-  DataChannelObserverJni(JNIEnv* jni, jobject j_observer);
-  virtual ~DataChannelObserverJni() {}
-
-  void OnBufferedAmountChange(uint64_t previous_amount) override;
-  void OnStateChange() override;
-  void OnMessage(const DataBuffer& buffer) override;
-
- private:
-  const ScopedGlobalRef<jobject> j_observer_global_;
-  const ScopedGlobalRef<jclass> j_observer_class_;
-  const ScopedGlobalRef<jclass> j_buffer_class_;
-  const jmethodID j_on_buffered_amount_change_mid_;
-  const jmethodID j_on_state_change_mid_;
-  const jmethodID j_on_message_mid_;
-  const jmethodID j_buffer_ctor_;
-};
-
-}  // namespace jni
-}  // namespace webrtc
-
-#endif  // SDK_ANDROID_SRC_JNI_PC_DATACHANNELOBSERVER_JNI_H_
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 31668e9..56a1f4e 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -18,31 +18,6 @@
 namespace webrtc {
 namespace jni {
 
-DataChannelInit JavaToNativeDataChannelInit(JNIEnv* jni, jobject j_init) {
-  DataChannelInit init;
-
-  jclass j_init_class = FindClass(jni, "org/webrtc/DataChannel$Init");
-  jfieldID ordered_id = GetFieldID(jni, j_init_class, "ordered", "Z");
-  jfieldID max_retransmit_time_id =
-      GetFieldID(jni, j_init_class, "maxRetransmitTimeMs", "I");
-  jfieldID max_retransmits_id =
-      GetFieldID(jni, j_init_class, "maxRetransmits", "I");
-  jfieldID protocol_id =
-      GetFieldID(jni, j_init_class, "protocol", "Ljava/lang/String;");
-  jfieldID negotiated_id = GetFieldID(jni, j_init_class, "negotiated", "Z");
-  jfieldID id_id = GetFieldID(jni, j_init_class, "id", "I");
-
-  init.ordered = GetBooleanField(jni, j_init, ordered_id);
-  init.maxRetransmitTime = GetIntField(jni, j_init, max_retransmit_time_id);
-  init.maxRetransmits = GetIntField(jni, j_init, max_retransmits_id);
-  init.protocol =
-      JavaToStdString(jni, GetStringField(jni, j_init, protocol_id));
-  init.negotiated = GetBooleanField(jni, j_init, negotiated_id);
-  init.id = GetIntField(jni, j_init, id_id);
-
-  return init;
-}
-
 jobject NativeToJavaMediaType(JNIEnv* jni, cricket::MediaType media_type) {
   jclass j_media_type_class =
       FindClass(jni, "org/webrtc/MediaStreamTrack$MediaType");
diff --git a/sdk/android/src/jni/pc/java_native_conversion.h b/sdk/android/src/jni/pc/java_native_conversion.h
index 73e1eef..9b74ad3 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.h
+++ b/sdk/android/src/jni/pc/java_native_conversion.h
@@ -30,8 +30,6 @@
 namespace webrtc {
 namespace jni {
 
-DataChannelInit JavaToNativeDataChannelInit(JNIEnv* jni, jobject j_init);
-
 cricket::MediaType JavaToNativeMediaType(JNIEnv* jni, jobject j_media_type);
 
 jobject NativeToJavaMediaType(JNIEnv* jni, cricket::MediaType media_type);
diff --git a/sdk/android/src/jni/pc/peerconnection_jni.cc b/sdk/android/src/jni/pc/peerconnection_jni.cc
index 8183700..075f382 100644
--- a/sdk/android/src/jni/pc/peerconnection_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnection_jni.cc
@@ -37,6 +37,7 @@
 #include "rtc_base/logging.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"
 #include "sdk/android/src/jni/pc/mediaconstraints_jni.h"
 #include "sdk/android/src/jni/pc/peerconnectionobserver_jni.h"
@@ -95,23 +96,7 @@
   rtc::scoped_refptr<DataChannelInterface> channel(
       ExtractNativePC(jni, j_pc)->CreateDataChannel(
           JavaToStdString(jni, j_label), &init));
-  // Mustn't pass channel.get() directly through NewObject to avoid reading its
-  // vararg parameter as 64-bit and reading memory that doesn't belong to the
-  // 32-bit parameter.
-  jlong nativeChannelPtr = jlongFromPointer(channel.get());
-  if (!nativeChannelPtr) {
-    RTC_LOG(LS_ERROR) << "Failed to create DataChannel";
-    return nullptr;
-  }
-  jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel");
-  jmethodID j_data_channel_ctor =
-      GetMethodID(jni, j_data_channel_class, "<init>", "(J)V");
-  jobject j_channel = jni->NewObject(j_data_channel_class, j_data_channel_ctor,
-                                     nativeChannelPtr);
-  CHECK_EXCEPTION(jni) << "error during NewObject";
-  // Channel is now owned by Java object, and will be freed from there.
-  channel->AddRef();
-  return j_channel;
+  return WrapNativeDataChannel(jni, channel);
 }
 
 JNI_FUNCTION_DECLARATION(void,
diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
index a0eac91..03436de 100644
--- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.cc
@@ -15,6 +15,7 @@
 
 #include "rtc_base/ptr_util.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
+#include "sdk/android/src/jni/pc/datachannel.h"
 #include "sdk/android/src/jni/pc/java_native_conversion.h"
 
 namespace webrtc {
@@ -46,9 +47,6 @@
       j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")),
       j_video_track_ctor_(
           GetMethodID(jni, *j_video_track_class_, "<init>", "(J)V")),
-      j_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")),
-      j_data_channel_ctor_(
-          GetMethodID(jni, *j_data_channel_class_, "<init>", "(J)V")),
       j_rtp_receiver_class_(jni, FindClass(jni, "org/webrtc/RtpReceiver")),
       j_rtp_receiver_ctor_(
           GetMethodID(jni, *j_rtp_receiver_class_, "<init>", "(J)V")) {}
@@ -290,23 +288,13 @@
 
 void PeerConnectionObserverJni::OnDataChannel(
     rtc::scoped_refptr<DataChannelInterface> channel) {
-  ScopedLocalRefFrame local_ref_frame(jni());
-  jobject j_channel =
-      jni()->NewObject(*j_data_channel_class_, j_data_channel_ctor_,
-                       jlongFromPointer(channel.get()));
-  CHECK_EXCEPTION(jni()) << "error during NewObject";
-
-  jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
+  JNIEnv* env = AttachCurrentThreadIfNeeded();
+  ScopedLocalRefFrame local_ref_frame(env);
+  jobject j_channel = WrapNativeDataChannel(env, channel);
+  jmethodID m = GetMethodID(env, *j_observer_class_, "onDataChannel",
                             "(Lorg/webrtc/DataChannel;)V");
-  jni()->CallVoidMethod(*j_observer_global_, m, j_channel);
-
-  // Channel is now owned by Java object, and will be freed from
-  // DataChannel.dispose().  Important that this be done _after_ the
-  // CallVoidMethod above as Java code might call back into native code and be
-  // surprised to see a refcount of 2.
-  channel->AddRef();
-
-  CHECK_EXCEPTION(jni()) << "error during CallVoidMethod";
+  env->CallVoidMethod(*j_observer_global_, m, j_channel);
+  CHECK_EXCEPTION(env) << "error during CallVoidMethod";
 }
 
 void PeerConnectionObserverJni::OnRenegotiationNeeded() {
diff --git a/sdk/android/src/jni/pc/peerconnectionobserver_jni.h b/sdk/android/src/jni/pc/peerconnectionobserver_jni.h
index d6fd0f3..44eb319 100644
--- a/sdk/android/src/jni/pc/peerconnectionobserver_jni.h
+++ b/sdk/android/src/jni/pc/peerconnectionobserver_jni.h
@@ -118,8 +118,6 @@
   const jmethodID j_audio_track_ctor_;
   const ScopedGlobalRef<jclass> j_video_track_class_;
   const jmethodID j_video_track_ctor_;
-  const ScopedGlobalRef<jclass> j_data_channel_class_;
-  const jmethodID j_data_channel_ctor_;
   const ScopedGlobalRef<jclass> j_rtp_receiver_class_;
   const jmethodID j_rtp_receiver_ctor_;