• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android/log.h>
14 
15 #include <memory>
16 
17 #include "rtc_base/checks.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/platform_thread.h"
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
LoadClasses(JNIEnv * jni)40 void LoadClasses(JNIEnv* jni) {
41   RTC_LOG(INFO) << "LoadClasses:";
42   for (auto& c : loaded_classes) {
43     jclass localRef = FindClass(jni, c.name);
44     RTC_LOG(INFO) << "name: " << c.name;
45     CHECK_EXCEPTION(jni) << "Error during FindClass: " << c.name;
46     RTC_CHECK(localRef) << c.name;
47     jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
48     CHECK_EXCEPTION(jni) << "Error during NewGlobalRef: " << c.name;
49     RTC_CHECK(globalRef) << c.name;
50     c.clazz = globalRef;
51   }
52 }
53 
FreeClassReferences(JNIEnv * jni)54 void FreeClassReferences(JNIEnv* jni) {
55   for (auto& c : loaded_classes) {
56     jni->DeleteGlobalRef(c.clazz);
57     c.clazz = nullptr;
58   }
59 }
60 
LookUpClass(const char * name)61 jclass LookUpClass(const char* name) {
62   for (auto& c : loaded_classes) {
63     if (strcmp(c.name, name) == 0)
64       return c.clazz;
65   }
66   RTC_CHECK(false) << "Unable to find class in lookup table";
67   return 0;
68 }
69 
70 // JvmThreadConnector implementation.
JvmThreadConnector()71 JvmThreadConnector::JvmThreadConnector() : attached_(false) {
72   RTC_LOG(INFO) << "JvmThreadConnector::ctor";
73   JavaVM* jvm = JVM::GetInstance()->jvm();
74   RTC_CHECK(jvm);
75   JNIEnv* jni = GetEnv(jvm);
76   if (!jni) {
77     RTC_LOG(INFO) << "Attaching thread to JVM";
78     JNIEnv* env = nullptr;
79     jint ret = jvm->AttachCurrentThread(&env, nullptr);
80     attached_ = (ret == JNI_OK);
81   }
82 }
83 
~JvmThreadConnector()84 JvmThreadConnector::~JvmThreadConnector() {
85   RTC_LOG(INFO) << "JvmThreadConnector::dtor";
86   RTC_DCHECK(thread_checker_.IsCurrent());
87   if (attached_) {
88     RTC_LOG(INFO) << "Detaching thread from JVM";
89     jint res = JVM::GetInstance()->jvm()->DetachCurrentThread();
90     RTC_CHECK(res == JNI_OK) << "DetachCurrentThread failed: " << res;
91   }
92 }
93 
94 // GlobalRef implementation.
GlobalRef(JNIEnv * jni,jobject object)95 GlobalRef::GlobalRef(JNIEnv* jni, jobject object)
96     : jni_(jni), j_object_(NewGlobalRef(jni, object)) {
97   RTC_LOG(INFO) << "GlobalRef::ctor";
98 }
99 
~GlobalRef()100 GlobalRef::~GlobalRef() {
101   RTC_LOG(INFO) << "GlobalRef::dtor";
102   DeleteGlobalRef(jni_, j_object_);
103 }
104 
CallBooleanMethod(jmethodID methodID,...)105 jboolean GlobalRef::CallBooleanMethod(jmethodID methodID, ...) {
106   va_list args;
107   va_start(args, methodID);
108   jboolean res = jni_->CallBooleanMethodV(j_object_, methodID, args);
109   CHECK_EXCEPTION(jni_) << "Error during CallBooleanMethod";
110   va_end(args);
111   return res;
112 }
113 
CallIntMethod(jmethodID methodID,...)114 jint GlobalRef::CallIntMethod(jmethodID methodID, ...) {
115   va_list args;
116   va_start(args, methodID);
117   jint res = jni_->CallIntMethodV(j_object_, methodID, args);
118   CHECK_EXCEPTION(jni_) << "Error during CallIntMethod";
119   va_end(args);
120   return res;
121 }
122 
CallVoidMethod(jmethodID methodID,...)123 void GlobalRef::CallVoidMethod(jmethodID methodID, ...) {
124   va_list args;
125   va_start(args, methodID);
126   jni_->CallVoidMethodV(j_object_, methodID, args);
127   CHECK_EXCEPTION(jni_) << "Error during CallVoidMethod";
128   va_end(args);
129 }
130 
131 // NativeRegistration implementation.
NativeRegistration(JNIEnv * jni,jclass clazz)132 NativeRegistration::NativeRegistration(JNIEnv* jni, jclass clazz)
133     : JavaClass(jni, clazz), jni_(jni) {
134   RTC_LOG(INFO) << "NativeRegistration::ctor";
135 }
136 
~NativeRegistration()137 NativeRegistration::~NativeRegistration() {
138   RTC_LOG(INFO) << "NativeRegistration::dtor";
139   jni_->UnregisterNatives(j_class_);
140   CHECK_EXCEPTION(jni_) << "Error during UnregisterNatives";
141 }
142 
NewObject(const char * name,const char * signature,...)143 std::unique_ptr<GlobalRef> NativeRegistration::NewObject(const char* name,
144                                                          const char* signature,
145                                                          ...) {
146   RTC_LOG(INFO) << "NativeRegistration::NewObject";
147   va_list args;
148   va_start(args, signature);
149   jobject obj = jni_->NewObjectV(
150       j_class_, GetMethodID(jni_, j_class_, name, signature), args);
151   CHECK_EXCEPTION(jni_) << "Error during NewObjectV";
152   va_end(args);
153   return std::unique_ptr<GlobalRef>(new GlobalRef(jni_, obj));
154 }
155 
156 // JavaClass implementation.
GetMethodId(const char * name,const char * signature)157 jmethodID JavaClass::GetMethodId(const char* name, const char* signature) {
158   return GetMethodID(jni_, j_class_, name, signature);
159 }
160 
GetStaticMethodId(const char * name,const char * signature)161 jmethodID JavaClass::GetStaticMethodId(const char* name,
162                                        const char* signature) {
163   return GetStaticMethodID(jni_, j_class_, name, signature);
164 }
165 
CallStaticObjectMethod(jmethodID methodID,...)166 jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) {
167   va_list args;
168   va_start(args, methodID);
169   jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args);
170   CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod";
171   return res;
172 }
173 
CallStaticIntMethod(jmethodID methodID,...)174 jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) {
175   va_list args;
176   va_start(args, methodID);
177   jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args);
178   CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod";
179   return res;
180 }
181 
182 // JNIEnvironment implementation.
JNIEnvironment(JNIEnv * jni)183 JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) {
184   RTC_LOG(INFO) << "JNIEnvironment::ctor";
185 }
186 
~JNIEnvironment()187 JNIEnvironment::~JNIEnvironment() {
188   RTC_LOG(INFO) << "JNIEnvironment::dtor";
189   RTC_DCHECK(thread_checker_.IsCurrent());
190 }
191 
RegisterNatives(const char * name,const JNINativeMethod * methods,int num_methods)192 std::unique_ptr<NativeRegistration> JNIEnvironment::RegisterNatives(
193     const char* name,
194     const JNINativeMethod* methods,
195     int num_methods) {
196   RTC_LOG(INFO) << "JNIEnvironment::RegisterNatives: " << name;
197   RTC_DCHECK(thread_checker_.IsCurrent());
198   jclass clazz = LookUpClass(name);
199   jni_->RegisterNatives(clazz, methods, num_methods);
200   CHECK_EXCEPTION(jni_) << "Error during RegisterNatives";
201   return std::unique_ptr<NativeRegistration>(
202       new NativeRegistration(jni_, clazz));
203 }
204 
JavaToStdString(const jstring & j_string)205 std::string JNIEnvironment::JavaToStdString(const jstring& j_string) {
206   RTC_DCHECK(thread_checker_.IsCurrent());
207   const char* jchars = jni_->GetStringUTFChars(j_string, nullptr);
208   CHECK_EXCEPTION(jni_);
209   const int size = jni_->GetStringUTFLength(j_string);
210   CHECK_EXCEPTION(jni_);
211   std::string ret(jchars, size);
212   jni_->ReleaseStringUTFChars(j_string, jchars);
213   CHECK_EXCEPTION(jni_);
214   return ret;
215 }
216 
217 // static
Initialize(JavaVM * jvm)218 void JVM::Initialize(JavaVM* jvm) {
219   RTC_LOG(INFO) << "JVM::Initialize";
220   RTC_CHECK(!g_jvm);
221   g_jvm = new JVM(jvm);
222 }
223 
Initialize(JavaVM * jvm,jobject context)224 void JVM::Initialize(JavaVM* jvm, jobject context) {
225   Initialize(jvm);
226 
227   // Pass in the context to the new ContextUtils class.
228   JNIEnv* jni = g_jvm->jni();
229   jclass context_utils = FindClass(jni, "org/webrtc/ContextUtils");
230   jmethodID initialize_method = jni->GetStaticMethodID(
231       context_utils, "initialize", "(Landroid/content/Context;)V");
232   jni->CallStaticVoidMethod(context_utils, initialize_method, context);
233 }
234 
235 // static
Uninitialize()236 void JVM::Uninitialize() {
237   RTC_LOG(INFO) << "JVM::Uninitialize";
238   RTC_DCHECK(g_jvm);
239   delete g_jvm;
240   g_jvm = nullptr;
241 }
242 
243 // static
GetInstance()244 JVM* JVM::GetInstance() {
245   RTC_DCHECK(g_jvm);
246   return g_jvm;
247 }
248 
JVM(JavaVM * jvm)249 JVM::JVM(JavaVM* jvm) : jvm_(jvm) {
250   RTC_LOG(INFO) << "JVM::JVM";
251   RTC_CHECK(jni()) << "AttachCurrentThread() must be called on this thread.";
252   LoadClasses(jni());
253 }
254 
~JVM()255 JVM::~JVM() {
256   RTC_LOG(INFO) << "JVM::~JVM";
257   RTC_DCHECK(thread_checker_.IsCurrent());
258   FreeClassReferences(jni());
259 }
260 
environment()261 std::unique_ptr<JNIEnvironment> JVM::environment() {
262   RTC_LOG(INFO) << "JVM::environment";
263   ;
264   // The JNIEnv is used for thread-local storage. For this reason, we cannot
265   // share a JNIEnv between threads. If a piece of code has no other way to get
266   // its JNIEnv, we should share the JavaVM, and use GetEnv to discover the
267   // thread's JNIEnv. (Assuming it has one, if not, use AttachCurrentThread).
268   // See // http://developer.android.com/training/articles/perf-jni.html.
269   JNIEnv* jni = GetEnv(jvm_);
270   if (!jni) {
271     RTC_LOG(LS_ERROR)
272         << "AttachCurrentThread() has not been called on this thread";
273     return std::unique_ptr<JNIEnvironment>();
274   }
275   return std::unique_ptr<JNIEnvironment>(new JNIEnvironment(jni));
276 }
277 
GetClass(const char * name)278 JavaClass JVM::GetClass(const char* name) {
279   RTC_LOG(INFO) << "JVM::GetClass: " << name;
280   RTC_DCHECK(thread_checker_.IsCurrent());
281   return JavaClass(jni(), LookUpClass(name));
282 }
283 
284 }  // namespace webrtc
285