From 50da5559ceb1cf871bfa6708ff10624c28ff0a0a Mon Sep 17 00:00:00 2001 From: Magnus Jedvert Date: Tue, 26 Sep 2017 10:51:14 +0200 Subject: [PATCH] Android: Add header for generated JNI code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header will be included from generated JNI code, and acts as a bridge between JNI types in WebRTC and Chromium. Bug: webrtc:8278 Change-Id: I88331d26315aa8b258aaaaa26d82324660d648b5 NOPRESUBMIT: True Reviewed-on: https://webrtc-review.googlesource.com/3441 Commit-Queue: Magnus Jedvert Reviewed-by: Sami Kalliomäki Cr-Commit-Position: refs/heads/master@{#19974} --- sdk/android/BUILD.gn | 2 + sdk/android/src/jni/jni_generator_helper.cc | 102 +++++++++++++++++++ sdk/android/src/jni/jni_generator_helper.h | 107 ++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 sdk/android/src/jni/jni_generator_helper.cc create mode 100644 sdk/android/src/jni/jni_generator_helper.h diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index c30f0478d8..d313771e17 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -39,6 +39,8 @@ rtc_source_set("base_jni") { "src/jni/classreferenceholder.cc", "src/jni/classreferenceholder.h", "src/jni/jni_common.cc", + "src/jni/jni_generator_helper.cc", + "src/jni/jni_generator_helper.h", "src/jni/jni_helpers.cc", "src/jni/jni_helpers.h", "src/jni/pc/audio_jni.h", diff --git a/sdk/android/src/jni/jni_generator_helper.cc b/sdk/android/src/jni/jni_generator_helper.cc new file mode 100644 index 0000000000..3bc9b05e82 --- /dev/null +++ b/sdk/android/src/jni/jni_generator_helper.cc @@ -0,0 +1,102 @@ +/* + * 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/jni_generator_helper.h" + +#include "rtc_base/atomicops.h" + +namespace base { +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 = jni->FindClass(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 +// 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). +jclass LazyGetClass(JNIEnv* env, + const char* class_name, + base::subtle::AtomicWord* atomic_class_id) { + static_assert(sizeof(base::subtle::AtomicWord) >= sizeof(jclass), + "AtomicWord can't be smaller than jclass"); + base::subtle::AtomicWord value = + rtc::AtomicOps::AcquireLoadPtr(atomic_class_id); + if (value) + return reinterpret_cast(value); + jclass clazz = + static_cast(env->NewGlobalRef(GetClass(env, class_name))); + base::subtle::AtomicWord null_aw = nullptr; + base::subtle::AtomicWord cas_result = rtc::AtomicOps::CompareAndSwapPtr( + atomic_class_id, null_aw, + reinterpret_cast(clazz)); + if (cas_result == null_aw) { + // We sucessfully stored |clazz| in |atomic_class_id|, so we are + // intentionally leaking the global ref since it's now stored there. + return clazz; + } else { + // Some other thread came before us and stored a global pointer in + // |atomic_class_id|. Relase our global ref and return the ref from the + // other thread. + env->DeleteGlobalRef(clazz); + return reinterpret_cast(cas_result); + } +} + +// If |atomic_method_id| set, it'll return immediately. Otherwise, it will look +// up the method id and store it. If there's a race, it's ok since the values +// are the same (and the duplicated effort will happen only once). +template +jmethodID MethodID::LazyGet(JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + base::subtle::AtomicWord* atomic_method_id) { + static_assert(sizeof(base::subtle::AtomicWord) >= sizeof(jmethodID), + "AtomicWord can't be smaller than jMethodID"); + base::subtle::AtomicWord value = + rtc::AtomicOps::AcquireLoadPtr(atomic_method_id); + if (value) + return reinterpret_cast(value); + jmethodID id = + (type == MethodID::TYPE_STATIC) + ? webrtc::jni::GetStaticMethodID(env, clazz, method_name, + jni_signature) + : webrtc::jni::GetMethodID(env, clazz, method_name, jni_signature); + rtc::AtomicOps::CompareAndSwapPtr( + atomic_method_id, base::subtle::AtomicWord(nullptr), + reinterpret_cast(id)); + return id; +} + +// Various template instantiations. +template jmethodID MethodID::LazyGet( + JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + base::subtle::AtomicWord* atomic_method_id); + +template jmethodID MethodID::LazyGet( + JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + base::subtle::AtomicWord* atomic_method_id); + +} // namespace android +} // namespace base diff --git a/sdk/android/src/jni/jni_generator_helper.h b/sdk/android/src/jni/jni_generator_helper.h new file mode 100644 index 0000000000..76d73e6f1e --- /dev/null +++ b/sdk/android/src/jni/jni_generator_helper.h @@ -0,0 +1,107 @@ +/* + * 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. + */ +// Do not include this file directly. It's intended to be used only by the JNI +// generation script. We are exporting types in strange namespaces in order to +// be compatible with the generated code targeted for Chromium. We are bypassing +// the wrapping done by Chromium's JniIntWrapper, JavaRef, and +// ScopedJavaLocalRef, and use raw jobjects instead. + +#ifndef SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_ +#define SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_ + +#include "sdk/android/src/jni/jni_helpers.h" + +#define CHECK_CLAZZ(env, jcaller, clazz, ...) RTC_DCHECK(clazz); + +#define BASE_EXPORT +#define JNI_REGISTRATION_EXPORT __attribute__((visibility("default"))) + +namespace jni_generator { +inline void CheckException(JNIEnv* env) { + CHECK_EXCEPTION(env); +} +} // namespace jni_generator + +namespace { +// Bypass JniIntWrapper. +// TODO(magjed): Start using Chromium's JniIntWrapper. +typedef jint JniIntWrapper; +inline jint as_jint(JniIntWrapper wrapper) { + return wrapper; +} +} // namespace + +namespace base { + +namespace subtle { +// This needs to be a type that is big enough to store a jobject/jclass. +typedef void* AtomicWord; +} // namespace subtle + +namespace android { + +// Implement JavaRef and ScopedJavaLocalRef as a shallow wrapper on top of a +// jobject/jclass, with no scoped destruction. +// TODO(magjed): Start using Chromium's scoped Java refs. +template +class JavaRef { + public: + JavaRef() {} + JavaRef(JNIEnv* env, T obj) : obj_(obj) {} + T obj() const { return obj_; } + + // Implicit on purpose. + JavaRef(const T& obj) : obj_(obj) {} + operator T() const { return obj_; } + + private: + T obj_; +}; + +// TODO(magjed): This looks weird, but it is safe. We don't use DeleteLocalRef +// in WebRTC, we use ScopedLocalRefFrame instead. We should probably switch to +// using DeleteLocalRef though. +template +using ScopedJavaLocalRef = JavaRef; + +// This function will initialize |atomic_class_id| to contain a global ref to +// the given class, and will return that ref on subsequent calls. The caller is +// responsible to zero-initialize |atomic_class_id|. It's fine to +// simultaneously call this on multiple threads referencing the same +// |atomic_method_id|. +jclass LazyGetClass(JNIEnv* env, + const char* class_name, + base::subtle::AtomicWord* atomic_class_id); + +// This class is a wrapper for JNIEnv Get(Static)MethodID. +class MethodID { + public: + enum Type { + TYPE_STATIC, + TYPE_INSTANCE, + }; + + // This function will initialize |atomic_method_id| to contain a ref to + // the given method, and will return that ref on subsequent calls. The caller + // is responsible to zero-initialize |atomic_method_id|. It's fine to + // simultaneously call this on multiple threads referencing the same + // |atomic_method_id|. + template + static jmethodID LazyGet(JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + base::subtle::AtomicWord* atomic_method_id); +}; + +} // namespace android +} // namespace base + +#endif // SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_