Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Android: Fix access to JavaVM for threads after #45618 #47682

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions platform/android/java_class_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
return false;

JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, false);

MethodInfo *method = NULL;
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
Expand Down Expand Up @@ -1049,6 +1050,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
return class_cache[p_class];

JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, Ref<JavaClass>());

jclass bclass = env->FindClass(p_class.utf8().get_data());
ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
Expand Down Expand Up @@ -1243,6 +1245,7 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
singleton = this;

JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

jclass activityClass = env->FindClass("android/app/Activity");
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
Expand Down
12 changes: 12 additions & 0 deletions platform/android/java_godot_io_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobject GodotIOJavaWrapper::get_instance() {
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
if (_open_URI) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, ERR_UNAVAILABLE);
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
} else {
Expand All @@ -87,6 +88,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
String GodotIOJavaWrapper::get_user_data_dir() {
if (_get_data_dir) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
return jstring_to_string(s, env);
} else {
Expand All @@ -97,6 +99,7 @@ String GodotIOJavaWrapper::get_user_data_dir() {
String GodotIOJavaWrapper::get_locale() {
if (_get_locale) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
return jstring_to_string(s, env);
} else {
Expand All @@ -107,6 +110,7 @@ String GodotIOJavaWrapper::get_locale() {
String GodotIOJavaWrapper::get_model() {
if (_get_model) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
return jstring_to_string(s, env);
} else {
Expand All @@ -117,6 +121,7 @@ String GodotIOJavaWrapper::get_model() {
int GodotIOJavaWrapper::get_screen_dpi() {
if (_get_screen_DPI) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, 160);
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
} else {
return 160;
Expand All @@ -126,6 +131,7 @@ int GodotIOJavaWrapper::get_screen_dpi() {
void GodotIOJavaWrapper::get_window_safe_area(int (&p_rect_xywh)[4]) {
if (_get_window_safe_area) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);
jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _get_window_safe_area);
ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
Expand All @@ -139,6 +145,7 @@ void GodotIOJavaWrapper::get_window_safe_area(int (&p_rect_xywh)[4]) {
String GodotIOJavaWrapper::get_unique_id() {
if (_get_unique_id) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
return jstring_to_string(s, env);
} else {
Expand All @@ -153,6 +160,7 @@ bool GodotIOJavaWrapper::has_vk() {
void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
if (_show_keyboard) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
}
Expand All @@ -161,20 +169,23 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int
void GodotIOJavaWrapper::hide_vk() {
if (_hide_keyboard) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
}
}

void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
if (_set_screen_orientation) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
}
}

int GodotIOJavaWrapper::get_screen_orientation() const {
if (_get_screen_orientation) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, 0);
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
} else {
return 0;
Expand All @@ -184,6 +195,7 @@ int GodotIOJavaWrapper::get_screen_orientation() const {
String GodotIOJavaWrapper::get_system_dir(int p_dir) {
if (_get_system_dir) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String("."));
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
return jstring_to_string(s, env);
} else {
Expand Down
35 changes: 35 additions & 0 deletions platform/android/java_godot_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
if (p_env == NULL)
p_env = get_jni_env();

ERR_FAIL_COND_V(p_env == nullptr, nullptr);

jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
return p_env->GetStaticObjectField(godot_class, fid);
} else {
Expand All @@ -104,6 +106,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
jobject GodotJavaWrapper::get_class_loader() {
if (_get_class_loader) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, nullptr);

return env->CallObjectMethod(activity, _get_class_loader);
} else {
return NULL;
Expand All @@ -121,6 +125,7 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
if (_on_video_init) {
if (p_env == NULL)
p_env = get_jni_env();
ERR_FAIL_COND(p_env == nullptr);

p_env->CallVoidMethod(godot_instance, _on_video_init);
}
Expand All @@ -131,6 +136,7 @@ void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
if (p_env == NULL) {
p_env = get_jni_env();
}
ERR_FAIL_COND(p_env == nullptr);
p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed);
}
}
Expand All @@ -140,6 +146,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
if (p_env == NULL) {
p_env = get_jni_env();
}
ERR_FAIL_COND(p_env == nullptr);
p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
}
}
Expand All @@ -148,6 +155,7 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
if (_restart) {
if (p_env == NULL)
p_env = get_jni_env();
ERR_FAIL_COND(p_env == nullptr);

p_env->CallVoidMethod(godot_instance, _restart);
}
Expand All @@ -157,6 +165,7 @@ void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
if (_finish) {
if (p_env == NULL)
p_env = get_jni_env();
ERR_FAIL_COND(p_env == nullptr);

p_env->CallVoidMethod(godot_instance, _finish);
}
Expand All @@ -165,13 +174,17 @@ void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
if (_set_keep_screen_on) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
}
}

