Browse Source

Fixes on android:
- checking for validity of ENV in wrapper classes
- fix for access to JavaVM from threads

(cherry picked from commit b3a43430aa1e6ac33025d86cde777e79be30ef99)

Bastiaan Olij 4 years ago
parent
commit
abaa032fae

+ 3 - 0
platform/android/java_class_wrapper.cpp

@@ -39,6 +39,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 		return false;
 		return false;
 
 
 	JNIEnv *env = get_jni_env();
 	JNIEnv *env = get_jni_env();
+	ERR_FAIL_COND_V(env == nullptr, false);
 
 
 	MethodInfo *method = NULL;
 	MethodInfo *method = NULL;
 	for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
 	for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
@@ -1049,6 +1050,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
 		return class_cache[p_class];
 		return class_cache[p_class];
 
 
 	JNIEnv *env = get_jni_env();
 	JNIEnv *env = get_jni_env();
+	ERR_FAIL_COND_V(env == nullptr, Ref<JavaClass>());
 
 
 	jclass bclass = env->FindClass(p_class.utf8().get_data());
 	jclass bclass = env->FindClass(p_class.utf8().get_data());
 	ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
 	ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
@@ -1243,6 +1245,7 @@ JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
 	singleton = this;
 	singleton = this;
 
 
 	JNIEnv *env = get_jni_env();
 	JNIEnv *env = get_jni_env();
+	ERR_FAIL_COND(env == nullptr);
 
 
 	jclass activityClass = env->FindClass("android/app/Activity");
 	jclass activityClass = env->FindClass("android/app/Activity");
 	jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
 	jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");

+ 12 - 0
platform/android/java_godot_io_wrapper.cpp

