blob: c4e6e67f95db8bc84e63cdd738b8b6bb08082f0f [file] [log] [blame]
perkj@webrtc.org96e4db92015-02-13 12:46:51 +00001
2/*
3 * libjingle
4 * Copyright 2015 Google Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 */
29#include "talk/app/webrtc/java/jni/jni_helpers.h"
30
31#include <asm/unistd.h>
32#include <sys/prctl.h>
33#include <sys/syscall.h>
34#include <unistd.h>
35
36namespace webrtc_jni {
37
38static JavaVM* g_jvm = nullptr;
39
40static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
41
42// Key for per-thread JNIEnv* data. Non-NULL in threads attached to |g_jvm| by
43// AttachCurrentThreadIfNeeded(), NULL in unattached threads and threads that
44// were attached by the JVM because of a Java->native call.
45static pthread_key_t g_jni_ptr;
46
47using icu::UnicodeString;
48
49JavaVM *GetJVM() {
50 CHECK(g_jvm) << "JNI_OnLoad failed to run?";
51 return g_jvm;
52}
53
54// Return a |JNIEnv*| usable on this thread or NULL if this thread is detached.
55JNIEnv* GetEnv() {
56 void* env = NULL;
57 jint status = g_jvm->GetEnv(&env, JNI_VERSION_1_6);
58 CHECK(((env != NULL) && (status == JNI_OK)) ||
59 ((env == NULL) && (status == JNI_EDETACHED)))
60 << "Unexpected GetEnv return: " << status << ":" << env;
61 return reinterpret_cast<JNIEnv*>(env);
62}
63
64static void ThreadDestructor(void* prev_jni_ptr) {
65 // This function only runs on threads where |g_jni_ptr| is non-NULL, meaning
66 // we were responsible for originally attaching the thread, so are responsible
67 // for detaching it now. However, because some JVM implementations (notably
68 // Oracle's http://goo.gl/eHApYT) also use the pthread_key_create mechanism,
69 // the JVMs accounting info for this thread may already be wiped out by the
70 // time this is called. Thus it may appear we are already detached even though
71 // it was our responsibility to detach! Oh well.
72 if (!GetEnv())
73 return;
74
75 CHECK(GetEnv() == prev_jni_ptr)
76 << "Detaching from another thread: " << prev_jni_ptr << ":" << GetEnv();
77 jint status = g_jvm->DetachCurrentThread();
78 CHECK(status == JNI_OK) << "Failed to detach thread: " << status;
79 CHECK(!GetEnv()) << "Detaching was a successful no-op???";
80}
81
82static void CreateJNIPtrKey() {
83 CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor))
84 << "pthread_key_create";
85}
86
87jint InitGlobalJniVariables(JavaVM *jvm) {
88 CHECK(!g_jvm) << "InitGlobalJniVariables!";
89 g_jvm = jvm;
90 CHECK(g_jvm) << "InitGlobalJniVariables handed NULL?";
91
92 CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey)) << "pthread_once";
93
94 JNIEnv* jni = nullptr;
95 if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
96 return -1;
97
98 return JNI_VERSION_1_6;
99}
100
101// Return thread ID as a string.
102static std::string GetThreadId() {
103 char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
104 CHECK_LT(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)),
105 sizeof(buf))
106 << "Thread id is bigger than uint64??";
107 return std::string(buf);
108}
109
110// Return the current thread's name.
111static std::string GetThreadName() {
112 char name[17];
113 CHECK_EQ(0, prctl(PR_GET_NAME, name)) << "prctl(PR_GET_NAME) failed";
114 name[16] = '\0';
115 return std::string(name);
116}
117
118// Return a |JNIEnv*| usable on this thread. Attaches to |g_jvm| if necessary.
119JNIEnv* AttachCurrentThreadIfNeeded() {
120 JNIEnv* jni = GetEnv();
121 if (jni)
122 return jni;
123 CHECK(!pthread_getspecific(g_jni_ptr))
124 << "TLS has a JNIEnv* but not attached?";
125
126 std::string name(GetThreadName() + " - " + GetThreadId());
127 JavaVMAttachArgs args;
128 args.version = JNI_VERSION_1_6;
129 args.name = &name[0];
130 args.group = NULL;
131 // Deal with difference in signatures between Oracle's jni.h and Android's.
132#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
133 void* env = NULL;
134#else
135 JNIEnv* env = NULL;
136#endif
137 CHECK(!g_jvm->AttachCurrentThread(&env, &args)) << "Failed to attach thread";
138 CHECK(env) << "AttachCurrentThread handed back NULL!";
139 jni = reinterpret_cast<JNIEnv*>(env);
140 CHECK(!pthread_setspecific(g_jni_ptr, jni)) << "pthread_setspecific";
141 return jni;
142}
143
144// Return a |jlong| that will correctly convert back to |ptr|. This is needed
145// because the alternative (of silently passing a 32-bit pointer to a vararg
146// function expecting a 64-bit param) picks up garbage in the high 32 bits.
147jlong jlongFromPointer(void* ptr) {
148 static_assert(sizeof(intptr_t) <= sizeof(jlong),
149 "Time to rethink the use of jlongs");
150 // Going through intptr_t to be obvious about the definedness of the
151 // conversion from pointer to integral type. intptr_t to jlong is a standard
152 // widening by the static_assert above.
153 jlong ret = reinterpret_cast<intptr_t>(ptr);
154 DCHECK(reinterpret_cast<void*>(ret) == ptr);
155 return ret;
156}
157
158// JNIEnv-helper methods that CHECK success: no Java exception thrown and found
159// object/class/method/field is non-null.
160jmethodID GetMethodID(
161 JNIEnv* jni, jclass c, const std::string& name, const char* signature) {
162 jmethodID m = jni->GetMethodID(c, name.c_str(), signature);
163 CHECK_EXCEPTION(jni) << "error during GetMethodID: " << name << ", "
164 << signature;
165 CHECK(m) << name << ", " << signature;
166 return m;
167}
168
169jmethodID GetStaticMethodID(
170 JNIEnv* jni, jclass c, const char* name, const char* signature) {
171 jmethodID m = jni->GetStaticMethodID(c, name, signature);
172 CHECK_EXCEPTION(jni) << "error during GetStaticMethodID: " << name << ", "
173 << signature;
174 CHECK(m) << name << ", " << signature;
175 return m;
176}
177
178jfieldID GetFieldID(
179 JNIEnv* jni, jclass c, const char* name, const char* signature) {
180 jfieldID f = jni->GetFieldID(c, name, signature);
181 CHECK_EXCEPTION(jni) << "error during GetFieldID";
182 CHECK(f) << name << ", " << signature;
183 return f;
184}
185
186jclass GetObjectClass(JNIEnv* jni, jobject object) {
187 jclass c = jni->GetObjectClass(object);
188 CHECK_EXCEPTION(jni) << "error during GetObjectClass";
189 CHECK(c) << "GetObjectClass returned NULL";
190 return c;
191}
192
193jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) {
194 jobject o = jni->GetObjectField(object, id);
195 CHECK_EXCEPTION(jni) << "error during GetObjectField";
196 CHECK(o) << "GetObjectField returned NULL";
197 return o;
198}
199
200jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) {
201 return static_cast<jstring>(GetObjectField(jni, object, id));
202}
203
204jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) {
205 jlong l = jni->GetLongField(object, id);
206 CHECK_EXCEPTION(jni) << "error during GetLongField";
207 return l;
208}
209
210jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) {
211 jint i = jni->GetIntField(object, id);
212 CHECK_EXCEPTION(jni) << "error during GetIntField";
213 return i;
214}
215
216bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) {
217 jboolean b = jni->GetBooleanField(object, id);
218 CHECK_EXCEPTION(jni) << "error during GetBooleanField";
219 return b;
220}
221
222// Java references to "null" can only be distinguished as such in C++ by
223// creating a local reference, so this helper wraps that logic.
224bool IsNull(JNIEnv* jni, jobject obj) {
225 ScopedLocalRefFrame local_ref_frame(jni);
226 return jni->NewLocalRef(obj) == NULL;
227}
228
229// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
230jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
231 UnicodeString ustr(UnicodeString::fromUTF8(native));
232 jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length());
233 CHECK_EXCEPTION(jni) << "error during NewString";
234 return jstr;
235}
236
237// Given a (UTF-16) jstring return a new UTF-8 native string.
238std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
239 const jchar* jchars = jni->GetStringChars(j_string, NULL);
240 CHECK_EXCEPTION(jni) << "Error during GetStringChars";
241 UnicodeString ustr(jchars, jni->GetStringLength(j_string));
242 CHECK_EXCEPTION(jni) << "Error during GetStringLength";
243 jni->ReleaseStringChars(j_string, jchars);
244 CHECK_EXCEPTION(jni) << "Error during ReleaseStringChars";
245 std::string ret;
246 return ustr.toUTF8String(ret);
247}
248
249// Return the (singleton) Java Enum object corresponding to |index|;
250jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
251 const std::string& state_class_name, int index) {
252 jmethodID state_values_id = GetStaticMethodID(
253 jni, state_class, "values", ("()[L" + state_class_name + ";").c_str());
254 jobjectArray state_values = static_cast<jobjectArray>(
255 jni->CallStaticObjectMethod(state_class, state_values_id));
256 CHECK_EXCEPTION(jni) << "error during CallStaticObjectMethod";
257 jobject ret = jni->GetObjectArrayElement(state_values, index);
258 CHECK_EXCEPTION(jni) << "error during GetObjectArrayElement";
259 return ret;
260}
261
262jobject NewGlobalRef(JNIEnv* jni, jobject o) {
263 jobject ret = jni->NewGlobalRef(o);
264 CHECK_EXCEPTION(jni) << "error during NewGlobalRef";
265 CHECK(ret);
266 return ret;
267}
268
269void DeleteGlobalRef(JNIEnv* jni, jobject o) {
270 jni->DeleteGlobalRef(o);
271 CHECK_EXCEPTION(jni) << "error during DeleteGlobalRef";
272}
273
274WeakRef::WeakRef(JNIEnv* jni, jweak ref)
275 : jni_(jni), obj_(jni_->NewLocalRef(ref)) {
276 CHECK_EXCEPTION(jni) << "error during NewLocalRef";
277}
278WeakRef::~WeakRef() {
279 if (obj_) {
280 jni_->DeleteLocalRef(obj_);
281 CHECK_EXCEPTION(jni_) << "error during DeleteLocalRef";
282 }
283}
284
285// Scope Java local references to the lifetime of this object. Use in all C++
286// callbacks (i.e. entry points that don't originate in a Java callstack
287// through a "native" method call).
288ScopedLocalRefFrame::ScopedLocalRefFrame(JNIEnv* jni) : jni_(jni) {
289 CHECK(!jni_->PushLocalFrame(0)) << "Failed to PushLocalFrame";
290}
291ScopedLocalRefFrame::~ScopedLocalRefFrame() {
292 jni_->PopLocalFrame(NULL);
293}
294
295} // namespace webrtc_jni