jvm_android.cc (8837B)
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 "modules/utility/include/jvm_android.h" 12 13 #include <jni.h> 14 15 #include <cstdarg> 16 #include <cstring> 17 #include <memory> 18 #include <string> 19 20 #include "modules/utility/include/helpers_android.h" 21 #include "rtc_base/checks.h" 22 #include "rtc_base/logging.h" 23 24 namespace webrtc { 25 26 JVM* g_jvm; 27 28 // TODO(henrika): add more clases here if needed. 29 struct { 30 const char* name; 31 jclass clazz; 32 } loaded_classes[] = {}; 33 34 // Android's FindClass() is trickier than usual because the app-specific 35 // ClassLoader is not consulted when there is no app-specific frame on the 36 // stack. Consequently, we only look up all classes once in native WebRTC. 37 // http://developer.android.com/training/articles/perf-jni.html#faq_FindClass 38 void LoadClasses(JNIEnv* jni) { 39 RTC_LOG(LS_INFO) << "LoadClasses:"; 40 for (auto& c : loaded_classes) { 41 jclass localRef = FindClass(jni, c.name); 42 RTC_LOG(LS_INFO) << "name: " << 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 // JvmThreadConnector implementation. 69 JvmThreadConnector::JvmThreadConnector() : attached_(false) { 70 RTC_LOG(LS_INFO) << "JvmThreadConnector::ctor"; 71 JavaVM* jvm = JVM::GetInstance()->jvm(); 72 RTC_CHECK(jvm); 73 JNIEnv* jni = GetEnv(jvm); 74 if (!jni) { 75 RTC_LOG(LS_INFO) << "Attaching thread to JVM"; 76 JNIEnv* env = nullptr; 77 jint ret = jvm->AttachCurrentThread(&env, nullptr); 78 attached_ = (ret == JNI_OK); 79 } 80 } 81 82 JvmThreadConnector::~JvmThreadConnector() { 83 RTC_LOG(LS_INFO) << "JvmThreadConnector::dtor"; 84 RTC_DCHECK(thread_checker_.IsCurrent()); 85 if (attached_) { 86 RTC_LOG(LS_INFO) << "Detaching thread from JVM"; 87 jint res = JVM::GetInstance()->jvm()->DetachCurrentThread(); 88 RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res; 89 } 90 } 91 92 // GlobalRef implementation. 93 GlobalRef::GlobalRef(JNIEnv* jni, jobject object) 94 : jni_(jni), j_object_(NewGlobalRef(jni, object)) { 95 RTC_LOG(LS_INFO) << "GlobalRef::ctor"; 96 } 97 98 GlobalRef::~GlobalRef() { 99 RTC_LOG(LS_INFO) << "GlobalRef::dtor"; 100 DeleteGlobalRef(jni_, j_object_); 101 } 102 103 jboolean GlobalRef::CallBooleanMethod(jmethodID methodID, ...) { 104 va_list args; 105 va_start(args, methodID); 106 jboolean res = jni_->CallBooleanMethodV(j_object_, methodID, args); 107 CHECK_EXCEPTION(jni_) << "Error during CallBooleanMethod"; 108 va_end(args); 109 return res; 110 } 111 112 jint GlobalRef::CallIntMethod(jmethodID methodID, ...) { 113 va_list args; 114 va_start(args, methodID); 115 jint res = jni_->CallIntMethodV(j_object_, methodID, args); 116 CHECK_EXCEPTION(jni_) << "Error during CallIntMethod"; 117 va_end(args); 118 return res; 119 } 120 121 void GlobalRef::CallVoidMethod(jmethodID methodID, ...) { 122 va_list args; 123 va_start(args, methodID); 124 jni_->CallVoidMethodV(j_object_, methodID, args); 125 CHECK_EXCEPTION(jni_) << "Error during CallVoidMethod"; 126 va_end(args); 127 } 128 129 // NativeRegistration implementation. 130 NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz) 131 : JavaClass(jni, clazz), jni_(jni) { 132 RTC_LOG(LS_INFO) << "NativeRegistration::ctor"; 133 } 134 135 NativeRegistration::~NativeRegistration() { 136 RTC_LOG(LS_INFO) << "NativeRegistration::dtor"; 137 jni_->UnregisterNatives(j_class_); 138 CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives"; 139 } 140 141 std::unique_ptr<GlobalRef> NativeRegistration::NewObject(const char* name, 142 const char* signature, 143 ...) { 144 RTC_LOG(LS_INFO) << "NativeRegistration::NewObject"; 145 va_list args; 146 va_start(args, signature); 147 jobject obj = jni_->NewObjectV( 148 j_class_, GetMethodID(jni_, j_class_, name, signature), args); 149 CHECK_EXCEPTION(jni_) << "Error during NewObjectV"; 150 va_end(args); 151 return std::unique_ptr<GlobalRef>(new GlobalRef(jni_, obj)); 152 } 153 154 // JavaClass implementation. 155 jmethodID JavaClass::GetMethodId(const char* name, const char* signature) { 156 return GetMethodID(jni_, j_class_, name, signature); 157 } 158 159 jmethodID JavaClass::GetStaticMethodId(const char* name, 160 const char* signature) { 161 return GetStaticMethodID(jni_, j_class_, name, signature); 162 } 163 164 jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) { 165 va_list args; 166 va_start(args, methodID); 167 jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args); 168 CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod"; 169 return res; 170 } 171 172 jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) { 173 va_list args; 174 va_start(args, methodID); 175 jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args); 176 CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod"; 177 return res; 178 } 179 180 // JNIEnvironment implementation. 181 JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) { 182 RTC_LOG(LS_INFO) << "JNIEnvironment::ctor"; 183 } 184 185 JNIEnvironment::~JNIEnvironment() { 186 RTC_LOG(LS_INFO) << "JNIEnvironment::dtor"; 187 RTC_DCHECK(thread_checker_.IsCurrent()); 188 } 189 190 std::unique_ptr<NativeRegistration> JNIEnvironment::RegisterNatives( 191 const char* name, 192 const JNINativeMethod* methods, 193 int num_methods) { 194 RTC_LOG(LS_INFO) << "JNIEnvironment::RegisterNatives: " << name; 195 RTC_DCHECK(thread_checker_.IsCurrent()); 196 jclass clazz = LookUpClass(name); 197 jni_->RegisterNatives(clazz, methods, num_methods); 198 CHECK_EXCEPTION(jni_) << "Error during RegisterNatives"; 199 return std::unique_ptr<NativeRegistration>( 200 new NativeRegistration(jni_, clazz)); 201 } 202 203 std::string JNIEnvironment::JavaToStdString(const jstring& j_string) { 204 RTC_DCHECK(thread_checker_.IsCurrent()); 205 const char* jchars = jni_->GetStringUTFChars(j_string, nullptr); 206 CHECK_EXCEPTION(jni_); 207 const int size = jni_->GetStringUTFLength(j_string); 208 CHECK_EXCEPTION(jni_); 209 std::string ret(jchars, size); 210 jni_->ReleaseStringUTFChars(j_string, jchars); 211 CHECK_EXCEPTION(jni_); 212 return ret; 213 } 214 215 // static 216 void JVM::Initialize(JavaVM* jvm) { 217 RTC_LOG(LS_INFO) << "JVM::Initialize"; 218 RTC_CHECK(!g_jvm); 219 g_jvm = new JVM(jvm); 220 } 221 222 void JVM::Initialize(JavaVM* jvm, jobject context) { 223 Initialize(jvm); 224 225 // Pass in the context to the new ContextUtils class. 226 JNIEnv* jni = g_jvm->jni(); 227 jclass context_utils = FindClass(jni, "org/webrtc/ContextUtils"); 228 jmethodID initialize_method = jni->GetStaticMethodID( 229 context_utils, "initialize", "(Landroid/content/Context;)V"); 230 jni->CallStaticVoidMethod(context_utils, initialize_method, context); 231 } 232 233 // static 234 void JVM::Uninitialize() { 235 RTC_LOG(LS_INFO) << "JVM::Uninitialize"; 236 RTC_DCHECK(g_jvm); 237 delete g_jvm; 238 g_jvm = nullptr; 239 } 240 241 // static 242 JVM* JVM::GetInstance() { 243 RTC_DCHECK(g_jvm); 244 return g_jvm; 245 } 246 247 JVM::JVM(JavaVM* jvm) : jvm_(jvm) { 248 RTC_LOG(LS_INFO) << "JVM::JVM"; 249 RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread."; 250 LoadClasses(jni()); 251 } 252 253 JVM::~JVM() { 254 RTC_LOG(LS_INFO) << "JVM::~JVM"; 255 RTC_DCHECK(thread_checker_.IsCurrent()); 256 FreeClassReferences(jni()); 257 } 258 259 std::unique_ptr<JNIEnvironment> JVM::environment() { 260 RTC_LOG(LS_INFO) << "JVM::environment"; 261 ; 262 // The JNIEnv is used for thread-local storage. For this reason, we cannot 263 // share a JNIEnv between threads. If a piece of code has no other way to get 264 // its JNIEnv, we should share the JavaVM, and use GetEnv to discover the 265 // thread's JNIEnv. (Assuming it has one, if not, use AttachCurrentThread). 266 // See // http://developer.android.com/training/articles/perf-jni.html. 267 JNIEnv* jni = GetEnv(jvm_); 268 if (!jni) { 269 RTC_LOG(LS_ERROR) 270 << "AttachCurrentThread() has not been called on this thread"; 271 return std::unique_ptr<JNIEnvironment>(); 272 } 273 return std::unique_ptr<JNIEnvironment>(new JNIEnvironment(jni)); 274 } 275 276 JavaClass JVM::GetClass(const char* name) { 277 RTC_LOG(LS_INFO) << "JVM::GetClass: " << name; 278 RTC_DCHECK(thread_checker_.IsCurrent()); 279 return JavaClass(jni(), LookUpClass(name)); 280 } 281 282 } // namespace webrtc