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 <codecvt>
8 #include <string>
9
10 #include "flutter/fml/logging.h"
11
12 namespace fml {
13 namespace jni {
14
15 static JavaVM* g_jvm = nullptr;
16
17 #define ASSERT_NO_EXCEPTION() FML_CHECK(env->ExceptionCheck() == JNI_FALSE);
18
InitJavaVM(JavaVM * vm)19 void InitJavaVM(JavaVM* vm) {
20 FML_DCHECK(g_jvm == nullptr);
21 g_jvm = vm;
22 }
23
AttachCurrentThread()24 JNIEnv* AttachCurrentThread() {
25 FML_DCHECK(g_jvm != nullptr)
26 << "Trying to attach to current thread without calling InitJavaVM first.";
27 JNIEnv* env = nullptr;
28 jint ret = g_jvm->AttachCurrentThread(&env, nullptr);
29 FML_DCHECK(JNI_OK == ret);
30 return env;
31 }
32
DetachFromVM()33 void DetachFromVM() {
34 if (g_jvm) {
35 g_jvm->DetachCurrentThread();
36 }
37 }
38
UTF16StringToUTF8String(const char16_t * chars,size_t len)39 static std::string UTF16StringToUTF8String(const char16_t* chars, size_t len) {
40 std::u16string u16_string(chars, len);
41 return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
42 .to_bytes(u16_string);
43 }
44
JavaStringToString(JNIEnv * env,jstring str)45 std::string JavaStringToString(JNIEnv* env, jstring str) {
46 if (env == nullptr || str == nullptr) {
47 return "";
48 }
49 const jchar* chars = env->GetStringChars(str, NULL);
50 if (chars == nullptr) {
51 return "";
52 }
53 std::string u8_string = UTF16StringToUTF8String(
54 reinterpret_cast<const char16_t*>(chars), env->GetStringLength(str));
55 env->ReleaseStringChars(str, chars);
56 ASSERT_NO_EXCEPTION();
57 return u8_string;
58 }
59
UTF8StringToUTF16String(const std::string & string)60 static std::u16string UTF8StringToUTF16String(const std::string& string) {
61 return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}
62 .from_bytes(string);
63 }
64
StringToJavaString(JNIEnv * env,const std::string & u8_string)65 ScopedJavaLocalRef<jstring> StringToJavaString(JNIEnv* env,
66 const std::string& u8_string) {
67 std::u16string u16_string = UTF8StringToUTF16String(u8_string);
68 auto result = ScopedJavaLocalRef<jstring>(
69 env, env->NewString(reinterpret_cast<const jchar*>(u16_string.data()),
70 u16_string.length()));
71 ASSERT_NO_EXCEPTION();
72 return result;
73 }
74
StringArrayToVector(JNIEnv * env,jobjectArray array)75 std::vector<std::string> StringArrayToVector(JNIEnv* env, jobjectArray array) {
76 std::vector<std::string> out;
77 if (env == nullptr || array == nullptr) {
78 return out;
79 }
80
81 jsize length = env->GetArrayLength(array);
82
83 if (length == -1) {
84 return out;
85 }
86
87 out.resize(length);
88 for (jsize i = 0; i < length; ++i) {
89 ScopedJavaLocalRef<jstring> java_string(
90 env, static_cast<jstring>(env->GetObjectArrayElement(array, i)));
91 out[i] = JavaStringToString(env, java_string.obj());
92 }
93
94 return out;
95 }
96
VectorToStringArray(JNIEnv * env,const std::vector<std::string> & vector)97 ScopedJavaLocalRef<jobjectArray> VectorToStringArray(
98 JNIEnv* env,
99 const std::vector<std::string>& vector) {
100 FML_DCHECK(env);
101 ScopedJavaLocalRef<jclass> string_clazz(env,
102 env->FindClass("java/lang/String"));
103 FML_DCHECK(!string_clazz.is_null());
104 jobjectArray joa =
105 env->NewObjectArray(vector.size(), string_clazz.obj(), NULL);
106 ASSERT_NO_EXCEPTION();
107 for (size_t i = 0; i < vector.size(); ++i) {
108 ScopedJavaLocalRef<jstring> item = StringToJavaString(env, vector[i]);
109 env->SetObjectArrayElement(joa, i, item.obj());
110 }
111 return ScopedJavaLocalRef<jobjectArray>(env, joa);
112 }
113
HasException(JNIEnv * env)114 bool HasException(JNIEnv* env) {
115 return env->ExceptionCheck() != JNI_FALSE;
116 }
117
ClearException(JNIEnv * env)118 bool ClearException(JNIEnv* env) {
119 if (!HasException(env))
120 return false;
121 env->ExceptionDescribe();
122 env->ExceptionClear();
123 return true;
124 }
125
GetJavaExceptionInfo(JNIEnv * env,jthrowable java_throwable)126 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
127 ScopedJavaLocalRef<jclass> throwable_clazz(
128 env, env->FindClass("java/lang/Throwable"));
129 jmethodID throwable_printstacktrace = env->GetMethodID(
130 throwable_clazz.obj(), "printStackTrace", "(Ljava/io/PrintStream;)V");
131
132 // Create an instance of ByteArrayOutputStream.
133 ScopedJavaLocalRef<jclass> bytearray_output_stream_clazz(
134 env, env->FindClass("java/io/ByteArrayOutputStream"));
135 jmethodID bytearray_output_stream_constructor =
136 env->GetMethodID(bytearray_output_stream_clazz.obj(), "<init>", "()V");
137 jmethodID bytearray_output_stream_tostring = env->GetMethodID(
138 bytearray_output_stream_clazz.obj(), "toString", "()Ljava/lang/String;");
139 ScopedJavaLocalRef<jobject> bytearray_output_stream(
140 env, env->NewObject(bytearray_output_stream_clazz.obj(),
141 bytearray_output_stream_constructor));
142
143 // Create an instance of PrintStream.
144 ScopedJavaLocalRef<jclass> printstream_clazz(
145 env, env->FindClass("java/io/PrintStream"));
146 jmethodID printstream_constructor = env->GetMethodID(
147 printstream_clazz.obj(), "<init>", "(Ljava/io/OutputStream;)V");
148 ScopedJavaLocalRef<jobject> printstream(
149 env, env->NewObject(printstream_clazz.obj(), printstream_constructor,
150 bytearray_output_stream.obj()));
151
152 // Call Throwable.printStackTrace(PrintStream)
153 env->CallVoidMethod(java_throwable, throwable_printstacktrace,
154 printstream.obj());
155
156 // Call ByteArrayOutputStream.toString()
157 ScopedJavaLocalRef<jstring> exception_string(
158 env,
159 static_cast<jstring>(env->CallObjectMethod(
160 bytearray_output_stream.obj(), bytearray_output_stream_tostring)));
161 if (ClearException(env)) {
162 return "Java OOM'd in exception handling, check logcat";
163 }
164
165 return JavaStringToString(env, exception_string.obj());
166 }
167
168 } // namespace jni
169 } // namespace fml
170