Android: Replacement for JNIEnv::FindClass that works from any thread
This CL adds a replacement for JNIEnv::FindClass that works from any thread, i.e. from native C++ threads as well. This function will be used from the generated JNI code. Long term, we should stop using classreferenceholder that relies on a hardcoded list of WebRTC classes. Bug: webrtc:8278 Change-Id: I4f40c744325ac02b73bd8fa479ab50b684429dc2 Reviewed-on: https://webrtc-review.googlesource.com/20223 Commit-Queue: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20583}
This commit is contained in:
committed by
Commit Bot
parent
fd0de7a4bb
commit
3111e5fb48
@ -33,9 +33,19 @@ config("libjingle_peerconnection_jni_warnings_config") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generate_jni("generated_base_jni") {
|
||||||
|
sources = [
|
||||||
|
"src/java/org/webrtc/ClassLoader.java",
|
||||||
|
]
|
||||||
|
jni_package = ""
|
||||||
|
jni_generator_include = "//sdk/android/src/jni/jni_generator_helper.h"
|
||||||
|
}
|
||||||
|
|
||||||
rtc_source_set("base_jni") {
|
rtc_source_set("base_jni") {
|
||||||
sources = [
|
sources = [
|
||||||
"src/jni/androidhistogram_jni.cc",
|
"src/jni/androidhistogram_jni.cc",
|
||||||
|
"src/jni/class_loader.cc",
|
||||||
|
"src/jni/class_loader.h",
|
||||||
"src/jni/classreferenceholder.cc",
|
"src/jni/classreferenceholder.cc",
|
||||||
"src/jni/classreferenceholder.h",
|
"src/jni/classreferenceholder.h",
|
||||||
"src/jni/jni_common.cc",
|
"src/jni/jni_common.cc",
|
||||||
@ -49,6 +59,7 @@ rtc_source_set("base_jni") {
|
|||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
|
":generated_base_jni",
|
||||||
"../../api:libjingle_peerconnection_api",
|
"../../api:libjingle_peerconnection_api",
|
||||||
"../../rtc_base:rtc_base",
|
"../../rtc_base:rtc_base",
|
||||||
"../../rtc_base:rtc_base_approved",
|
"../../rtc_base:rtc_base_approved",
|
||||||
@ -465,6 +476,7 @@ android_library("libjingle_peerconnection_java") {
|
|||||||
"src/java/org/webrtc/Camera2Session.java",
|
"src/java/org/webrtc/Camera2Session.java",
|
||||||
"src/java/org/webrtc/CameraCapturer.java",
|
"src/java/org/webrtc/CameraCapturer.java",
|
||||||
"src/java/org/webrtc/CameraSession.java",
|
"src/java/org/webrtc/CameraSession.java",
|
||||||
|
"src/java/org/webrtc/ClassLoader.java",
|
||||||
"src/java/org/webrtc/DynamicBitrateAdjuster.java",
|
"src/java/org/webrtc/DynamicBitrateAdjuster.java",
|
||||||
"src/java/org/webrtc/EglBase10.java",
|
"src/java/org/webrtc/EglBase10.java",
|
||||||
"src/java/org/webrtc/EglBase14.java",
|
"src/java/org/webrtc/EglBase14.java",
|
||||||
|
|||||||
23
sdk/android/src/java/org/webrtc/ClassLoader.java
Normal file
23
sdk/android/src/java/org/webrtc/ClassLoader.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.webrtc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides a ClassLoader that is capable of loading WebRTC Java classes regardless of
|
||||||
|
* what thread it's called from. Such a ClassLoader is needed for the few cases where the JNI
|
||||||
|
* mechanism is unable to automatically determine the appropriate ClassLoader instance.
|
||||||
|
*/
|
||||||
|
class ClassLoader {
|
||||||
|
@CalledByNative
|
||||||
|
static Object getClassLoader() {
|
||||||
|
return ClassLoader.class.getClassLoader();
|
||||||
|
}
|
||||||
|
}
|
||||||
80
sdk/android/src/jni/class_loader.cc
Normal file
80
sdk/android/src/jni/class_loader.cc
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* 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/class_loader.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "rtc_base/checks.h"
|
||||||
|
#include "sdk/android/generated_base_jni/jni/ClassLoader_jni.h"
|
||||||
|
|
||||||
|
// Abort the process if |jni| has a Java exception pending. This macros uses the
|
||||||
|
// comma operator to execute ExceptionDescribe and ExceptionClear ignoring their
|
||||||
|
// return values and sending "" to the error stream.
|
||||||
|
#define CHECK_EXCEPTION(jni) \
|
||||||
|
RTC_CHECK(!jni->ExceptionCheck()) \
|
||||||
|
<< (jni->ExceptionDescribe(), jni->ExceptionClear(), "")
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ClassLoader {
|
||||||
|
public:
|
||||||
|
explicit ClassLoader(JNIEnv* env) {
|
||||||
|
class_loader_class_ = reinterpret_cast<jclass>(
|
||||||
|
env->NewGlobalRef(env->FindClass("java/lang/ClassLoader")));
|
||||||
|
CHECK_EXCEPTION(env);
|
||||||
|
load_class_method_ =
|
||||||
|
env->GetMethodID(class_loader_class_, "loadClass",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/Class;");
|
||||||
|
CHECK_EXCEPTION(env);
|
||||||
|
class_loader_ = env->NewGlobalRef(Java_ClassLoader_getClassLoader(env));
|
||||||
|
CHECK_EXCEPTION(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass FindClass(JNIEnv* env, const char* c_name) {
|
||||||
|
// ClassLoader.loadClass expects a classname with components separated by
|
||||||
|
// dots instead of the slashes that JNIEnv::FindClass expects.
|
||||||
|
std::string name(c_name);
|
||||||
|
std::replace(name.begin(), name.end(), '/', '.');
|
||||||
|
jstring jstr = env->NewStringUTF(name.c_str());
|
||||||
|
const jclass clazz = static_cast<jclass>(
|
||||||
|
env->CallObjectMethod(class_loader_, load_class_method_, jstr));
|
||||||
|
CHECK_EXCEPTION(env);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
jclass class_loader_class_;
|
||||||
|
jmethodID load_class_method_;
|
||||||
|
jobject class_loader_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ClassLoader* g_class_loader = nullptr;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void InitClassLoader(JNIEnv* env) {
|
||||||
|
RTC_CHECK(g_class_loader == nullptr);
|
||||||
|
g_class_loader = new ClassLoader(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetClass(JNIEnv* env, const char* name) {
|
||||||
|
// The class loader will be null in the JNI code called from the ClassLoader
|
||||||
|
// ctor when we are bootstrapping ourself.
|
||||||
|
return (g_class_loader == nullptr) ? env->FindClass(name)
|
||||||
|
: g_class_loader->FindClass(env, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jni
|
||||||
|
} // namespace webrtc
|
||||||
40
sdk/android/src/jni/class_loader.h
Normal file
40
sdk/android/src/jni/class_loader.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Android's FindClass() is tricky because the app-specific ClassLoader is not
|
||||||
|
// consulted when there is no app-specific frame on the stack (i.e. when called
|
||||||
|
// from a thread created from native C++ code). These helper functions provide a
|
||||||
|
// workaround for this.
|
||||||
|
// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
|
||||||
|
|
||||||
|
#ifndef SDK_ANDROID_SRC_JNI_CLASS_LOADER_H_
|
||||||
|
#define SDK_ANDROID_SRC_JNI_CLASS_LOADER_H_
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace jni {
|
||||||
|
|
||||||
|
// This method should be called from JNI_OnLoad and before any calls to
|
||||||
|
// FindClass.
|
||||||
|
void InitClassLoader(JNIEnv* env);
|
||||||
|
|
||||||
|
// This function is identical to JNIEnv::FindClass except that it works from any
|
||||||
|
// thread. This function loads and returns a local reference to the class with
|
||||||
|
// the given name. The name argument is a fully-qualified class name. For
|
||||||
|
// example, the fully-qualified class name for the java.lang.String class is:
|
||||||
|
// "java/lang/String". This function will be used from the JNI generated code
|
||||||
|
// and should rarely be used manually.
|
||||||
|
jclass GetClass(JNIEnv* env, const char* name);
|
||||||
|
|
||||||
|
} // namespace jni
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // SDK_ANDROID_SRC_JNI_CLASS_LOADER_H_
|
||||||
@ -111,7 +111,6 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
|
|||||||
LoadClass(jni, "org/webrtc/VideoCodecStatus");
|
LoadClass(jni, "org/webrtc/VideoCodecStatus");
|
||||||
LoadClass(jni, "org/webrtc/VideoDecoder$Settings");
|
LoadClass(jni, "org/webrtc/VideoDecoder$Settings");
|
||||||
LoadClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
|
LoadClass(jni, "org/webrtc/VideoDecoderWrapperCallback");
|
||||||
LoadClass(jni, "org/webrtc/VideoEncoder");
|
|
||||||
LoadClass(jni, "org/webrtc/VideoEncoder$BitrateAllocation");
|
LoadClass(jni, "org/webrtc/VideoEncoder$BitrateAllocation");
|
||||||
LoadClass(jni, "org/webrtc/VideoEncoder$EncodeInfo");
|
LoadClass(jni, "org/webrtc/VideoEncoder$EncodeInfo");
|
||||||
LoadClass(jni, "org/webrtc/VideoEncoder$ScalingSettings");
|
LoadClass(jni, "org/webrtc/VideoEncoder$ScalingSettings");
|
||||||
|
|||||||
@ -16,6 +16,9 @@
|
|||||||
#ifndef SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
#ifndef SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
||||||
#define SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
#define SDK_ANDROID_SRC_JNI_CLASSREFERENCEHOLDER_H_
|
||||||
|
|
||||||
|
// TODO(magjed): Remove this whole file and replace with either generated JNI
|
||||||
|
// code or class_loader.h.
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -28,6 +31,9 @@ void LoadGlobalClassReferenceHolder();
|
|||||||
// FreeGlobalClassReferenceHolder must be called in JNI_UnLoad.
|
// FreeGlobalClassReferenceHolder must be called in JNI_UnLoad.
|
||||||
void FreeGlobalClassReferenceHolder();
|
void FreeGlobalClassReferenceHolder();
|
||||||
|
|
||||||
|
// Deprecated. Most cases of finding classes should be done with generated JNI
|
||||||
|
// code, and the few remaining cases should use the function from
|
||||||
|
// class_loader.h.
|
||||||
// Returns a global reference guaranteed to be valid for the lifetime of the
|
// Returns a global reference guaranteed to be valid for the lifetime of the
|
||||||
// process.
|
// process.
|
||||||
jclass FindClass(JNIEnv* jni, const char* name);
|
jclass FindClass(JNIEnv* jni, const char* name);
|
||||||
|
|||||||
@ -11,22 +11,11 @@
|
|||||||
#include "sdk/android/src/jni/jni_generator_helper.h"
|
#include "sdk/android/src/jni/jni_generator_helper.h"
|
||||||
|
|
||||||
#include "rtc_base/atomicops.h"
|
#include "rtc_base/atomicops.h"
|
||||||
#include "sdk/android/src/jni/classreferenceholder.h"
|
#include "sdk/android/src/jni/class_loader.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
namespace {
|
|
||||||
// JNIEnv-helper methods that RTC_CHECK success: no Java exception thrown and
|
|
||||||
// found object/class/method/field is non-null.
|
|
||||||
jclass GetClass(JNIEnv* jni, const char* class_name) {
|
|
||||||
jclass clazz = webrtc::jni::FindClass(jni, class_name);
|
|
||||||
CHECK_EXCEPTION(jni) << "error during FindClass: " << class_name;
|
|
||||||
RTC_CHECK(clazz) << class_name;
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// If |atomic_class_id| set, it'll return immediately. Otherwise, it will look
|
// If |atomic_class_id| set, it'll return immediately. Otherwise, it will look
|
||||||
// up the class and store it. If there's a race, we take care to only store one
|
// up the class and store it. If there's a race, we take care to only store one
|
||||||
// global reference (and the duplicated effort will happen only once).
|
// global reference (and the duplicated effort will happen only once).
|
||||||
@ -39,8 +28,9 @@ jclass LazyGetClass(JNIEnv* env,
|
|||||||
rtc::AtomicOps::AcquireLoadPtr(atomic_class_id);
|
rtc::AtomicOps::AcquireLoadPtr(atomic_class_id);
|
||||||
if (value)
|
if (value)
|
||||||
return reinterpret_cast<jclass>(value);
|
return reinterpret_cast<jclass>(value);
|
||||||
jclass clazz =
|
jclass clazz = static_cast<jclass>(
|
||||||
static_cast<jclass>(env->NewGlobalRef(GetClass(env, class_name)));
|
env->NewGlobalRef(webrtc::jni::GetClass(env, class_name)));
|
||||||
|
RTC_CHECK(clazz) << class_name;
|
||||||
base::subtle::AtomicWord null_aw = nullptr;
|
base::subtle::AtomicWord null_aw = nullptr;
|
||||||
base::subtle::AtomicWord cas_result = rtc::AtomicOps::CompareAndSwapPtr(
|
base::subtle::AtomicWord cas_result = rtc::AtomicOps::CompareAndSwapPtr(
|
||||||
atomic_class_id, null_aw,
|
atomic_class_id, null_aw,
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#define JNIEXPORT __attribute__((visibility("default")))
|
#define JNIEXPORT __attribute__((visibility("default")))
|
||||||
|
|
||||||
#include "rtc_base/ssladapter.h"
|
#include "rtc_base/ssladapter.h"
|
||||||
|
#include "sdk/android/src/jni/class_loader.h"
|
||||||
#include "sdk/android/src/jni/classreferenceholder.h"
|
#include "sdk/android/src/jni/classreferenceholder.h"
|
||||||
#include "sdk/android/src/jni/jni_helpers.h"
|
#include "sdk/android/src/jni/jni_helpers.h"
|
||||||
|
|
||||||
@ -27,6 +28,8 @@ extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
|
|||||||
|
|
||||||
RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()";
|
RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()";
|
||||||
LoadGlobalClassReferenceHolder();
|
LoadGlobalClassReferenceHolder();
|
||||||
|
JNIEnv* env = AttachCurrentThreadIfNeeded();
|
||||||
|
InitClassLoader(env);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user