Flutter Engine
The Flutter Engine
jni_util.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/fml/platform/android/jni_util.h"
6
7#include <sys/prctl.h>
8
9#include <memory>
10#include <string>
11
12#include "flutter/fml/logging.h"
13#include "flutter/fml/string_conversion.h"
14
15namespace fml {
16namespace jni {
17
18static JavaVM* g_jvm = nullptr;
19
20#define ASSERT_NO_EXCEPTION() FML_CHECK(env->ExceptionCheck() == JNI_FALSE);
21
22struct JNIDetach {
24};
25
26// Thread-local object that will detach from JNI during thread shutdown;
27static thread_local std::unique_ptr<JNIDetach> tls_jni_detach;
28
29void InitJavaVM(JavaVM* vm) {
30 FML_DCHECK(g_jvm == nullptr);
31 g_jvm = vm;
32}
33
35 FML_DCHECK(g_jvm != nullptr)
36 << "Trying to attach to current thread without calling InitJavaVM first.";
37
38 JNIEnv* env = nullptr;
39 if (g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) ==
40 JNI_OK) {
41 return env;
42 }
43
44 JavaVMAttachArgs args;
45 args.version = JNI_VERSION_1_4;
46 args.group = nullptr;
47 // 16 is the maximum size for thread names on Android.
48 char thread_name[16];
49 int err = prctl(PR_GET_NAME, thread_name);
50 if (err < 0) {
51 args.name = nullptr;
52 } else {
53 args.name = thread_name;
54 }
55 [[maybe_unused]] jint ret = g_jvm->AttachCurrentThread(&env, &args);
56 FML_DCHECK(JNI_OK == ret);
57
58 FML_DCHECK(tls_jni_detach.get() == nullptr);
59 tls_jni_detach.reset(new JNIDetach());
60
61 return env;
62}
63
65 if (g_jvm) {
66 g_jvm->DetachCurrentThread();
67 }
68}
69
70std::string JavaStringToString(JNIEnv* env, jstring str) {
71 if (env == nullptr || str == nullptr) {
72 return "";
73 }
74 const jchar* chars = env->GetStringChars(str, NULL);
75 if (chars == nullptr) {
76 return "";
77 }
78 std::u16string u16_string(reinterpret_cast<const char16_t*>(chars),
79 env->GetStringLength(str));
80 std::string u8_string = Utf16ToUtf8(u16_string);
81 env->ReleaseStringChars(str, chars);
83 return u8_string;
84}
85
87 const std::string& u8_string) {
88 std::u16string u16_string = Utf8ToUtf16(u8_string);
90 env, env->NewString(reinterpret_cast<const jchar*>(u16_string.data()),
91 u16_string.length()));
93 return result;
94}
95
96std::vector<std::string> StringArrayToVector(JNIEnv* env, jobjectArray array) {
97 std::vector<std::string> out;
98 if (env == nullptr || array == nullptr) {
99 return out;
100 }
101
102 jsize length = env->GetArrayLength(array);
103
104 if (length == -1) {
105 return out;
106 }
107
108 out.resize(length);
109 for (jsize i = 0; i < length; ++i) {
110 ScopedJavaLocalRef<jstring> java_string(
111 env, static_cast<jstring>(env->GetObjectArrayElement(array, i)));
112 out[i] = JavaStringToString(env, java_string.obj());
113 }
114
115 return out;
116}
117
118std::vector<std::string> StringListToVector(JNIEnv* env, jobject list) {
119 std::vector<std::string> out;
120 if (env == nullptr || list == nullptr) {
121 return out;
122 }
123
124 ScopedJavaLocalRef<jclass> list_clazz(env, env->FindClass("java/util/List"));
125 FML_DCHECK(!list_clazz.is_null());
126
127 jmethodID list_get =
128 env->GetMethodID(list_clazz.obj(), "get", "(I)Ljava/lang/Object;");
129 jmethodID list_size = env->GetMethodID(list_clazz.obj(), "size", "()I");
130
131 jint size = env->CallIntMethod(list, list_size);
132
133 if (size == 0) {
134 return out;
135 }
136
137 out.resize(size);
138 for (jint i = 0; i < size; ++i) {
139 ScopedJavaLocalRef<jstring> java_string(
140 env, static_cast<jstring>(env->CallObjectMethod(list, list_get, i)));
141 out[i] = JavaStringToString(env, java_string.obj());
142 }
143
144 return out;
145}
146
148 JNIEnv* env,
149 const std::vector<std::string>& vector) {
151 ScopedJavaLocalRef<jclass> string_clazz(env,
152 env->FindClass("java/lang/String"));
153 FML_DCHECK(!string_clazz.is_null());
154 jobjectArray java_array =
155 env->NewObjectArray(vector.size(), string_clazz.obj(), NULL);
157 for (size_t i = 0; i < vector.size(); ++i) {
159 env->SetObjectArrayElement(java_array, i, item.obj());
160 }
161 return ScopedJavaLocalRef<jobjectArray>(env, java_array);
162}
163
165 JNIEnv* env,
166 const std::vector<std::vector<uint8_t>>& vector) {
168 ScopedJavaLocalRef<jclass> byte_buffer_clazz(
169 env, env->FindClass("java/nio/ByteBuffer"));
170 FML_DCHECK(!byte_buffer_clazz.is_null());
171 jobjectArray java_array =
172 env->NewObjectArray(vector.size(), byte_buffer_clazz.obj(), NULL);
174 for (size_t i = 0; i < vector.size(); ++i) {
175 uint8_t* data = const_cast<uint8_t*>(vector[i].data());
177 env, env->NewDirectByteBuffer(reinterpret_cast<void*>(data),
178 vector[i].size()));
179 env->SetObjectArrayElement(java_array, i, item.obj());
180 }
181 return ScopedJavaLocalRef<jobjectArray>(env, java_array);
182}
183
184bool HasException(JNIEnv* env) {
185 return env->ExceptionCheck() != JNI_FALSE;
186}
187
188bool ClearException(JNIEnv* env, bool silent) {
189 if (!HasException(env)) {
190 return false;
191 }
192 if (!silent) {
193 env->ExceptionDescribe();
194 }
195 env->ExceptionClear();
196 return true;
197}
198
199bool CheckException(JNIEnv* env) {
200 if (!HasException(env)) {
201 return true;
202 }
203
204 jthrowable exception = env->ExceptionOccurred();
205 env->ExceptionClear();
207 env->DeleteLocalRef(exception);
208 return false;
209}
210
211std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
212 ScopedJavaLocalRef<jclass> throwable_clazz(
213 env, env->FindClass("java/lang/Throwable"));
214 jmethodID throwable_printstacktrace = env->GetMethodID(
215 throwable_clazz.obj(), "printStackTrace", "(Ljava/io/PrintStream;)V");
216
217 // Create an instance of ByteArrayOutputStream.
218 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz(
219 env, env->FindClass("java/io/ByteArrayOutputStream"));
220 jmethodID bytearray_output_stream_constructor =
221 env->GetMethodID(bytearray_output_stream_clazz.obj(), "<init>", "()V");
222 jmethodID bytearray_output_stream_tostring = env->GetMethodID(
223 bytearray_output_stream_clazz.obj(), "toString", "()Ljava/lang/String;");
224 ScopedJavaLocalRef<jobject> bytearray_output_stream(
225 env, env->NewObject(bytearray_output_stream_clazz.obj(),
226 bytearray_output_stream_constructor));
227
228 // Create an instance of PrintStream.
229 ScopedJavaLocalRef<jclass> printstream_clazz(
230 env, env->FindClass("java/io/PrintStream"));
231 jmethodID printstream_constructor = env->GetMethodID(
232 printstream_clazz.obj(), "<init>", "(Ljava/io/OutputStream;)V");
233 ScopedJavaLocalRef<jobject> printstream(
234 env, env->NewObject(printstream_clazz.obj(), printstream_constructor,
235 bytearray_output_stream.obj()));
236
237 // Call Throwable.printStackTrace(PrintStream)
238 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
239 printstream.obj());
240
241 // Call ByteArrayOutputStream.toString()
242 ScopedJavaLocalRef<jstring> exception_string(
243 env,
244 static_cast<jstring>(env->CallObjectMethod(
245 bytearray_output_stream.obj(), bytearray_output_stream_tostring)));
246 if (ClearException(env)) {
247 return "Java OOM'd in exception handling, check logcat";
248 }
249
250 return JavaStringToString(env, exception_string.obj());
251}
252
253} // namespace jni
254} // namespace fml
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
#define FML_LOG(severity)
Definition: logging.h:82
#define FML_DCHECK(condition)
Definition: logging.h:103
#define ASSERT_NO_EXCEPTION()
Definition: jni_util.cc:20
size_t length
Definition: __init__.py:1
JNIEnv * AttachCurrentThread()
Definition: jni_util.cc:34
ScopedJavaLocalRef< jobjectArray > VectorToBufferArray(JNIEnv *env, const std::vector< std::vector< uint8_t > > &vector)
Definition: jni_util.cc:164
std::string JavaStringToString(JNIEnv *env, jstring str)
Definition: jni_util.cc:70
void DetachFromVM()
Definition: jni_util.cc:64
bool ClearException(JNIEnv *env, bool silent)
Definition: jni_util.cc:188
static thread_local std::unique_ptr< JNIDetach > tls_jni_detach
Definition: jni_util.cc:27
bool CheckException(JNIEnv *env)
Definition: jni_util.cc:199
static JavaVM * g_jvm
Definition: jni_util.cc:18
std::string GetJavaExceptionInfo(JNIEnv *env, jthrowable java_throwable)
Definition: jni_util.cc:211
ScopedJavaLocalRef< jobjectArray > VectorToStringArray(JNIEnv *env, const std::vector< std::string > &vector)
Definition: jni_util.cc:147
std::vector< std::string > StringListToVector(JNIEnv *env, jobject list)
Definition: jni_util.cc:118
bool HasException(JNIEnv *env)
Definition: jni_util.cc:184
void InitJavaVM(JavaVM *vm)
Definition: jni_util.cc:29
std::vector< std::string > StringArrayToVector(JNIEnv *env, jobjectArray array)
Definition: jni_util.cc:96
ScopedJavaLocalRef< jstring > StringToJavaString(JNIEnv *env, const std::string &u8_string)
Definition: jni_util.cc:86
Definition: ascii_trie.cc:9
std::string Utf16ToUtf8(const std::u16string_view string)
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
std::u16string Utf8ToUtf16(const std::string_view string)
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define ERROR(message)
Definition: elf_loader.cc:260