1 /* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <android/log.h> 12 13 #include "webrtc/modules/utility/include/jvm_android.h" 14 15 #include "webrtc/base/checks.h" 16 17 #define TAG "JVM" 18 #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) 19 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 20 21 namespace webrtc { 22 23 JVM* g_jvm; 24 25 // TODO(henrika): add more clases here if needed. 26 struct { 27 const char* name; 28 jclass clazz; 29 } loaded_classes[] = { 30 {"org/webrtc/voiceengine/BuildInfo", nullptr}, 31 {"org/webrtc/voiceengine/WebRtcAudioManager", nullptr}, 32 {"org/webrtc/voiceengine/WebRtcAudioRecord", nullptr}, 33 {"org/webrtc/voiceengine/WebRtcAudioTrack", nullptr}, 34 }; 35 36 // Android's FindClass() is trickier than usual because the app-specific 37 // ClassLoader is not consulted when there is no app-specific frame on the 38 // stack. Consequently, we only look up all classes once in native WebRTC. 39 // http://developer.android.com/training/articles/perf-jni.html#faq_FindClass 40 void LoadClasses(JNIEnv* jni) { 41 for (auto& c : loaded_classes) { 42 jclass localRef = FindClass(jni, c.name); 43 CHECK_EXCEPTION(jni) << "Error during FindClass: " << c.name; 44 RTC_CHECK(localRef) << c.name; 45 jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef)); 46 CHECK_EXCEPTION(jni) << "Error during NewGlobalRef: " << c.name; 47 RTC_CHECK(globalRef) << c.name; 48 c.clazz = globalRef; 49 } 50 } 51 52 void FreeClassReferences(JNIEnv* jni) { 53 for (auto& c : loaded_classes) { 54 jni->DeleteGlobalRef(c.clazz); 55 c.clazz = nullptr; 56 } 57 } 58 59 jclass LookUpClass(const char* name) { 60 for (auto& c : loaded_classes) { 61 if (strcmp(c.name, name) == 0) 62 return c.clazz; 63 } 64 RTC_CHECK(false) << "Unable to find class in lookup table"; 65 return 0; 66 } 67 68 // AttachCurrentThreadIfNeeded implementation. 69 AttachCurrentThreadIfNeeded::AttachCurrentThreadIfNeeded() 70 : attached_(false) { 71 ALOGD("AttachCurrentThreadIfNeeded::ctor%s", GetThreadInfo().c_str()); 72 JavaVM* jvm = JVM::GetInstance()->jvm(); 73 RTC_CHECK(jvm); 74 JNIEnv* jni = GetEnv(jvm); 75 if (!jni) { 76 ALOGD("Attaching thread to JVM"); 77 JNIEnv* env = nullptr; 78 jint ret = jvm->AttachCurrentThread(&env, nullptr); 79 attached_ = (ret == JNI_OK); 80 } 81 } 82 83 AttachCurrentThreadIfNeeded::~AttachCurrentThreadIfNeeded() { 84 ALOGD("AttachCurrentThreadIfNeeded::dtor%s", GetThreadInfo().c_str()); 85 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 86 if (attached_) { 87 ALOGD("Detaching thread from JVM"); 88 jint res = JVM::GetInstance()->jvm()->DetachCurrentThread(); 89 RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res; 90 } 91 } 92 93 // GlobalRef implementation. 94 GlobalRef::GlobalRef(JNIEnv* jni, jobject object) 95 : jni_(jni), j_object_(NewGlobalRef(jni, object)) { 96 ALOGD("GlobalRef::ctor%s", GetThreadInfo().c_str()); 97 } 98 99 GlobalRef::~GlobalRef() { 100 ALOGD("GlobalRef::dtor%s", GetThreadInfo().c_str()); 101 DeleteGlobalRef(jni_, j_object_); 102 } 103 104 jboolean GlobalRef::CallBooleanMethod(jmethodID methodID, ...) { 105 va_list args; 106 va_start(args, methodID); 107 jboolean res = jni_->CallBooleanMethodV(j_object_, methodID, args); 108 CHECK_EXCEPTION(jni_) << "Error during CallBooleanMethod"; 109 va_end(args); 110 return res; 111 } 112 113 jint GlobalRef::CallIntMethod(jmethodID methodID, ...) { 114 va_list args; 115 va_start(args, methodID); 116 jint res = jni_->CallIntMethodV(j_object_, methodID, args); 117 CHECK_EXCEPTION(jni_) << "Error during CallIntMethod"; 118 va_end(args); 119 return res; 120 } 121 122 void GlobalRef::CallVoidMethod(jmethodID methodID, ...) { 123 va_list args; 124 va_start(args, methodID); 125 jni_->CallVoidMethodV(j_object_, methodID, args); 126 CHECK_EXCEPTION(jni_) << "Error during CallVoidMethod"; 127 va_end(args); 128 } 129 130 // NativeRegistration implementation. 131 NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz) 132 : JavaClass(jni, clazz), jni_(jni) { 133 ALOGD("NativeRegistration::ctor%s", GetThreadInfo().c_str()); 134 } 135 136 NativeRegistration::~NativeRegistration() { 137 ALOGD("NativeRegistration::dtor%s", GetThreadInfo().c_str()); 138 jni_->UnregisterNatives(j_class_); 139 CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives"; 140 } 141 142 rtc::scoped_ptr<GlobalRef> NativeRegistration::NewObject( 143 const char* name, const char* signature, ...) { 144 ALOGD("NativeRegistration::NewObject%s", GetThreadInfo().c_str()); 145 va_list args; 146 va_start(args, signature); 147 jobject obj = jni_->NewObjectV(j_class_, 148 GetMethodID(jni_, j_class_, name, signature), 149 args); 150 CHECK_EXCEPTION(jni_) << "Error during NewObjectV"; 151 va_end(args); 152 return rtc::scoped_ptr<GlobalRef>(new GlobalRef(jni_, obj)); 153 } 154 155 // JavaClass implementation. 156 jmethodID JavaClass::GetMethodId( 157 const char* name, const char* signature) { 158 return GetMethodID(jni_, j_class_, name, signature); 159 } 160 161 jmethodID JavaClass::GetStaticMethodId( 162 const char* name, const char* signature) { 163 return GetStaticMethodID(jni_, j_class_, name, signature); 164 } 165 166 jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) { 167 va_list args; 168 va_start(args, methodID); 169 jobject res = jni_->CallStaticObjectMethod(j_class_, methodID, args); 170 CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod"; 171 return res; 172 } 173 174 // JNIEnvironment implementation. 175 JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) { 176 ALOGD("JNIEnvironment::ctor%s", GetThreadInfo().c_str()); 177 } 178 179 JNIEnvironment::~JNIEnvironment() { 180 ALOGD("JNIEnvironment::dtor%s", GetThreadInfo().c_str()); 181 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 182 } 183 184 rtc::scoped_ptr<NativeRegistration> JNIEnvironment::RegisterNatives( 185 const char* name, const JNINativeMethod *methods, int num_methods) { 186 ALOGD("JNIEnvironment::RegisterNatives(%s)", name); 187 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 188 jclass clazz = LookUpClass(name); 189 jni_->RegisterNatives(clazz, methods, num_methods); 190 CHECK_EXCEPTION(jni_) << "Error during RegisterNatives"; 191 return rtc::scoped_ptr<NativeRegistration>( 192 new NativeRegistration(jni_, clazz)); 193 } 194 195 std::string JNIEnvironment::JavaToStdString(const jstring& j_string) { 196 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 197 const char* jchars = jni_->GetStringUTFChars(j_string, nullptr); 198 CHECK_EXCEPTION(jni_); 199 const int size = jni_->GetStringUTFLength(j_string); 200 CHECK_EXCEPTION(jni_); 201 std::string ret(jchars, size); 202 jni_->ReleaseStringUTFChars(j_string, jchars); 203 CHECK_EXCEPTION(jni_); 204 return ret; 205 } 206 207 // static 208 void JVM::Initialize(JavaVM* jvm, jobject context) { 209 ALOGD("JVM::Initialize%s", GetThreadInfo().c_str()); 210 RTC_CHECK(!g_jvm); 211 g_jvm = new JVM(jvm, context); 212 } 213 214 // static 215 void JVM::Uninitialize() { 216 ALOGD("JVM::Uninitialize%s", GetThreadInfo().c_str()); 217 RTC_DCHECK(g_jvm); 218 delete g_jvm; 219 g_jvm = nullptr; 220 } 221 222 // static 223 JVM* JVM::GetInstance() { 224 RTC_DCHECK(g_jvm); 225 return g_jvm; 226 } 227 228 JVM::JVM(JavaVM* jvm, jobject context) 229 : jvm_(jvm) { 230 ALOGD("JVM::JVM%s", GetThreadInfo().c_str()); 231 RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread."; 232 context_ = NewGlobalRef(jni(), context); 233 LoadClasses(jni()); 234 } 235 236 JVM::~JVM() { 237 ALOGD("JVM::~JVM%s", GetThreadInfo().c_str()); 238 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 239 FreeClassReferences(jni()); 240 DeleteGlobalRef(jni(), context_); 241 } 242 243 rtc::scoped_ptr<JNIEnvironment> JVM::environment() { 244 ALOGD("JVM::environment%s", GetThreadInfo().c_str()); 245 // The JNIEnv is used for thread-local storage. For this reason, we cannot 246 // share a JNIEnv between threads. If a piece of code has no other way to get 247 // its JNIEnv, we should share the JavaVM, and use GetEnv to discover the 248 // thread's JNIEnv. (Assuming it has one, if not, use AttachCurrentThread). 249 // See // http://developer.android.com/training/articles/perf-jni.html. 250 JNIEnv* jni = GetEnv(jvm_); 251 if (!jni) { 252 ALOGE("AttachCurrentThread() has not been called on this thread."); 253 return rtc::scoped_ptr<JNIEnvironment>(); 254 } 255 return rtc::scoped_ptr<JNIEnvironment>(new JNIEnvironment(jni)); 256 } 257 258 JavaClass JVM::GetClass(const char* name) { 259 ALOGD("JVM::GetClass(%s)%s", name, GetThreadInfo().c_str()); 260 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 261 return JavaClass(jni(), LookUpClass(name)); 262 } 263 264 } // namespace webrtc 265