@@ -77,6 +77,7 @@ jobject GodotIOJavaWrapper::get_instance() {
 Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
 Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
 	if (_open_URI) {
 	if (_open_URI) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, ERR_UNAVAILABLE);
 		jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
 		jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
 		return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
 		return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
 	} else {
 	} else {
@@ -87,6 +88,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
 String GodotIOJavaWrapper::get_user_data_dir() {
 String GodotIOJavaWrapper::get_user_data_dir() {
 	if (_get_data_dir) {
 	if (_get_data_dir) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {
@@ -97,6 +99,7 @@ String GodotIOJavaWrapper::get_user_data_dir() {
 String GodotIOJavaWrapper::get_locale() {
 String GodotIOJavaWrapper::get_locale() {
 	if (_get_locale) {
 	if (_get_locale) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {
@@ -107,6 +110,7 @@ String GodotIOJavaWrapper::get_locale() {
 String GodotIOJavaWrapper::get_model() {
 String GodotIOJavaWrapper::get_model() {
 	if (_get_model) {
 	if (_get_model) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {
@@ -117,6 +121,7 @@ String GodotIOJavaWrapper::get_model() {
 int GodotIOJavaWrapper::get_screen_dpi() {
 int GodotIOJavaWrapper::get_screen_dpi() {
 	if (_get_screen_DPI) {
 	if (_get_screen_DPI) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, 160);
 		return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
 		return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
 	} else {
 	} else {
 		return 160;
 		return 160;
@@ -126,6 +131,7 @@ int GodotIOJavaWrapper::get_screen_dpi() {
 void GodotIOJavaWrapper::get_window_safe_area(int (&p_rect_xywh)[4]) {
 void GodotIOJavaWrapper::get_window_safe_area(int (&p_rect_xywh)[4]) {
 	if (_get_window_safe_area) {
 	if (_get_window_safe_area) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
 		jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _get_window_safe_area);
 		jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _get_window_safe_area);
 		ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
 		ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
 		jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
 		jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
@@ -139,6 +145,7 @@ void GodotIOJavaWrapper::get_window_safe_area(int (&p_rect_xywh)[4]) {
 String GodotIOJavaWrapper::get_unique_id() {
 String GodotIOJavaWrapper::get_unique_id() {
 	if (_get_unique_id) {
 	if (_get_unique_id) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {
@@ -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) {
 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) {
 	if (_show_keyboard) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
 		jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
 		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);
 		env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
 	}
 	}
@@ -161,6 +169,7 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int
 void GodotIOJavaWrapper::hide_vk() {
 void GodotIOJavaWrapper::hide_vk() {
 	if (_hide_keyboard) {
 	if (_hide_keyboard) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
 		env->CallVoidMethod(godot_io_instance, _hide_keyboard);
 		env->CallVoidMethod(godot_io_instance, _hide_keyboard);
 	}
 	}
 }
 }
@@ -168,6 +177,7 @@ void GodotIOJavaWrapper::hide_vk() {
 void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
 void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
 	if (_set_screen_orientation) {
 	if (_set_screen_orientation) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
 		env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
 		env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
 	}
 	}
 }
 }
@@ -175,6 +185,7 @@ void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
 int GodotIOJavaWrapper::get_screen_orientation() const {
 int GodotIOJavaWrapper::get_screen_orientation() const {
 	if (_get_screen_orientation) {
 	if (_get_screen_orientation) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, 0);
 		return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
 		return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
 	} else {
 	} else {
 		return 0;
 		return 0;
@@ -184,6 +195,7 @@ int GodotIOJavaWrapper::get_screen_orientation() const {
 String GodotIOJavaWrapper::get_system_dir(int p_dir) {
 String GodotIOJavaWrapper::get_system_dir(int p_dir) {
 	if (_get_system_dir) {
 	if (_get_system_dir) {
 		JNIEnv *env = get_jni_env();
 		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);
 		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {

+ 35 - 0
platform/android/java_godot_wrapper.cpp

@@ -94,6 +94,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
 		if (p_env == NULL)
 		if (p_env == NULL)
 			p_env = get_jni_env();
 			p_env = get_jni_env();
 
 
+		ERR_FAIL_COND_V(p_env == nullptr, nullptr);
+
 		jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
 		jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
 		return p_env->GetStaticObjectField(godot_class, fid);
 		return p_env->GetStaticObjectField(godot_class, fid);
 	} else {
 	} else {
@@ -104,6 +106,8 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
 jobject GodotJavaWrapper::get_class_loader() {
 jobject GodotJavaWrapper::get_class_loader() {
 	if (_get_class_loader) {
 	if (_get_class_loader) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, nullptr);
+
 		return env->CallObjectMethod(activity, _get_class_loader);
 		return env->CallObjectMethod(activity, _get_class_loader);
 	} else {
 	} else {
 		return NULL;
 		return NULL;
@@ -121,6 +125,7 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
 	if (_on_video_init) {
 	if (_on_video_init) {
 		if (p_env == NULL)
 		if (p_env == NULL)
 			p_env = get_jni_env();
 			p_env = get_jni_env();
+		ERR_FAIL_COND(p_env == nullptr);
 
 
 		p_env->CallVoidMethod(godot_instance, _on_video_init);
 		p_env->CallVoidMethod(godot_instance, _on_video_init);
 	}
 	}
@@ -131,6 +136,7 @@ void GodotJavaWrapper::on_godot_setup_completed(JNIEnv *p_env) {
 		if (p_env == NULL) {
 		if (p_env == NULL) {
 			p_env = get_jni_env();
 			p_env = get_jni_env();
 		}
 		}
+		ERR_FAIL_COND(p_env == nullptr);
 		p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed);
 		p_env->CallVoidMethod(godot_instance, _on_godot_setup_completed);
 	}
 	}
 }
 }
@@ -140,6 +146,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
 		if (p_env == NULL) {
 		if (p_env == NULL) {
 			p_env = get_jni_env();
 			p_env = get_jni_env();
 		}
 		}
+		ERR_FAIL_COND(p_env == nullptr);
 		p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
 		p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
 	}
 	}
 }
 }
@@ -148,6 +155,7 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
 	if (_restart) {
 	if (_restart) {
 		if (p_env == NULL)
 		if (p_env == NULL)
 			p_env = get_jni_env();
 			p_env = get_jni_env();
+		ERR_FAIL_COND(p_env == nullptr);
 
 
 		p_env->CallVoidMethod(godot_instance, _restart);
 		p_env->CallVoidMethod(godot_instance, _restart);
 	}
 	}
@@ -157,6 +165,7 @@ void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
 	if (_finish) {
 	if (_finish) {
 		if (p_env == NULL)
 		if (p_env == NULL)
 			p_env = get_jni_env();
 			p_env = get_jni_env();
+		ERR_FAIL_COND(p_env == nullptr);
 
 
 		p_env->CallVoidMethod(godot_instance, _finish);
 		p_env->CallVoidMethod(godot_instance, _finish);
 	}
 	}
@@ -165,6 +174,8 @@ void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
 void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
 void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
 	if (_set_keep_screen_on) {
 	if (_set_keep_screen_on) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
 		env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
 		env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
 	}
 	}
 }
 }
@@ -172,6 +183,8 @@ void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
 void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
 void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
 	if (_alert) {
 	if (_alert) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
 		jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
 		jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
 		jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
 		jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
 		env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
 		env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
@@ -180,6 +193,8 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
 
 
 int GodotJavaWrapper::get_gles_version_code() {
 int GodotJavaWrapper::get_gles_version_code() {
 	JNIEnv *env = get_jni_env();
 	JNIEnv *env = get_jni_env();
+	ERR_FAIL_COND_V(env == nullptr, 0);
+
 	if (_get_GLES_version_code) {
 	if (_get_GLES_version_code) {
 		return env->CallIntMethod(godot_instance, _get_GLES_version_code);
 		return env->CallIntMethod(godot_instance, _get_GLES_version_code);
 	}
 	}
@@ -194,6 +209,8 @@ bool GodotJavaWrapper::has_get_clipboard() {
 String GodotJavaWrapper::get_clipboard() {
 String GodotJavaWrapper::get_clipboard() {
 	if (_get_clipboard) {
 	if (_get_clipboard) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
+
 		jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
 		jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
 		return jstring_to_string(s, env);
 		return jstring_to_string(s, env);
 	} else {
 	} else {
@@ -204,6 +221,8 @@ String GodotJavaWrapper::get_clipboard() {
 String GodotJavaWrapper::get_input_fallback_mapping() {
 String GodotJavaWrapper::get_input_fallback_mapping() {
 	if (_get_input_fallback_mapping) {
 	if (_get_input_fallback_mapping) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, String());
+
 		jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
 		jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
 		return jstring_to_string(fallback_mapping, env);
 		return jstring_to_string(fallback_mapping, env);
 	} else {
 	} else {
@@ -218,6 +237,8 @@ bool GodotJavaWrapper::has_set_clipboard() {
 void GodotJavaWrapper::set_clipboard(const String &p_text) {
 void GodotJavaWrapper::set_clipboard(const String &p_text) {
 	if (_set_clipboard) {
 	if (_set_clipboard) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
 		jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
 		jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
 		env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
 		env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
 	}
 	}
@@ -226,6 +247,8 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
 bool GodotJavaWrapper::request_permission(const String &p_name) {
 bool GodotJavaWrapper::request_permission(const String &p_name) {
 	if (_request_permission) {
 	if (_request_permission) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, false);
+
 		jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
 		jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
 		return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
 		return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
 	} else {
 	} else {
@@ -236,6 +259,8 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
 bool GodotJavaWrapper::request_permissions() {
 bool GodotJavaWrapper::request_permissions() {
 	if (_request_permissions) {
 	if (_request_permissions) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, false);
+
 		return env->CallBooleanMethod(godot_instance, _request_permissions);
 		return env->CallBooleanMethod(godot_instance, _request_permissions);
 	} else {
 	} else {
 		return false;
 		return false;
@@ -246,6 +271,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
 	Vector<String> permissions_list;
 	Vector<String> permissions_list;
 	if (_get_granted_permissions) {
 	if (_get_granted_permissions) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, permissions_list);
+
 		jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
 		jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
 		jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
 		jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
 
 
@@ -264,6 +291,8 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
 void GodotJavaWrapper::init_input_devices() {
 void GodotJavaWrapper::init_input_devices() {
 	if (_init_input_devices) {
 	if (_init_input_devices) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
 		env->CallVoidMethod(godot_instance, _init_input_devices);
 		env->CallVoidMethod(godot_instance, _init_input_devices);
 	}
 	}
 }
 }
@@ -271,6 +300,8 @@ void GodotJavaWrapper::init_input_devices() {
 jobject GodotJavaWrapper::get_surface() {
 jobject GodotJavaWrapper::get_surface() {
 	if (_get_surface) {
 	if (_get_surface) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, nullptr);
+
 		return env->CallObjectMethod(godot_instance, _get_surface);
 		return env->CallObjectMethod(godot_instance, _get_surface);
 	} else {
 	} else {
 		return NULL;
 		return NULL;
@@ -280,6 +311,8 @@ jobject GodotJavaWrapper::get_surface() {
 bool GodotJavaWrapper::is_activity_resumed() {
 bool GodotJavaWrapper::is_activity_resumed() {
 	if (_is_activity_resumed) {
 	if (_is_activity_resumed) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND_V(env == nullptr, false);
+
 		return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
 		return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
 	} else {
 	} else {
 		return false;
 		return false;
@@ -289,6 +322,8 @@ bool GodotJavaWrapper::is_activity_resumed() {
 void GodotJavaWrapper::vibrate(int p_duration_ms) {
 void GodotJavaWrapper::vibrate(int p_duration_ms) {
 	if (_vibrate) {
 	if (_vibrate) {
 		JNIEnv *env = get_jni_env();
 		JNIEnv *env = get_jni_env();
+		ERR_FAIL_COND(env == nullptr);
+
 		env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
 		env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
 	}
 	}
 }
 }

+ 26 - 1
platform/android/thread_jandroid.cpp

@@ -30,17 +30,34 @@
 
 
 #include "thread_jandroid.h"
 #include "thread_jandroid.h"
 
 
+#include <android/log.h>
+
 #include "core/os/thread.h"
 #include "core/os/thread.h"
 
 
 static JavaVM *java_vm = nullptr;
 static JavaVM *java_vm = nullptr;
 static thread_local JNIEnv *env = 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() {
 static void init_thread() {
+	if (env) {
+		// thread never detached! just keep using...
+		return;
+	}
+
 	java_vm->AttachCurrentThread(&env, nullptr);
 	java_vm->AttachCurrentThread(&env, nullptr);
 }
 }
 
 
 static void term_thread() {
 static void term_thread() {
 	java_vm->DetachCurrentThread();
 	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) {
 void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
@@ -50,9 +67,17 @@ void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
 }
 }
 
 
 void setup_android_thread() {
 void setup_android_thread() {
-	init_thread();
+	if (!env) {
+		// !BAS! see remarks above
+		init_thread();
+	}
 }
 }
 
 
 JNIEnv *get_jni_env() {
 JNIEnv *get_jni_env() {
+	if (!env) {
+		// !BAS! see remarks above
+		init_thread();
+	}
+
 	return env;
 	return env;
 }
 }