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 <codecvt>
10 #include <string>
11 
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/thread_local.h"
14 
15 namespace fml {
16 namespace jni {
17 
18 static JavaVM* g_jvm = nullptr;
19 
20 #define ASSERT_NO_EXCEPTION() FML_CHECK(env->ExceptionCheck() == JNI_FALSE);
21 
22 struct JNIDetach {
24 };
25 
26 // Thread-local object that will detach from JNI during thread shutdown;
28 
29 void 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  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 
64 void DetachFromVM() {
65  if (g_jvm) {
66  g_jvm->DetachCurrentThread();
67  }
68 }
69 
70 static std::string UTF16StringToUTF8String(const char16_t* chars, size_t len) {
71  std::u16string u16_string(chars, len);
72  return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
73  .to_bytes(u16_string);
74 }
75 
76 std::string JavaStringToString(JNIEnv* env, jstring str) {
77  if (env == nullptr || str == nullptr) {
78  return "";
79  }
80  const jchar* chars = env->GetStringChars(str, NULL);
81  if (chars == nullptr) {
82  return "";
83  }
84  std::string u8_string = UTF16StringToUTF8String(
85  reinterpret_cast<const char16_t*>(chars), env->GetStringLength(str));
86  env->ReleaseStringChars(str, chars);
88  return u8_string;
89 }
90 
91 static std::u16string UTF8StringToUTF16String(const std::string& string) {
92  return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
93  .from_bytes(string);
94 }
95 
97  const std::string& u8_string) {
98  std::u16string u16_string = UTF8StringToUTF16String(u8_string);
100  env, env->NewString(reinterpret_cast<const jchar*>(u16_string.data()),
101  u16_string.length()));
103  return result;
104 }
105 
106 std::vector<std::string> StringArrayToVector(JNIEnv* env, jobjectArray array) {
107  std::vector<std::string> out;
108  if (env == nullptr || array == nullptr) {
109  return out;
110  }
111 
112  jsize length = env->GetArrayLength(array);
113 
114  if (length == -1) {
115  return out;
116  }
117 
118  out.resize(length);
119  for (jsize i = 0; i < length; ++i) {
120  ScopedJavaLocalRef<jstring> java_string(
121  env, static_cast<jstring>(env->GetObjectArrayElement(array, i)));
122  out[i] = JavaStringToString(env, java_string.obj());
123  }
124 
125  return out;
126 }
127 
129  JNIEnv* env,
130  const std::vector<std::string>& vector) {
131  FML_DCHECK(env);
132  ScopedJavaLocalRef<jclass> string_clazz(env,
133  env->FindClass("java/lang/String"));
134  FML_DCHECK(!string_clazz.is_null());
135  jobjectArray java_array =
136  env->NewObjectArray(vector.size(), string_clazz.obj(), NULL);
138  for (size_t i = 0; i < vector.size(); ++i) {
139  ScopedJavaLocalRef<jstring> item = StringToJavaString(env, vector[i]);
140  env->SetObjectArrayElement(java_array, i, item.obj());
141  }
142  return ScopedJavaLocalRef<jobjectArray>(env, java_array);
143 }
144 
146  JNIEnv* env,
147  const std::vector<std::vector<uint8_t>>& vector) {
148  FML_DCHECK(env);
149  ScopedJavaLocalRef<jclass> byte_buffer_clazz(
150  env, env->FindClass("java/nio/ByteBuffer"));
151  FML_DCHECK(!byte_buffer_clazz.is_null());
152  jobjectArray java_array =
153  env->NewObjectArray(vector.size(), byte_buffer_clazz.obj(), NULL);
155  for (size_t i = 0; i < vector.size(); ++i) {
157  env,
158  env->NewDirectByteBuffer((void*)(vector[i].data()), vector[i].size()));
159  env->SetObjectArrayElement(java_array, i, item.obj());
160  }
161  return ScopedJavaLocalRef<jobjectArray>(env, java_array);
162 }
163 
164 bool HasException(JNIEnv* env) {
165  return env->ExceptionCheck() != JNI_FALSE;
166 }
167 
168 bool ClearException(JNIEnv* env) {
169  if (!HasException(env))
170  return false;
171  env->ExceptionDescribe();
172  env->ExceptionClear();
173  return true;
174 }
175 
176 bool CheckException(JNIEnv* env) {
177  if (!HasException(env))
178  return true;
179 
180  jthrowable exception = env->ExceptionOccurred();
181  env->ExceptionClear();
182  FML_LOG(ERROR) << fml::jni::GetJavaExceptionInfo(env, exception);
183  env->DeleteLocalRef(exception);
184  return false;
185 }
186 
187 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
188  ScopedJavaLocalRef<jclass> throwable_clazz(
189  env, env->FindClass("java/lang/Throwable"));
190  jmethodID throwable_printstacktrace = env->GetMethodID(
191  throwable_clazz.obj(), "printStackTrace", "(Ljava/io/PrintStream;)V");
192 
193  // Create an instance of ByteArrayOutputStream.
194  ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz(
195  env, env->FindClass("java/io/ByteArrayOutputStream"));
196  jmethodID bytearray_output_stream_constructor =
197  env->GetMethodID(bytearray_output_stream_clazz.obj(), "<init>", "()V");
198  jmethodID bytearray_output_stream_tostring = env->GetMethodID(
199  bytearray_output_stream_clazz.obj(), "toString", "()Ljava/lang/String;");
200  ScopedJavaLocalRef<jobject> bytearray_output_stream(
201  env, env->NewObject(bytearray_output_stream_clazz.obj(),
202  bytearray_output_stream_constructor));
203 
204  // Create an instance of PrintStream.
205  ScopedJavaLocalRef<jclass> printstream_clazz(
206  env, env->FindClass("java/io/PrintStream"));
207  jmethodID printstream_constructor = env->GetMethodID(
208  printstream_clazz.obj(), "<init>", "(Ljava/io/OutputStream;)V");
209  ScopedJavaLocalRef<jobject> printstream(
210  env, env->NewObject(printstream_clazz.obj(), printstream_constructor,
211  bytearray_output_stream.obj()));
212 
213  // Call Throwable.printStackTrace(PrintStream)
214  env->CallVoidMethod(java_throwable, throwable_printstacktrace,
215  printstream.obj());
216 
217  // Call ByteArrayOutputStream.toString()
218  ScopedJavaLocalRef<jstring> exception_string(
219  env,
220  static_cast<jstring>(env->CallObjectMethod(
221  bytearray_output_stream.obj(), bytearray_output_stream_tostring)));
222  if (ClearException(env)) {
223  return "Java OOM'd in exception handling, check logcat";
224  }
225 
226  return JavaStringToString(env, exception_string.obj());
227 }
228 
229 } // namespace jni
230 } // namespace fml
bool HasException(JNIEnv *env)
Definition: jni_util.cc:164
G_BEGIN_DECLS FlValue * args
#define FML_DCHECK(condition)
Definition: logging.h:86
bool ClearException(JNIEnv *env)
Definition: jni_util.cc:168
ScopedJavaLocalRef< jobjectArray > VectorToStringArray(JNIEnv *env, const std::vector< std::string > &vector)
Definition: jni_util.cc:128
ScopedJavaLocalRef< jstring > StringToJavaString(JNIEnv *env, const std::string &u8_string)
Definition: jni_util.cc:96
GAsyncResult * result
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
#define FML_LOG(severity)
Definition: logging.h:65
void InitJavaVM(JavaVM *vm)
Definition: jni_util.cc:29
ScopedJavaLocalRef< jobjectArray > VectorToBufferArray(JNIEnv *env, const std::vector< std::vector< uint8_t >> &vector)
Definition: jni_util.cc:145
Definition: ascii_trie.cc:9
FML_THREAD_LOCAL fml::ThreadLocalUniquePtr< JNIDetach > tls_jni_detach
Definition: jni_util.cc:27
static JavaVM * g_jvm
Definition: jni_util.cc:18
#define FML_THREAD_LOCAL
Definition: thread_local.h:61
size_t length
#define ASSERT_NO_EXCEPTION()
Definition: jni_util.cc:20
bool CheckException(JNIEnv *env)
Definition: jni_util.cc:176
static std::u16string UTF8StringToUTF16String(const std::string &string)
Definition: jni_util.cc:91
std::string GetJavaExceptionInfo(JNIEnv *env, jthrowable java_throwable)
Definition: jni_util.cc:187
JNIEnv * AttachCurrentThread()
Definition: jni_util.cc:34
static std::string UTF16StringToUTF8String(const char16_t *chars, size_t len)
Definition: jni_util.cc:70
void DetachFromVM()
Definition: jni_util.cc:64
std::string JavaStringToString(JNIEnv *env, jstring str)
Definition: jni_util.cc:76
std::vector< std::string > StringArrayToVector(JNIEnv *env, jobjectArray array)
Definition: jni_util.cc:106