Skip to content

Commit e50249b

Browse files
committed
Fixed loading classes from native threads with detached class-loaders #49
1 parent 29a6e3b commit e50249b

File tree

10 files changed

+166
-24
lines changed

10 files changed

+166
-24
lines changed

webrtc-jni/src/main/cpp/dependencies/jni-voithos/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ target_sources(${PROJECT_NAME}
1212
include/JavaBigInteger.h
1313
include/JavaClasses.h
1414
include/JavaClass.h
15+
include/JavaClassLoader.h
1516
include/JavaClassUtils.h
1617
include/JavaContext.h
1718
include/JavaDimension.h
@@ -40,6 +41,7 @@ target_sources(${PROJECT_NAME}
4041
src/Exception.cpp
4142
src/JavaArrayList.cpp
4243
src/JavaBigInteger.cpp
44+
src/JavaClassLoader.cpp
4345
src/JavaClassUtils.cpp
4446
src/JavaContext.cpp
4547
src/JavaDimension.cpp
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2021, Alex Andres. All rights reserved.
3+
*
4+
* Use of this source code is governed by the 3-Clause BSD license that can be
5+
* found in the LICENSE file in the root of the source tree.
6+
*/
7+
8+
#ifndef JNI_JAVA_CLASSLOADER_H_
9+
#define JNI_JAVA_CLASSLOADER_H_
10+
11+
#include "JavaRef.h"
12+
13+
#include <jni.h>
14+
15+
namespace jni
16+
{
17+
void InitClassLoader(JNIEnv * env, const JavaGlobalRef<jobject> & classLoader);
18+
19+
JavaLocalRef<jclass> ClassLoaderGetClass(JNIEnv * env, const char * name);
20+
}
21+
22+
#endif

webrtc-jni/src/main/cpp/dependencies/jni-voithos/include/JavaContext.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ namespace jni
3333
void operator=(const JavaContext &) = delete;
3434
JavaContext & operator=(JavaContext &&) = delete;
3535

36-
virtual void initialize(JNIEnv * env);
37-
virtual void destroy(JNIEnv * env);
36+
virtual void initialize(JNIEnv * env) = 0;
37+
virtual void initializeClassLoader(JNIEnv * env, const char * loaderName) = 0;
38+
virtual void destroy(JNIEnv * env) = 0;
3839

3940
JavaVM * getVM();
4041

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2021, Alex Andres. All rights reserved.
3+
*
4+
* Use of this source code is governed by the 3-Clause BSD license that can be
5+
* found in the LICENSE file in the root of the source tree.
6+
*/
7+
8+
#include "JavaClassLoader.h"
9+
#include "JavaString.h"
10+
#include "JavaUtils.h"
11+
12+
#include <algorithm>
13+
#include <string>
14+
15+
namespace jni
16+
{
17+
class JavaClassLoader {
18+
public:
19+
explicit JavaClassLoader(JNIEnv * env, const JavaGlobalRef<jobject> & classLoader) :
20+
classLoader(classLoader)
21+
{
22+
classLoaderClass_ = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/lang/ClassLoader")));
23+
24+
if (ExceptionCheck(env)) {
25+
return;
26+
}
27+
28+
loadClass = env->GetMethodID(classLoaderClass_, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
29+
30+
ExceptionCheck(env);
31+
}
32+
33+
JavaLocalRef<jclass> FindClass(JNIEnv * env, const char * c_name)
34+
{
35+
std::string name(c_name);
36+
std::replace(name.begin(), name.end(), '/', '.');
37+
38+
JavaLocalRef<jstring> j_name = JavaString::toJava(env, name);
39+
40+
const jclass cls = static_cast<jclass>(env->CallObjectMethod(classLoader.get(), loadClass, j_name.get()));
41+
42+
if (ExceptionCheck(env)) {
43+
return nullptr;
44+
}
45+
46+
return JavaLocalRef<jclass>(env, cls);
47+
}
48+
49+
private:
50+
JavaGlobalRef<jobject> classLoader;
51+
jclass classLoaderClass_;
52+
jmethodID loadClass;
53+
};
54+
55+
56+
static JavaClassLoader * javaClassLoader = nullptr;
57+
58+
59+
void InitClassLoader(JNIEnv * env, const JavaGlobalRef<jobject> & classLoader) {
60+
if (javaClassLoader == nullptr) {
61+
javaClassLoader = new JavaClassLoader(env, classLoader);
62+
}
63+
}
64+
65+
JavaLocalRef<jclass> ClassLoaderGetClass(JNIEnv * env, const char * name) {
66+
return (javaClassLoader == nullptr)
67+
? JavaLocalRef<jclass>(env, env->FindClass(name))
68+
: javaClassLoader->FindClass(env, name);
69+
}
70+
}

webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaContext.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,6 @@ namespace jni
1414
{
1515
}
1616

17-
void JavaContext::initialize(JNIEnv * env)
18-
{
19-
}
20-
21-
void JavaContext::destroy(JNIEnv * env)
22-
{
23-
}
24-
2517
JavaVM * JavaContext::getVM()
2618
{
2719
return vm;

webrtc-jni/src/main/cpp/dependencies/jni-voithos/src/JavaUtils.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "Exception.h"
99
#include "JavaUtils.h"
10+
#include "JavaClassLoader.h"
1011
#include "JavaContext.h"
1112
#include "JavaError.h"
1213
#include "JavaIterable.h"
@@ -60,7 +61,7 @@ void ThrowCxxJavaException(JNIEnv * env)
6061

6162
jclass FindClass(JNIEnv * env, const char * name)
6263
{
63-
jclass cls = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(name)));
64+
jclass cls = reinterpret_cast<jclass>(env->NewGlobalRef(jni::ClassLoaderGetClass(env, name)));
6465

6566
if (cls == nullptr) {
6667
ExceptionCheck(env);

webrtc-jni/src/main/cpp/include/JNI_WebRTC.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
#ifndef JNI_WEBRTC_H_
1818
#define JNI_WEBRTC_H_
1919

20-
#define PKG "dev/onvoid/webrtc/"
21-
#define PKG_LOG "dev/onvoid/webrtc/logging/"
22-
#define PKG_MEDIA "dev/onvoid/webrtc/media/"
23-
#define PKG_AUDIO "dev/onvoid/webrtc/media/audio/"
24-
#define PKG_VIDEO "dev/onvoid/webrtc/media/video/"
25-
#define PKG_DESKTOP "dev/onvoid/webrtc/media/video/desktop/"
20+
#define PKG "dev/onvoid/webrtc/"
21+
#define PKG_INTERNAL "dev/onvoid/webrtc/internal/"
22+
#define PKG_LOG "dev/onvoid/webrtc/logging/"
23+
#define PKG_MEDIA "dev/onvoid/webrtc/media/"
24+
#define PKG_AUDIO "dev/onvoid/webrtc/media/audio/"
25+
#define PKG_VIDEO "dev/onvoid/webrtc/media/video/"
26+
#define PKG_DESKTOP "dev/onvoid/webrtc/media/video/desktop/"
2627

2728
#define BOOLEAN_SIG "Ljava/lang/Boolean;"
2829
#define DOUBLE_SIG "Ljava/lang/Double;"

webrtc-jni/src/main/cpp/include/WebRTCContext.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ namespace jni
3333
~WebRTCContext() = default;
3434

3535
void initialize(JNIEnv * env) override;
36+
void initializeClassLoader(JNIEnv* env, const char * loaderName) override;
3637
void destroy(JNIEnv * env) override;
3738

3839
avdev::AudioDeviceManager * getAudioDeviceManager();
3940
avdev::VideoDeviceManager * getVideoDeviceManager();
4041

4142
private:
42-
void initDeviceManagers();
43+
void initializeDeviceManagers();
4344

4445
private:
4546
std::unique_ptr<avdev::AudioDeviceManager> audioDevManager;

webrtc-jni/src/main/cpp/src/WebRTCContext.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "api/DataBufferFactory.h"
1919
#include "api/RTCStats.h"
2020
#include "Exception.h"
21+
#include "JavaClassLoader.h"
2122
#include "JavaError.h"
2223
#include "JavaEnums.h"
2324
#include "JavaFactories.h"
@@ -55,8 +56,6 @@ namespace jni
5556

5657
void WebRTCContext::initialize(JNIEnv * env)
5758
{
58-
JavaContext::initialize(env);
59-
6059
if (!rtc::InitializeSSL()) {
6160
throw Exception("Initialize SSL failed");
6261
}
@@ -96,7 +95,23 @@ namespace jni
9695
JavaFactories::add<webrtc::RtpTransceiverInterface>(env, PKG"RTCRtpTransceiver");
9796
JavaFactories::add<webrtc::DataBuffer>(std::make_unique<DataBufferFactory>(env, PKG"RTCDataChannelBuffer"));
9897

99-
initDeviceManagers();
98+
initializeClassLoader(env, PKG_INTERNAL"NativeClassLoader");
99+
initializeDeviceManagers();
100+
}
101+
102+
void WebRTCContext::initializeClassLoader(JNIEnv* env, const char * loaderName)
103+
{
104+
auto javaClass = JavaLocalRef<jclass>(env, FindClass(env, loaderName));
105+
106+
if (ExceptionCheck(env)) {
107+
return;
108+
}
109+
110+
auto javaGet = GetStaticMethod(env, javaClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
111+
112+
auto loaderRef = jni::JavaGlobalRef<jobject>(env, env->CallStaticObjectMethod(javaClass, javaGet));
113+
114+
InitClassLoader(env, loaderRef);
100115
}
101116

102117
void WebRTCContext::destroy(JNIEnv * env)
@@ -107,8 +122,6 @@ namespace jni
107122

108123
audioDevManager = nullptr;
109124
videoDevManager = nullptr;
110-
111-
JavaContext::destroy(env);
112125
}
113126

114127
avdev::AudioDeviceManager * WebRTCContext::getAudioDeviceManager()
@@ -121,7 +134,7 @@ namespace jni
121134
return videoDevManager.get();
122135
}
123136

124-
void WebRTCContext::initDeviceManagers()
137+
void WebRTCContext::initializeDeviceManagers()
125138
{
126139
#ifdef _WIN32
127140
audioDevManager = std::make_unique<avdev::WindowsAudioDeviceManager>();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2021 Alex Andres
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package dev.onvoid.webrtc.internal;
18+
19+
/**
20+
* This ClassLoader is capable of loading Java classes regardless of what thread
21+
* classes should be loaded from. Such a ClassLoader is needed when a Java class
22+
* lookup is performed from a native thread and there is no app-specific frame
23+
* on the stack.
24+
*
25+
* @author Alex Andres
26+
*/
27+
class NativeClassLoader {
28+
29+
static ClassLoader getClassLoader() {
30+
ClassLoader loader = NativeClassLoader.class.getClassLoader();
31+
32+
if (loader == null) {
33+
throw new RuntimeException("Get NativeClassLoader failed");
34+
}
35+
36+
return loader;
37+
}
38+
39+
}

0 commit comments

Comments
 (0)