void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
if (_alert) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
Expand All @@ -180,6 +193,8 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {

int GodotJavaWrapper::get_gles_version_code() {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, 0);

if (_get_GLES_version_code) {
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
}
Expand All @@ -194,6 +209,8 @@ bool GodotJavaWrapper::has_get_clipboard() {
String GodotJavaWrapper::get_clipboard() {
if (_get_clipboard) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());

jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
return jstring_to_string(s, env);
} else {
Expand All @@ -204,6 +221,8 @@ String GodotJavaWrapper::get_clipboard() {
String GodotJavaWrapper::get_input_fallback_mapping() {
if (_get_input_fallback_mapping) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, String());

jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
return jstring_to_string(fallback_mapping, env);
} else {
Expand All @@ -218,6 +237,8 @@ bool GodotJavaWrapper::has_set_clipboard() {
void GodotJavaWrapper::set_clipboard(const String &p_text) {
if (_set_clipboard) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
}
Expand All @@ -226,6 +247,8 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
bool GodotJavaWrapper::request_permission(const String &p_name) {
if (_request_permission) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, false);

jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
} else {
Expand All @@ -236,6 +259,8 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
bool GodotJavaWrapper::request_permissions() {
if (_request_permissions) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, false);

return env->CallBooleanMethod(godot_instance, _request_permissions);
} else {
return false;
Expand All @@ -246,6 +271,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
Vector<String> permissions_list;
if (_get_granted_permissions) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, permissions_list);

jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);

Expand All @@ -264,13 +291,17 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

env->CallVoidMethod(godot_instance, _init_input_devices);
}
}

jobject GodotJavaWrapper::get_surface() {
if (_get_surface) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, nullptr);

return env->CallObjectMethod(godot_instance, _get_surface);
} else {
return NULL;
Expand All @@ -280,6 +311,8 @@ jobject GodotJavaWrapper::get_surface() {
bool GodotJavaWrapper::is_activity_resumed() {
if (_is_activity_resumed) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND_V(env == nullptr, false);

return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
} else {
return false;
Expand All @@ -289,6 +322,8 @@ bool GodotJavaWrapper::is_activity_resumed() {
void GodotJavaWrapper::vibrate(int p_duration_ms) {
if (_vibrate) {
JNIEnv *env = get_jni_env();
ERR_FAIL_COND(env == nullptr);

env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
}
}
27 changes: 26 additions & 1 deletion platform/android/thread_jandroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,34 @@

#include "thread_jandroid.h"

#include <android/log.h>

#include "core/os/thread.h"

static JavaVM *java_vm = nullptr;
static thread_local JNIEnv *env = nullptr;

// The logic here need to improve, init_thread/term_tread are designed to work with Thread::callback
// Calling init_thread from setup_android_thread and get_jni_env to setup an env we're keeping and not detaching
// could cause issues on app termination.
//
// We should be making sure that any thread started calls a nice cleanup function when it's done,
// especially now that we use many more threads.

static void init_thread() {
if (env) {
// thread never detached! just keep using...
return;
}

java_vm->AttachCurrentThread(&env, nullptr);
}

static void term_thread() {
java_vm->DetachCurrentThread();

// this is no longer valid, must called init_thread to re-establish
env = nullptr;
}

void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
Expand All @@ -50,9 +67,17 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
}

void setup_android_thread() {
init_thread();
if (!env) {
// !BAS! see remarks above
init_thread();
}
}

JNIEnv *get_jni_env() {
if (!env) {
// !BAS! see remarks above
init_thread();
}

return env;
}