diff --git a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactories.h b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactories.h index 7e21f15c..55722b35 100644 --- a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactories.h +++ b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactories.h @@ -32,11 +32,11 @@ namespace jni #ifdef __cpp_lib_any template - static void add(JNIEnv * env, const char * className) + static void add(JNIEnv * env, const char * className, const char * handleClass) { std::unordered_map & map = getFactoryMap(); - map.emplace(std::type_index(typeid(T)), std::make_any>(JavaFactory(env, className))); + map.emplace(std::type_index(typeid(T)), std::make_any>(JavaFactory(env, className, handleClass))); } template @@ -87,11 +87,11 @@ namespace jni } #else template - static void add(JNIEnv * env, const char * className) + static void add(JNIEnv * env, const char * className, const char * handleClass) { std::unordered_map & map = getFactoryMap(); - map.emplace(std::type_index(typeid(T)), make_unique_void(new JavaFactory(env, className))); + map.emplace(std::type_index(typeid(T)), make_unique_void(new JavaFactory(env, className, handleClass))); } template diff --git a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactory.h b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactory.h index c2d0747b..1d4506a1 100644 --- a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactory.h +++ b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaFactory.h @@ -19,8 +19,10 @@ namespace jni class JavaFactory { public: - JavaFactory(JNIEnv * env, const char * className) : - JavaFactory(env, className, "()V") + // The created objects hold their native object in the `nativeHandle` field + // declared by `handleClass`. + JavaFactory(JNIEnv * env, const char * className, const char * handleClass) : + JavaFactory(env, className, "()V", handleClass) { } @@ -34,7 +36,7 @@ namespace jni jobject object = env->NewObject(javaClass, javaCtor); ExceptionCheck(env); - SetHandle(env, object, nativeObject); + SetHandle(env, object, "nativeHandle", nativeObject, handleClass); return JavaLocalRef(env, object); } @@ -45,9 +47,12 @@ namespace jni } protected: - JavaFactory(JNIEnv * env, const char * className, const char * ctorSignature) : + // A subclass that overrides create() binds no handle and may pass no `handleClass`. + JavaFactory(JNIEnv * env, const char * className, const char * ctorSignature, + const char * handleClass) : javaClass(nullptr), - javaCtor(nullptr) + javaCtor(nullptr), + handleClass(handleClass) { loadClass(env, className, ctorSignature); } @@ -63,6 +68,7 @@ namespace jni protected: JavaGlobalRef javaClass; jmethodID javaCtor; + const char * handleClass; }; } diff --git a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaUtils.h b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaUtils.h index aead881d..93944f69 100644 --- a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaUtils.h +++ b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaUtils.h @@ -51,9 +51,11 @@ bool ExceptionCheck(JNIEnv * env); jclass FindClass(JNIEnv * env, const char * name); jmethodID GetMethod(JNIEnv * env, jclass cls, const char * name, const char * sig); jmethodID GetStaticMethod(JNIEnv * env, jclass cls, const char * name, const char * sig); -// Specify the declaring class when subclasses may hide a handle field. +// Resolves a handle field on the class that declares it, never on the dynamic class of `obj`, +// so that a subclass field of the same name cannot hide it. Returns null with a pending +// IllegalArgumentException when `obj` is no instance of `declaringClass`. jfieldID GetHandleField(JNIEnv * env, jobject obj, const std::string & fieldName, - const char * declaringClass = nullptr); + const char * declaringClass); jfieldID GetFieldID(JNIEnv * env, jobject obj, const std::string & fieldName, const char * type); jfieldID GetFieldID(JNIEnv * env, jclass cls, const std::string & fieldName, const char * type); @@ -69,22 +71,9 @@ auto make_unique_void(T * ptr) -> unique_void_ptr }); } -template -jlong GetHandleLong(JNIEnv * env, jobject obj) -{ - jfieldID field = GetHandleField(env, obj, "nativeHandle"); - - if (!field) { - ExceptionCheck(env); - return 0; - } - - return env->GetLongField(obj, field); -} - template T * GetHandle(JNIEnv * env, jobject obj, const std::string & handleName, - const char * declaringClass = nullptr) + const char * declaringClass) { jfieldID field = GetHandleField(env, obj, handleName, declaringClass); @@ -96,15 +85,9 @@ T * GetHandle(JNIEnv * env, jobject obj, const std::string & handleName, return reinterpret_cast(env->GetLongField(obj, field)); } -template -T * GetHandle(JNIEnv * env, jobject obj) -{ - return GetHandle(env, obj, "nativeHandle"); -} - template void SetHandle(JNIEnv * env, jobject obj, const std::string & handleName, T * t, - const char * declaringClass = nullptr) + const char * declaringClass) { jfieldID field = GetHandleField(env, obj, handleName, declaringClass); @@ -118,12 +101,6 @@ void SetHandle(JNIEnv * env, jobject obj, const std::string & handleName, T * t, env->SetLongField(obj, field, handle); } -template -void SetHandle(JNIEnv * env, jobject obj, T * t) -{ - SetHandle(env, obj, "nativeHandle", t); -} - // Replaces the native observer stored in the `handleName` field on `obj` // with `observer`, deleting whatever was stored there before. Use this // together with ClearNativeObserver() wherever a Java object registers a @@ -132,13 +109,14 @@ void SetHandle(JNIEnv * env, jobject obj, T * t) // forgotten and leaked -- as opposed to reimplementing the same // get-old/delete/store-new sequence by hand in each JNI_Foo.cpp. template -void ReplaceNativeObserver(JNIEnv * env, jobject obj, const std::string & handleName, T * observer) +void ReplaceNativeObserver(JNIEnv * env, jobject obj, const std::string & handleName, T * observer, + const char * declaringClass) { - T * old = GetHandle(env, obj, handleName); + T * old = GetHandle(env, obj, handleName, declaringClass); delete old; - SetHandle(env, obj, handleName, observer); + SetHandle(env, obj, handleName, observer, declaringClass); } // Deletes and clears the native observer stored in the `handleName` field @@ -146,12 +124,13 @@ void ReplaceNativeObserver(JNIEnv * env, jobject obj, const std::string & handle // (e.g. from an unregisterObserver()/dispose() implementation that isn't // sure whether one was ever set). template -void ClearNativeObserver(JNIEnv * env, jobject obj, const std::string & handleName) +void ClearNativeObserver(JNIEnv * env, jobject obj, const std::string & handleName, + const char * declaringClass) { - T * observer = GetHandle(env, obj, handleName); + T * observer = GetHandle(env, obj, handleName, declaringClass); if (observer) { - SetHandle(env, obj, handleName, nullptr); + SetHandle(env, obj, handleName, nullptr, declaringClass); delete observer; } } diff --git a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaUtils.cpp b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaUtils.cpp index 835c3eb6..b4957032 100644 --- a/webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaUtils.cpp +++ b/webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaUtils.cpp @@ -17,7 +17,10 @@ #include "JavaWrappedException.h" #include +#include #include +#include +#include bool ExceptionCheck(JNIEnv * env) { @@ -118,21 +121,98 @@ jmethodID GetStaticMethod(JNIEnv * env, jclass cls, const char * name, const cha return method; } +namespace +{ + struct HandleClass + { + jclass cls; + std::map fields; + }; + + struct HandleClasses + { + std::mutex mutex; + std::map> classes; + }; + + HandleClasses & GetHandleClasses() + { + // Never freed: the global class references keep the field IDs valid, and native + // threads may still look up handles while the process exits. + static HandleClasses * handleClasses = new HandleClasses(); + + return *handleClasses; + } +} + jfieldID GetHandleField(JNIEnv * env, jobject obj, const std::string & fieldName, const char * declaringClass) { - if (declaringClass == nullptr) { - return GetFieldID(env, obj, fieldName, "J"); + HandleClasses & handles = GetHandleClasses(); + jclass cls = nullptr; + jfieldID field = nullptr; + + { + std::lock_guard lock(handles.mutex); + + auto entry = handles.classes.find(declaringClass); + + if (entry != handles.classes.end()) { + cls = entry->second.cls; + + auto id = entry->second.fields.find(fieldName); + + if (id != entry->second.fields.end()) { + field = id->second; + } + } } - auto cls = jni::ClassLoaderGetClass(env, declaringClass); + // Resolved without holding the lock, as loading a class calls into Java. + if (cls == nullptr) { + auto localCls = jni::ClassLoaderGetClass(env, declaringClass); - if (cls.get() == nullptr) { - ExceptionCheck(env); + if (localCls.get() == nullptr) { + ExceptionCheck(env); + return nullptr; + } + + jclass globalCls = static_cast(env->NewGlobalRef(localCls.get())); + + std::lock_guard lock(handles.mutex); + + auto entry = handles.classes.emplace(declaringClass, HandleClass { globalCls, {} }); + + if (!entry.second) { + env->DeleteGlobalRef(globalCls); + } + + cls = entry.first->second.cls; + } + + if (field == nullptr) { + field = GetFieldID(env, cls, fieldName, "J"); + + if (field == nullptr) { + return nullptr; + } + + std::lock_guard lock(handles.mutex); + + handles.classes.find(declaringClass)->second.fields.emplace(fieldName, field); + } + + // The field ID is only valid on instances of the class that declares it. + if (obj == nullptr) { + ThrowJavaException(env, "java/lang/NullPointerException", "Object is null"); + return nullptr; + } + if (!env->IsInstanceOf(obj, cls)) { + ThrowJavaException(env, "java/lang/IllegalArgumentException", "Object holds no native handle"); return nullptr; } - return GetFieldID(env, cls.get(), fieldName, "J"); + return field; } jfieldID GetFieldID(JNIEnv * env, jobject obj, const std::string & fieldName, const char * type) diff --git a/webrtc-jni/src/main/cpp/include/NativeHandle.h b/webrtc-jni/src/main/cpp/include/NativeHandle.h new file mode 100644 index 00000000..396ad201 --- /dev/null +++ b/webrtc-jni/src/main/cpp/include/NativeHandle.h @@ -0,0 +1,43 @@ +/* + * Copyright 2026 Alex Andres + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JNI_WEBRTC_NATIVE_HANDLE_H_ +#define JNI_WEBRTC_NATIVE_HANDLE_H_ + +#include "JNI_WebRTC.h" +#include "JavaUtils.h" + +#include + +// The class declaring the field that binds a native object to a Java object. A handle is +// always looked up on its declaring class, as a subclass may declare a field of the same name. +#define NATIVE_OBJECT_CLASS PKG_INTERNAL "NativeObject" + +// Returns the native object bound to the NativeObject `obj`. +template +T * GetHandle(JNIEnv * env, jobject obj) +{ + return GetHandle(env, obj, "nativeHandle", NATIVE_OBJECT_CLASS); +} + +// Binds `t` to the NativeObject `obj`. +template +void SetHandle(JNIEnv * env, jobject obj, T * t) +{ + SetHandle(env, obj, "nativeHandle", t, NATIVE_OBJECT_CLASS); +} + +#endif diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioConverter.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioConverter.cpp index 48ea4b58..f4ea3373 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioConverter.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioConverter.cpp @@ -18,6 +18,7 @@ #include "api/audio/audio_processing.h" #include "media/audio/AudioConverter.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include #include diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp index 5373f131..e1df7d4b 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModule.cpp @@ -20,6 +20,7 @@ #include "JavaError.h" #include "JavaRef.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCContext.h" #include "api/audio/create_audio_device_module.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp index be9101bb..bfa0ae41 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioDeviceModuleBase.cpp @@ -23,6 +23,7 @@ #include "JavaRef.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCContext.h" #include "api/audio/create_audio_device_module.h" #include "media/audio/AudioDevice.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioProcessing.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioProcessing.cpp index 3f7547b1..dcc7e4d2 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioProcessing.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioProcessing.cpp @@ -23,6 +23,7 @@ #include "JavaRef.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCContext.h" #include "media/audio/AudioProcessing.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioResampler.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioResampler.cpp index 45c77781..4aef010e 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioResampler.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioResampler.cpp @@ -19,6 +19,7 @@ #include "JavaObject.h" #include "JavaRef.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "common_audio/resampler/include/push_resampler.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioTrack.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioTrack.cpp index c6bb1440..cadbeb1b 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioTrack.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioTrack.cpp @@ -18,6 +18,7 @@ #include "api/AudioTrackSink.h" #include "JavaNullPointerException.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/media_stream_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp index f5b37d0e..00d78f1f 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_AudioTrackSource.cpp @@ -16,6 +16,7 @@ #include "JNI_AudioTrackSource.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/media_stream_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_CustomAudioSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_CustomAudioSource.cpp index 595a18e2..a6fd85f7 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_CustomAudioSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_CustomAudioSource.cpp @@ -16,6 +16,7 @@ #include "JNI_CustomAudioSource.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/audio/CustomAudioSource.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_CustomVideoSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_CustomVideoSource.cpp index a075fee2..d9fe8352 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_CustomVideoSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_CustomVideoSource.cpp @@ -16,6 +16,7 @@ #include "JNI_CustomVideoSource.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/VideoFrame.h" #include "media/video/CustomVideoSource.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_DesktopCapturer.cpp b/webrtc-jni/src/main/cpp/src/JNI_DesktopCapturer.cpp index 542bfb6c..8a1d3a7a 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_DesktopCapturer.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_DesktopCapturer.cpp @@ -18,6 +18,7 @@ #include "JavaArrayList.h" #include "JavaError.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/video/desktop/DesktopCapturer.h" #include "media/video/desktop/DesktopCaptureCallback.h" #include "media/video/desktop/DesktopSource.h" @@ -34,7 +35,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_desktop_DesktopCapture SetHandle(env, caller, nullptr); - auto callback = GetHandle(env, caller, "callbackHandle"); + auto callback = GetHandle(env, caller, "callbackHandle", PKG_DESKTOP "DesktopCapturer"); if (callback) { delete callback; @@ -119,7 +120,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_desktop_DesktopCapture auto callback = new jni::DesktopCaptureCallback(env, jni::JavaGlobalRef(env, jcallback)); try { - SetHandle(env, caller, "callbackHandle", callback); + SetHandle(env, caller, "callbackHandle", callback, PKG_DESKTOP "DesktopCapturer"); capturer->Start(callback); } diff --git a/webrtc-jni/src/main/cpp/src/JNI_HeadlessAudioDeviceModule.cpp b/webrtc-jni/src/main/cpp/src/JNI_HeadlessAudioDeviceModule.cpp index 52984285..bd3c4856 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_HeadlessAudioDeviceModule.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_HeadlessAudioDeviceModule.cpp @@ -18,6 +18,7 @@ #include "Exception.h" #include "JavaError.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/HeadlessAudioDeviceModule.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaSource.cpp index 72cc887a..c58594ad 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaSource.cpp @@ -17,6 +17,7 @@ #include "JNI_MediaSource.h" #include "JavaEnums.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/media_stream_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaStream.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaStream.cpp index 56823007..a4908950 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaStream.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaStream.cpp @@ -20,6 +20,7 @@ #include "JavaFactories.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/MediaStreamTrackView.h" #include "api/media_stream_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_MediaStreamTrack.cpp b/webrtc-jni/src/main/cpp/src/JNI_MediaStreamTrack.cpp index 25231030..d8876c05 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_MediaStreamTrack.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_MediaStreamTrack.cpp @@ -21,6 +21,7 @@ #include "JavaFactories.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCContext.h" #include "media/MediaStreamTrackObserver.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_NativeI420Buffer.cpp b/webrtc-jni/src/main/cpp/src/JNI_NativeI420Buffer.cpp index 2d03b04c..2089423e 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_NativeI420Buffer.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_NativeI420Buffer.cpp @@ -18,6 +18,7 @@ #include "api/VideoFrame.h" #include "JavaRuntimeException.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/video/i420_buffer.h" #include "common_video/include/video_frame_buffer.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_PeerConnectionFactory.cpp b/webrtc-jni/src/main/cpp/src/JNI_PeerConnectionFactory.cpp index 19c3c6fd..c72e99fe 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_PeerConnectionFactory.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_PeerConnectionFactory.cpp @@ -30,6 +30,7 @@ #include "JavaRef.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCContext.h" #include "api/audio/builtin_audio_processing_builder.h" @@ -169,9 +170,9 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_initialize if (factory != nullptr) { SetHandle(env, caller, factory.release()); - SetHandle(env, caller, "networkThreadHandle", networkThread.release()); - SetHandle(env, caller, "signalingThreadHandle", signalingThread.release()); - SetHandle(env, caller, "audioModuleHandle", proxy.release()); + SetHandle(env, caller, "networkThreadHandle", networkThread.release(), PKG "PeerConnectionFactory"); + SetHandle(env, caller, "signalingThreadHandle", signalingThread.release(), PKG "PeerConnectionFactory"); + SetHandle(env, caller, "audioModuleHandle", proxy.release(), PKG "PeerConnectionFactory"); } else { throw jni::Exception("Create PeerConnectionFactory failed"); @@ -188,9 +189,9 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_dispose webrtc::PeerConnectionFactoryInterface * factory = GetHandle(env, caller); CHECK_HANDLE(factory); - webrtc::Thread * networkThread = GetHandle(env, caller, "networkThreadHandle"); - webrtc::Thread * signalingThread = GetHandle(env, caller, "signalingThreadHandle"); - jni::ProxyAudioDeviceModule * audioModule = GetHandle(env, caller, "audioModuleHandle"); + webrtc::Thread * networkThread = GetHandle(env, caller, "networkThreadHandle", PKG "PeerConnectionFactory"); + webrtc::Thread * signalingThread = GetHandle(env, caller, "signalingThreadHandle", PKG "PeerConnectionFactory"); + jni::ProxyAudioDeviceModule * audioModule = GetHandle(env, caller, "audioModuleHandle", PKG "PeerConnectionFactory"); webrtc::RefCountReleaseStatus status = factory->Release(); @@ -220,7 +221,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_dispose RTC_LOG(LS_WARNING) << "ProxyAudioDeviceModule was not deleted. A reference is still around somewhere."; } - SetHandle(env, caller, "audioModuleHandle", nullptr); + SetHandle(env, caller, "audioModuleHandle", nullptr, PKG "PeerConnectionFactory"); } } catch (...) { @@ -231,7 +232,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_dispose JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_setDeviceCaptureEnabled (JNIEnv * env, jobject caller, jboolean enabled) { - jni::ProxyAudioDeviceModule * audioModule = GetHandle(env, caller, "audioModuleHandle"); + jni::ProxyAudioDeviceModule * audioModule = GetHandle(env, caller, "audioModuleHandle", PKG "PeerConnectionFactory"); CHECK_HANDLE(audioModule); try { @@ -411,7 +412,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_PeerConnectionFactory_createPee if (pc != nullptr) { auto javaPeerConnection = jni::JavaFactories::create(env, pc.release()); - SetHandle(env, javaPeerConnection.get(), "observerHandle", observer); + SetHandle(env, javaPeerConnection.get(), "observerHandle", observer, PKG "RTCPeerConnection"); return javaPeerConnection.release(); } diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp index 6e00f359..83119468 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCDataChannel.cpp @@ -23,6 +23,7 @@ #include "JavaRuntimeException.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/data_channel_interface.h" #include "rtc_base/logging.h" @@ -42,7 +43,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_registerObserver channel->UnregisterObserver(); auto observer = new jni::RTCDataChannelObserver(env, jni::JavaGlobalRef(env, jObserver)); - ReplaceNativeObserver(env, caller, "observerHandle", observer); + ReplaceNativeObserver(env, caller, "observerHandle", observer, PKG "RTCDataChannel"); channel->RegisterObserver(observer); } @@ -59,7 +60,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_unregisterObserver channel->UnregisterObserver(); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCDataChannel"); } JNIEXPORT jstring JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_getLabel @@ -214,7 +215,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDataChannel_dispose channel->UnregisterObserver(); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCDataChannel"); webrtc::RefCountReleaseStatus status = channel->Release(); diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCDtlsTransport.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCDtlsTransport.cpp index 24daf06c..cd58878d 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCDtlsTransport.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCDtlsTransport.cpp @@ -22,6 +22,7 @@ #include "JavaFactories.h" #include "JavaRef.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/dtls_transport_interface.h" @@ -100,7 +101,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDtlsTransport_registerObserver transport->UnregisterObserver(); auto newObserver = new jni::RTCDtlsTransportObserver(env, jni::JavaGlobalRef(env, observer)); - ReplaceNativeObserver(env, caller, "observerHandle", newObserver); + ReplaceNativeObserver(env, caller, "observerHandle", newObserver, PKG "RTCDtlsTransport"); transport->RegisterObserver(newObserver); } @@ -117,5 +118,5 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDtlsTransport_unregisterObserve transport->UnregisterObserver(); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCDtlsTransport"); } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCDtmfSender.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCDtmfSender.cpp index 2a0a157f..42f96e3f 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCDtmfSender.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCDtmfSender.cpp @@ -18,6 +18,7 @@ #include "api/RTCDtmfSenderObserver.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/dtmf_sender_interface.h" @@ -92,7 +93,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDtmfSender_registerObserver sender->UnregisterObserver(); auto observer = new jni::RTCDtmfSenderObserver(env, jni::JavaGlobalRef(env, jObserver)); - ReplaceNativeObserver(env, caller, "observerHandle", observer); + ReplaceNativeObserver(env, caller, "observerHandle", observer, PKG "RTCDtmfSender"); sender->RegisterObserver(observer); } @@ -109,5 +110,5 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCDtmfSender_unregisterObserver sender->UnregisterObserver(); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCDtmfSender"); } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedAudioFrameInjector.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedAudioFrameInjector.cpp index a1061fb1..b4d0c4f3 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedAudioFrameInjector.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedAudioFrameInjector.cpp @@ -18,6 +18,7 @@ #include "api/EncodedFrameInjectorObserver.h" #include "JavaError.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/encoded_audio_frame_injector_interface.h" #include "api/frame_transformer_factory.h" @@ -116,7 +117,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCEncodedAudioFrameInjector_dispo // The sender keeps the injector and its callbacks; only the Java observer // stops hearing from them here. - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCEncodedAudioFrameInjector"); injector->Release(); diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedVideoFrameInjector.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedVideoFrameInjector.cpp index 397648e9..133d5ef7 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedVideoFrameInjector.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCEncodedVideoFrameInjector.cpp @@ -18,6 +18,7 @@ #include "api/EncodedFrameInjectorObserver.h" #include "JavaError.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/encoded_video_frame_injector_interface.h" #include "api/frame_transformer_factory.h" @@ -112,7 +113,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCEncodedVideoFrameInjector_dispo // The sender keeps the injector and its callbacks; only the Java observer // stops hearing from them here. - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCEncodedVideoFrameInjector"); injector->Release(); diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCPeerConnection.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCPeerConnection.cpp index 9330cd2e..681b001c 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCPeerConnection.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCPeerConnection.cpp @@ -39,6 +39,7 @@ #include "JavaRuntimeException.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/peer_connection_interface.h" #include "api/transport/bitrate_settings.h" @@ -842,7 +843,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCPeerConnection_close SetHandle(env, caller, nullptr); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCPeerConnection"); // Drop the owning reference taken when the PeerConnection was handed // to Java in PeerConnectionFactory::createPeerConnection. diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpReceiver.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpReceiver.cpp index 848cb440..bdd65599 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpReceiver.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpReceiver.cpp @@ -23,6 +23,7 @@ #include "JavaList.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/EncodedFrameTransformer.h" @@ -202,7 +203,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpReceiver_setObserver // the observer, so the previous one is out of use once this returns. receiver->SetObserver(observer); - ReplaceNativeObserver(env, caller, "observerHandle", observer); + ReplaceNativeObserver(env, caller, "observerHandle", observer, PKG "RTCRtpReceiver"); } catch (...) { ThrowCxxJavaException(env); @@ -217,10 +218,10 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpReceiver_dispose // WebRTC must let go of an observer set through this object before it is // deleted. - if (GetHandle(env, caller, "observerHandle") != nullptr) { + if (GetHandle(env, caller, "observerHandle", PKG "RTCRtpReceiver") != nullptr) { receiver->SetObserver(nullptr); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCRtpReceiver"); } // Unlike e.g. MediaStreamTrack, an RTCRtpReceiver is not exclusively diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpSender.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpSender.cpp index 03769f71..679510f6 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpSender.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpSender.cpp @@ -26,6 +26,7 @@ #include "JavaRuntimeException.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/EncodedFrameTransformer.h" #include "api/RTCDtmfSender.h" @@ -216,7 +217,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_RTCRtpSender_createVideoFrameIn auto jInjector = jni::JavaFactories::create(env, injector.release()); - SetHandle(env, jInjector.get(), "observerHandle", observer.release()); + SetHandle(env, jInjector.get(), "observerHandle", observer.release(), PKG "RTCEncodedVideoFrameInjector"); return jInjector.release(); } @@ -252,7 +253,7 @@ JNIEXPORT jobject JNICALL Java_dev_onvoid_webrtc_RTCRtpSender_createAudioFrameIn auto jInjector = jni::JavaFactories::create(env, injector.release()); - SetHandle(env, jInjector.get(), "observerHandle", observer.release()); + SetHandle(env, jInjector.get(), "observerHandle", observer.release(), PKG "RTCEncodedAudioFrameInjector"); return jInjector.release(); } @@ -318,7 +319,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpSender_setObserver // the observer, so the previous one is out of use once this returns. sender->SetObserver(observer); - ReplaceNativeObserver(env, caller, "observerHandle", observer); + ReplaceNativeObserver(env, caller, "observerHandle", observer, PKG "RTCRtpSender"); } catch (...) { ThrowCxxJavaException(env); @@ -333,10 +334,10 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_RTCRtpSender_dispose // WebRTC must let go of an observer set through this object before it is // deleted. - if (GetHandle(env, caller, "observerHandle") != nullptr) { + if (GetHandle(env, caller, "observerHandle", PKG "RTCRtpSender") != nullptr) { sender->SetObserver(nullptr); - ClearNativeObserver(env, caller, "observerHandle"); + ClearNativeObserver(env, caller, "observerHandle", PKG "RTCRtpSender"); } // Unlike e.g. MediaStreamTrack, an RTCRtpSender is not exclusively owned diff --git a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpTransceiver.cpp b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpTransceiver.cpp index 19aa3480..1f077dc8 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RTCRtpTransceiver.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RTCRtpTransceiver.cpp @@ -24,6 +24,7 @@ #include "JavaRuntimeException.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "WebRTCUtils.h" #include "api/rtp_transceiver_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_RefCountedObject.cpp b/webrtc-jni/src/main/cpp/src/JNI_RefCountedObject.cpp index 74791248..66a4dc28 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_RefCountedObject.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_RefCountedObject.cpp @@ -16,6 +16,7 @@ #include "JNI_RefCountedObject.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "rtc_base/ref_count.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_ScreenCapturer.cpp b/webrtc-jni/src/main/cpp/src/JNI_ScreenCapturer.cpp index 33f45b97..6363b129 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_ScreenCapturer.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_ScreenCapturer.cpp @@ -16,6 +16,7 @@ #include "JNI_ScreenCapturer.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/video/desktop/DesktopCapturer.h" JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_desktop_ScreenCapturer_initialize diff --git a/webrtc-jni/src/main/cpp/src/JNI_StandaloneVoiceActivityDetector.cpp b/webrtc-jni/src/main/cpp/src/JNI_StandaloneVoiceActivityDetector.cpp index 26700a17..8fd4e708 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_StandaloneVoiceActivityDetector.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_StandaloneVoiceActivityDetector.cpp @@ -15,8 +15,8 @@ */ #include "JNI_StandaloneVoiceActivityDetector.h" -#include "JNI_WebRTC.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "common_audio/vad/include/webrtc_vad.h" @@ -28,7 +28,7 @@ namespace VadInst * GetDetector(JNIEnv * env, jobject caller) { - auto * detector = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * detector = GetHandle(env, caller); if (env->ExceptionCheck()) { return nullptr; } @@ -56,7 +56,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_StandaloneVoiceActivit if (!ValidMode(env, mode)) { return; } - auto * existing = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * existing = GetHandle(env, caller); if (env->ExceptionCheck()) { return; } @@ -73,7 +73,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_StandaloneVoiceActivit ThrowJavaException(env, "java/lang/IllegalStateException", "Cannot initialize VAD"); return; } - SetHandle(env, caller, "nativeHandle", detector.get(), PKG_INTERNAL "NativeObject"); + SetHandle(env, caller, detector.get()); if (!env->ExceptionCheck()) { detector.release(); } @@ -161,11 +161,11 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_StandaloneVoiceActivit (JNIEnv * env, jobject caller) { try { - auto * detector = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * detector = GetHandle(env, caller); if (env->ExceptionCheck()) { return; } - SetHandle(env, caller, "nativeHandle", nullptr, PKG_INTERNAL "NativeObject"); + SetHandle(env, caller, nullptr); if (!env->ExceptionCheck()) { WebRtcVad_Free(detector); } diff --git a/webrtc-jni/src/main/cpp/src/JNI_SyncClock.cpp b/webrtc-jni/src/main/cpp/src/JNI_SyncClock.cpp index 63ffcc02..850ee118 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_SyncClock.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_SyncClock.cpp @@ -16,6 +16,7 @@ #include "JNI_SyncClock.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/SyncClock.h" #include "rtc_base/time_utils.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_VideoCapture.cpp b/webrtc-jni/src/main/cpp/src/JNI_VideoCapture.cpp index 55c44f2d..f6d7d729 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VideoCapture.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VideoCapture.cpp @@ -22,6 +22,7 @@ #include "JavaObject.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #ifdef __APPLE__ #include "media/video/macos/VideoCaptureMac.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_VideoDesktopSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_VideoDesktopSource.cpp index 1cb2e1fe..66663741 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VideoDesktopSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VideoDesktopSource.cpp @@ -21,6 +21,7 @@ #include "JavaObject.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "rtc_base/logging.h" #include "rtc_base/ref_counted_object.h" @@ -97,7 +98,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoDesktopSource_dis // A track may keep the source alive; it must not reach the processor // any more. videoSource->SetVideoProcessor(env, nullptr); - SetHandle(env, caller, "processorHostHandle", nullptr); + SetHandle(env, caller, "processorHostHandle", nullptr, PKG_VIDEO "VideoTrackSource"); webrtc::RefCountReleaseStatus status = videoSource->Release(); @@ -115,6 +116,6 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoDesktopSource_ini { webrtc::scoped_refptr videoSource = webrtc::make_ref_counted(); - SetHandle(env, caller, "processorHostHandle", static_cast(videoSource.get())); + SetHandle(env, caller, "processorHostHandle", static_cast(videoSource.get()), PKG_VIDEO "VideoTrackSource"); SetHandle(env, caller, videoSource.release()); } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/JNI_VideoDeviceSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_VideoDeviceSource.cpp index 66160a5c..11297ff8 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VideoDeviceSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VideoDeviceSource.cpp @@ -28,6 +28,7 @@ #include "JavaObject.h" #include "JavaString.h" #include "JavaUtils.h" +#include "NativeHandle.h" JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoDeviceSource_setVideoCaptureDevice (JNIEnv * env, jobject caller, jobject device) @@ -135,7 +136,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoDeviceSource_disp // A track may keep the source alive; it must not reach the processor // any more. videoSource->SetVideoProcessor(env, nullptr); - SetHandle(env, caller, "processorHostHandle", nullptr); + SetHandle(env, caller, "processorHostHandle", nullptr, PKG_VIDEO "VideoTrackSource"); webrtc::RefCountReleaseStatus status = videoSource->Release(); @@ -153,11 +154,11 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoDeviceSource_init { #ifdef __APPLE__ webrtc::scoped_refptr videoSource_ = webrtc::make_ref_counted(); - SetHandle(env, caller, "processorHostHandle", static_cast(videoSource_.get())); + SetHandle(env, caller, "processorHostHandle", static_cast(videoSource_.get()), PKG_VIDEO "VideoTrackSource"); SetHandle(env, caller, videoSource_.release()); #else webrtc::scoped_refptr videoSource = webrtc::make_ref_counted(); - SetHandle(env, caller, "processorHostHandle", static_cast(videoSource.get())); + SetHandle(env, caller, "processorHostHandle", static_cast(videoSource.get()), PKG_VIDEO "VideoTrackSource"); SetHandle(env, caller, videoSource.release()); #endif } \ No newline at end of file diff --git a/webrtc-jni/src/main/cpp/src/JNI_VideoTrack.cpp b/webrtc-jni/src/main/cpp/src/JNI_VideoTrack.cpp index cfc2bf30..e6dfe60d 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VideoTrack.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VideoTrack.cpp @@ -18,6 +18,7 @@ #include "api/VideoTrackSink.h" #include "JavaNullPointerException.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/media_stream_interface.h" diff --git a/webrtc-jni/src/main/cpp/src/JNI_VideoTrackSource.cpp b/webrtc-jni/src/main/cpp/src/JNI_VideoTrackSource.cpp index bc71da0d..2fae1d6a 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VideoTrackSource.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VideoTrackSource.cpp @@ -23,6 +23,7 @@ #include "JavaRef.h" #include "JavaRuntimeException.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "api/video/video_frame.h" #include "api/video/video_rotation.h" @@ -30,7 +31,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoTrackSource_setVideoProcessorNative (JNIEnv * env, jobject caller, jobject jProcessor) { - jni::VideoProcessorHost * host = GetHandle(env, caller, "processorHostHandle"); + jni::VideoProcessorHost * host = GetHandle(env, caller, "processorHostHandle", PKG_VIDEO "VideoTrackSource"); if (host == nullptr) { env->Throw(jni::JavaRuntimeException(env, "The video source does not support a VideoProcessor")); @@ -53,7 +54,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_VideoTrackSource_deliv return; } - jni::VideoProcessorHost * host = GetHandle(env, caller, "processorHostHandle"); + jni::VideoProcessorHost * host = GetHandle(env, caller, "processorHostHandle", PKG_VIDEO "VideoTrackSource"); // The source has been disposed. if (host == nullptr) { diff --git a/webrtc-jni/src/main/cpp/src/JNI_VoiceActivityDetector.cpp b/webrtc-jni/src/main/cpp/src/JNI_VoiceActivityDetector.cpp index 6f93a8c3..cec16787 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_VoiceActivityDetector.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_VoiceActivityDetector.cpp @@ -15,8 +15,8 @@ */ #include "JNI_VoiceActivityDetector.h" -#include "JNI_WebRTC.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "modules/audio_processing/vad/voice_activity_detector.h" @@ -28,7 +28,7 @@ namespace webrtc::VoiceActivityDetector * GetDetector(JNIEnv * env, jobject caller) { - auto * detector = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * detector = GetHandle(env, caller); if (env->ExceptionCheck()) { return nullptr; } @@ -44,7 +44,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_VoiceActivityDetector_ (JNIEnv * env, jobject caller) { try { - auto * existing = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * existing = GetHandle(env, caller); if (env->ExceptionCheck()) { return; } @@ -55,7 +55,7 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_VoiceActivityDetector_ // Keep WebRTC's native allocation-failure policy. Its constructors are // built without exception cleanup; JNI cannot promise OOM recovery here. auto detector = std::make_unique(); - SetHandle(env, caller, "nativeHandle", detector.get(), PKG_INTERNAL "NativeObject"); + SetHandle(env, caller, detector.get()); if (!env->ExceptionCheck()) { detector.release(); } @@ -121,11 +121,11 @@ JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_audio_VoiceActivityDetector_ (JNIEnv * env, jobject caller) { try { - auto * detector = GetHandle(env, caller, "nativeHandle", PKG_INTERNAL "NativeObject"); + auto * detector = GetHandle(env, caller); if (env->ExceptionCheck()) { return; } - SetHandle(env, caller, "nativeHandle", nullptr, PKG_INTERNAL "NativeObject"); + SetHandle(env, caller, nullptr); if (!env->ExceptionCheck()) { delete detector; } diff --git a/webrtc-jni/src/main/cpp/src/JNI_WindowCapturer.cpp b/webrtc-jni/src/main/cpp/src/JNI_WindowCapturer.cpp index e93b8cee..a267d7dc 100644 --- a/webrtc-jni/src/main/cpp/src/JNI_WindowCapturer.cpp +++ b/webrtc-jni/src/main/cpp/src/JNI_WindowCapturer.cpp @@ -16,6 +16,7 @@ #include "JNI_WindowCapturer.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "media/video/desktop/DesktopCapturer.h" JNIEXPORT void JNICALL Java_dev_onvoid_webrtc_media_video_desktop_WindowCapturer_initialize diff --git a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp index 700d2b5b..1e7abffe 100644 --- a/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp +++ b/webrtc-jni/src/main/cpp/src/WebRTCContext.cpp @@ -25,6 +25,7 @@ #include "JavaFactories.h" #include "JavaUtils.h" #include "JNI_WebRTC.h" +#include "NativeHandle.h" #include "api/encoded_audio_frame_injector_interface.h" #include "api/encoded_video_frame_injector_interface.h" @@ -108,19 +109,19 @@ namespace jni JavaEnums::add(env, PKG_MEDIA"DeviceTransport"); JavaEnums::add(env, PKG_MEDIA"AudioDeviceDirectionType"); - JavaFactories::add(env, PKG_MEDIA"audio/AudioTrackSource"); - JavaFactories::add(env, PKG_MEDIA"audio/AudioTrack"); - JavaFactories::add(env, PKG_MEDIA"video/VideoTrack"); - JavaFactories::add(env, PKG_MEDIA"MediaStream"); - JavaFactories::add(env, PKG"RTCDataChannel"); - JavaFactories::add(env, PKG"RTCDtlsTransport"); - JavaFactories::add(env, PKG"RTCEncodedAudioFrameInjector"); - JavaFactories::add(env, PKG"RTCEncodedVideoFrameInjector"); - JavaFactories::add(env, PKG"RTCIceTransport"); - JavaFactories::add(env, PKG"RTCPeerConnection"); - JavaFactories::add(env, PKG"RTCRtpReceiver"); - JavaFactories::add(env, PKG"RTCRtpSender"); - JavaFactories::add(env, PKG"RTCRtpTransceiver"); + JavaFactories::add(env, PKG_MEDIA"audio/AudioTrackSource", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG_MEDIA"audio/AudioTrack", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG_MEDIA"video/VideoTrack", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG_MEDIA"MediaStream", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCDataChannel", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCDtlsTransport", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCEncodedAudioFrameInjector", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCEncodedVideoFrameInjector", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCIceTransport", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCPeerConnection", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCRtpReceiver", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCRtpSender", NATIVE_OBJECT_CLASS); + JavaFactories::add(env, PKG"RTCRtpTransceiver", NATIVE_OBJECT_CLASS); initializeClassLoader(env, PKG_INTERNAL"NativeClassLoader"); } diff --git a/webrtc-jni/src/main/cpp/src/api/DataBufferFactory.cpp b/webrtc-jni/src/main/cpp/src/api/DataBufferFactory.cpp index 2e29c4ae..89964fb1 100644 --- a/webrtc-jni/src/main/cpp/src/api/DataBufferFactory.cpp +++ b/webrtc-jni/src/main/cpp/src/api/DataBufferFactory.cpp @@ -21,7 +21,7 @@ namespace jni { DataBufferFactory::DataBufferFactory(JNIEnv * env, const char * className) : - JavaFactory(env, className, "(" BYTE_BUFFER_SIG "Z)V") + JavaFactory(env, className, "(" BYTE_BUFFER_SIG "Z)V", nullptr) { } diff --git a/webrtc-jni/src/main/cpp/src/api/VideoFrame.cpp b/webrtc-jni/src/main/cpp/src/api/VideoFrame.cpp index 5e5c6a53..c5353944 100644 --- a/webrtc-jni/src/main/cpp/src/api/VideoFrame.cpp +++ b/webrtc-jni/src/main/cpp/src/api/VideoFrame.cpp @@ -17,6 +17,7 @@ #include "api/VideoFrame.h" #include "JavaObject.h" #include "JavaUtils.h" +#include "NativeHandle.h" #include "JavaRuntimeException.h" #include "JavaWrappedException.h" #include "JNI_WebRTC.h" @@ -63,6 +64,12 @@ namespace jni width, height, y_data, strideY, u_data, strideU, v_data, strideV); */ + // Any other buffer is implemented in Java, and its handle would be no I420 buffer. + if (!env->IsInstanceOf(jBuffer, JavaClasses::get(env)->cls)) { + ThrowJavaException(env, "java/lang/IllegalArgumentException", "The buffer of the VideoFrame is no NativeI420Buffer"); + ExceptionCheck(env); + } + webrtc::I420BufferInterface * i420Buffer = GetHandle(env, jBuffer); // A buffer that has been released for good has no pixels left. diff --git a/webrtc/src/test/java/dev/onvoid/webrtc/internal/NativeHandleTests.java b/webrtc/src/test/java/dev/onvoid/webrtc/internal/NativeHandleTests.java new file mode 100644 index 00000000..923084e7 --- /dev/null +++ b/webrtc/src/test/java/dev/onvoid/webrtc/internal/NativeHandleTests.java @@ -0,0 +1,320 @@ +/* + * Copyright 2026 Alex Andres + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dev.onvoid.webrtc.internal; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.ByteBuffer; + +import dev.onvoid.webrtc.PeerConnectionFactory; +import dev.onvoid.webrtc.TestBase; +import dev.onvoid.webrtc.media.SyncClock; +import dev.onvoid.webrtc.media.audio.AudioConverter; +import dev.onvoid.webrtc.media.audio.AudioDeviceModuleBase; +import dev.onvoid.webrtc.media.audio.AudioResampler; +import dev.onvoid.webrtc.media.audio.CustomAudioSource; +import dev.onvoid.webrtc.media.video.CustomVideoSource; +import dev.onvoid.webrtc.media.video.I420Buffer; +import dev.onvoid.webrtc.media.video.VideoDeviceSource; +import dev.onvoid.webrtc.media.video.VideoFrame; +import dev.onvoid.webrtc.media.video.VideoFrameBuffer; +import dev.onvoid.webrtc.media.video.VideoProcessor; +import dev.onvoid.webrtc.media.video.VideoTrackSink; +import dev.onvoid.webrtc.media.video.desktop.ScreenCapturer; + +import org.junit.jupiter.api.Test; + +/** + * Native code binds its objects to the fields that the library's classes + * declare. The subclasses here declare fields of the same names, initialized + * after the native object was bound. Were a handle looked up on the dynamic + * class, native code would write to these fields and then read this value + * back as a pointer. + */ +class NativeHandleTests extends TestBase { + + private static final long SHADOW = 7; + + + @Test + void resamplerSubclass() { + ShadowedResampler resampler = new ShadowedResampler(); + + assertNotEquals(0, resampler.getNativeHandle()); + assertEquals(160, resampler.resample(new byte[960], 480, new byte[320], 160, 1)); + + resampler.dispose(); + + assertEquals(0, resampler.getNativeHandle()); + assertEquals(SHADOW, resampler.nativeHandle); + } + + @Test + void converterSubclass() { + ShadowedConverter converter = new ShadowedConverter(); + + assertNotEquals(0, converter.getNativeHandle()); + converter.convert(new byte[960], new byte[converter.getTargetBufferSize()]); + + converter.dispose(); + + assertEquals(0, converter.getNativeHandle()); + assertEquals(SHADOW, converter.nativeHandle); + } + + @Test + void clockSubclass() { + ShadowedClock clock = new ShadowedClock(); + + try { + assertTrue(clock.getTimestampUs() > 0); + + // The clock is read as a parameter of another native call. + CustomAudioSource source = new CustomAudioSource(clock); + source.dispose(); + } + finally { + clock.dispose(); + } + + assertEquals(0, clock.getNativeHandle()); + assertEquals(SHADOW, clock.nativeHandle); + } + + @Test + void factorySubclass() { + ShadowedFactory factory = new ShadowedFactory(audioDevModule); + + assertNotEquals(0, factory.getNativeHandle()); + + // Reads the thread and audio module handles. + factory.dispose(); + + assertEquals(0, factory.getNativeHandle()); + assertEquals(SHADOW, factory.nativeHandle); + assertEquals(SHADOW, factory.networkThreadHandle); + assertEquals(SHADOW, factory.signalingThreadHandle); + assertEquals(SHADOW, factory.audioModuleHandle); + } + + @Test + void videoSourceSubclass() { + ShadowedVideoSource source = new ShadowedVideoSource(); + TestProcessor processor = new TestProcessor(); + + try { + source.setVideoProcessor(processor); + + assertNotNull(processor.sink, "setSink was not called"); + + source.setVideoProcessor(null); + } + finally { + source.dispose(); + } + + assertEquals(SHADOW, source.nativeHandle); + assertEquals(SHADOW, source.processorHostHandle); + } + + @Test + void capturerSubclass() { + ShadowedCapturer capturer = new ShadowedCapturer(); + + assertNotEquals(0, capturer.getNativeHandle()); + + // Reads the callback handle, which no capture has set. + capturer.dispose(); + + assertEquals(0, capturer.getNativeHandle()); + assertEquals(SHADOW, capturer.nativeHandle); + assertEquals(SHADOW, capturer.callbackHandle); + } + + @Test + void foreignBufferRejected() { + // A buffer implemented in Java holds no native buffer, whatever its fields are called. + CustomVideoSource source = new CustomVideoSource(); + VideoFrame frame = new VideoFrame(new ForeignBuffer(), 0); + + try { + assertThrows(IllegalArgumentException.class, () -> source.pushFrame(frame)); + assertThrows(IllegalArgumentException.class, () -> source.pushFrame(frame, 1000)); + } + finally { + source.dispose(); + } + } + + + + private static class ShadowedResampler extends AudioResampler { + + private long nativeHandle = SHADOW; + + + ShadowedResampler() { + super(48000, 16000, 1); + } + } + + + + private static class ShadowedConverter extends AudioConverter { + + private long nativeHandle = SHADOW; + + + ShadowedConverter() { + super(48000, 1, 16000, 1); + } + } + + + + private static class ShadowedClock extends SyncClock { + + private long nativeHandle = SHADOW; + + } + + + + private static class ShadowedFactory extends PeerConnectionFactory { + + private long nativeHandle = SHADOW; + + private long networkThreadHandle = SHADOW; + + private long signalingThreadHandle = SHADOW; + + private long audioModuleHandle = SHADOW; + + + ShadowedFactory(AudioDeviceModuleBase audioModule) { + super(audioModule); + } + } + + + + private static class ShadowedVideoSource extends VideoDeviceSource { + + private long nativeHandle = SHADOW; + + private long processorHostHandle = SHADOW; + + } + + + + private static class ShadowedCapturer extends ScreenCapturer { + + private long nativeHandle = SHADOW; + + private long callbackHandle = SHADOW; + + } + + + + private static class TestProcessor implements VideoProcessor { + + VideoTrackSink sink; + + + @Override + public void setSink(VideoTrackSink sink) { + this.sink = sink; + } + + @Override + public void onFrameCaptured(VideoFrame frame) { + sink.onVideoFrame(frame); + } + } + + + + private static class ForeignBuffer implements I420Buffer { + + @SuppressWarnings("unused") + private long nativeHandle = SHADOW; + + private final ByteBuffer data = ByteBuffer.allocateDirect(16 * 16); + + + @Override + public ByteBuffer getDataY() { + return data; + } + + @Override + public ByteBuffer getDataU() { + return data; + } + + @Override + public ByteBuffer getDataV() { + return data; + } + + @Override + public int getStrideY() { + return 16; + } + + @Override + public int getStrideU() { + return 8; + } + + @Override + public int getStrideV() { + return 8; + } + + @Override + public int getWidth() { + return 16; + } + + @Override + public int getHeight() { + return 16; + } + + @Override + public I420Buffer toI420() { + return this; + } + + @Override + public VideoFrameBuffer cropAndScale(int cropX, int cropY, int cropWidth, + int cropHeight, int scaleWidth, int scaleHeight) { + return this; + } + + @Override + public void retain() { + } + + @Override + public void release() { + } + } +}