1 /*************************************************************************/
2 /* android_support.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "android_support.h"
32
33 #if defined(ANDROID_ENABLED)
34
35 #include <dlfcn.h> // dlopen, dlsym
36 #include <mono/utils/mono-dl-fallback.h>
37 #include <sys/system_properties.h>
38 #include <cstddef>
39
40 #if __ANDROID_API__ < 24
41 #include "thirdparty/misc/ifaddrs-android.h"
42 #else
43 #include <ifaddrs.h>
44 #endif
45
46 #include "core/os/os.h"
47 #include "core/ustring.h"
48 #include "platform/android/java_godot_wrapper.h"
49 #include "platform/android/os_android.h"
50 #include "platform/android/thread_jandroid.h"
51
52 #include "../../utils/path_utils.h"
53 #include "../../utils/string_utils.h"
54 #include "../gd_mono_cache.h"
55 #include "../gd_mono_marshal.h"
56
57 // Warning: JNI boilerplate ahead... continue at your own risk
58
59 namespace gdmono {
60 namespace android {
61 namespace support {
62
63 template <typename T>
64 struct ScopedLocalRef {
65 JNIEnv *env;
66 T local_ref;
67
getgdmono::android::support::ScopedLocalRef68 _FORCE_INLINE_ T get() const { return local_ref; }
operator Tgdmono::android::support::ScopedLocalRef69 _FORCE_INLINE_ operator T() const { return local_ref; }
operator jvaluegdmono::android::support::ScopedLocalRef70 _FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
71
operator boolgdmono::android::support::ScopedLocalRef72 _FORCE_INLINE_ operator bool() const { return local_ref != NULL; }
73
operator ==gdmono::android::support::ScopedLocalRef74 _FORCE_INLINE_ bool operator==(std::nullptr_t) const {
75 return local_ref == nullptr;
76 }
77
operator !=gdmono::android::support::ScopedLocalRef78 _FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
79 return local_ref != nullptr;
80 }
81
82 ScopedLocalRef(const ScopedLocalRef &) = delete;
83 ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
84
ScopedLocalRefgdmono::android::support::ScopedLocalRef85 ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
86 env(p_env),
87 local_ref(p_local_ref) {
88 }
89
~ScopedLocalRefgdmono::android::support::ScopedLocalRef90 ~ScopedLocalRef() {
91 if (local_ref) {
92 env->DeleteLocalRef(local_ref);
93 }
94 }
95 };
96
jni_exception_check(JNIEnv * p_env)97 bool jni_exception_check(JNIEnv *p_env) {
98 if (p_env->ExceptionCheck()) {
99 // Print the exception to logcat
100 p_env->ExceptionDescribe();
101
102 p_env->ExceptionClear();
103 return true;
104 }
105
106 return false;
107 }
108
109 String app_native_lib_dir_cache;
110
determine_app_native_lib_dir()111 String determine_app_native_lib_dir() {
112 JNIEnv *env = ThreadAndroid::get_env();
113
114 ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
115 jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
116 ScopedLocalRef<jobject> activityThread(env, env->CallStaticObjectMethod(activityThreadClass, currentActivityThread));
117 jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
118 ScopedLocalRef<jobject> ctx(env, env->CallObjectMethod(activityThread, getApplication));
119
120 jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
121 ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(ctx, getApplicationInfo));
122 jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
123 ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
124
125 String result;
126
127 const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
128 if (nativeLibraryDirUtf8) {
129 result.parse_utf8(nativeLibraryDirUtf8);
130 env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
131 }
132
133 return result;
134 }
135
get_app_native_lib_dir()136 String get_app_native_lib_dir() {
137 if (app_native_lib_dir_cache.empty())
138 app_native_lib_dir_cache = determine_app_native_lib_dir();
139 return app_native_lib_dir_cache;
140 }
141
gd_mono_convert_dl_flags(int flags)142 int gd_mono_convert_dl_flags(int flags) {
143 // from mono's runtime-bootstrap.c
144
145 int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL;
146
147 if (flags & MONO_DL_LAZY)
148 lflags |= RTLD_LAZY;
149 else
150 lflags |= RTLD_NOW;
151
152 return lflags;
153 }
154
155 #ifndef GD_MONO_SO_NAME
156 #define GD_MONO_SO_NAME "libmonosgen-2.0.so"
157 #endif
158
159 const char *mono_so_name = GD_MONO_SO_NAME;
160 const char *godot_so_name = "libgodot_android.so";
161
162 void *mono_dl_handle = NULL;
163 void *godot_dl_handle = NULL;
164
try_dlopen(const String & p_so_path,int p_flags)165 void *try_dlopen(const String &p_so_path, int p_flags) {
166 if (!FileAccess::exists(p_so_path)) {
167 if (OS::get_singleton()->is_stdout_verbose())
168 OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
169 return NULL;
170 }
171
172 int lflags = gd_mono_convert_dl_flags(p_flags);
173
174 void *handle = dlopen(p_so_path.utf8().get_data(), lflags);
175
176 if (!handle) {
177 if (OS::get_singleton()->is_stdout_verbose())
178 OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", p_so_path.utf8().get_data(), dlerror());
179 return NULL;
180 }
181
182 if (OS::get_singleton()->is_stdout_verbose())
183 OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
184
185 return handle;
186 }
187
gd_mono_android_dlopen(const char * p_name,int p_flags,char ** r_err,void * p_user_data)188 void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
189 if (p_name == NULL) {
190 // __Internal
191
192 if (!mono_dl_handle) {
193 String app_native_lib_dir = get_app_native_lib_dir();
194 String so_path = path::join(app_native_lib_dir, mono_so_name);
195
196 mono_dl_handle = try_dlopen(so_path, p_flags);
197 }
198
199 return mono_dl_handle;
200 }
201
202 String name = String::utf8(p_name);
203
204 if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
205 String app_native_lib_dir = get_app_native_lib_dir();
206
207 String orig_so_name = name.get_file();
208 String so_name = "lib-aot-" + orig_so_name;
209 String so_path = path::join(app_native_lib_dir, so_name);
210
211 return try_dlopen(so_path, p_flags);
212 }
213
214 return NULL;
215 }
216
gd_mono_android_dlsym(void * p_handle,const char * p_name,char ** r_err,void * p_user_data)217 void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
218 void *sym_addr = dlsym(p_handle, p_name);
219
220 if (sym_addr)
221 return sym_addr;
222
223 if (p_handle == mono_dl_handle && godot_dl_handle) {
224 // Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
225 // This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
226 sym_addr = dlsym(godot_dl_handle, p_name);
227
228 if (sym_addr)
229 return sym_addr;
230 }
231
232 if (r_err)
233 *r_err = str_format_new("%s\n", dlerror());
234
235 return NULL;
236 }
237
gd_mono_android_dlclose(void * p_handle,void * p_user_data)238 void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
239 dlclose(p_handle);
240
241 // Not sure if this ever happens. Does Mono close the handle for the main module?
242 if (p_handle == mono_dl_handle)
243 mono_dl_handle = NULL;
244
245 return NULL;
246 }
247
248 int32_t build_version_sdk_int = 0;
249
get_build_version_sdk_int()250 int32_t get_build_version_sdk_int() {
251 // The JNI code is the equivalent of:
252 //
253 // android.os.Build.VERSION.SDK_INT
254
255 if (build_version_sdk_int == 0) {
256 JNIEnv *env = ThreadAndroid::get_env();
257
258 jclass versionClass = env->FindClass("android/os/Build$VERSION");
259 ERR_FAIL_NULL_V(versionClass, 0);
260
261 jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
262 ERR_FAIL_NULL_V(sdkIntField, 0);
263
264 build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
265 }
266
267 return build_version_sdk_int;
268 }
269
270 jobject certStore = NULL; // KeyStore
271
_gd_mono_init_cert_store()272 MonoBoolean _gd_mono_init_cert_store() {
273 // The JNI code is the equivalent of:
274 //
275 // try {
276 // certStoreLocal = KeyStore.getInstance("AndroidCAStore");
277 // certStoreLocal.load(null);
278 // certStore = certStoreLocal;
279 // return true;
280 // } catch (Exception e) {
281 // return false;
282 // }
283
284 JNIEnv *env = ThreadAndroid::get_env();
285
286 ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
287
288 jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
289 jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
290
291 ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
292
293 ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
294
295 if (jni_exception_check(env))
296 return 0;
297
298 env->CallVoidMethod(certStoreLocal, load, NULL);
299
300 if (jni_exception_check(env))
301 return 0;
302
303 certStore = env->NewGlobalRef(certStoreLocal);
304
305 return 1;
306 }
307
_gd_mono_android_cert_store_lookup(MonoString * p_alias)308 MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
309 // The JNI code is the equivalent of:
310 //
311 // Certificate certificate = certStore.getCertificate(alias);
312 // if (certificate == null)
313 // return null;
314 // return certificate.getEncoded();
315
316 MonoError mono_error;
317 char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
318
319 if (!mono_error_ok(&mono_error)) {
320 ERR_PRINTS(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
321 mono_error_cleanup(&mono_error);
322 return NULL;
323 }
324
325 JNIEnv *env = ThreadAndroid::get_env();
326
327 ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
328 mono_free(alias_utf8);
329
330 ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
331 ERR_FAIL_NULL_V(keyStoreClass, NULL);
332 ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
333 ERR_FAIL_NULL_V(certificateClass, NULL);
334
335 jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
336 ERR_FAIL_NULL_V(getCertificate, NULL);
337
338 jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
339 ERR_FAIL_NULL_V(getEncoded, NULL);
340
341 ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
342
343 if (!certificate)
344 return NULL;
345
346 ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
347 jsize encodedLength = env->GetArrayLength(encoded);
348
349 MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
350 uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
351
352 env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
353
354 return encoded_ret;
355 }
356
register_internal_calls()357 void register_internal_calls() {
358 mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store);
359 mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup);
360 }
361
initialize()362 void initialize() {
363 // We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
364 OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
365
366 mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, NULL);
367
368 String app_native_lib_dir = get_app_native_lib_dir();
369 String so_path = path::join(app_native_lib_dir, godot_so_name);
370
371 godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
372 }
373
cleanup()374 void cleanup() {
375 // This is called after shutting down the Mono runtime
376
377 if (mono_dl_handle)
378 gd_mono_android_dlclose(mono_dl_handle, NULL);
379
380 if (godot_dl_handle)
381 gd_mono_android_dlclose(godot_dl_handle, NULL);
382
383 JNIEnv *env = ThreadAndroid::get_env();
384
385 if (certStore) {
386 env->DeleteGlobalRef(certStore);
387 certStore = NULL;
388 }
389 }
390
391 } // namespace support
392 } // namespace android
393 } // namespace gdmono
394
395 using namespace gdmono::android::support;
396
397 // The following are P/Invoke functions required by the monodroid profile of the BCL.
398 // These are P/Invoke functions and not internal calls, hence why they use
399 // 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
400
401 #define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
402
_monodroid_get_android_api_level()403 GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
404 return get_build_version_sdk_int();
405 }
406
monodroid_free(void * ptr)407 GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
408 free(ptr);
409 }
410
monodroid_get_system_property(const char * p_name,char ** r_value)411 GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
412 char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
413
414 int len = __system_property_get(p_name, prop_value_str);
415
416 if (r_value) {
417 if (len >= 0) {
418 *r_value = (char *)malloc(len + 1);
419 if (!*r_value)
420 return -1;
421 memcpy(*r_value, prop_value_str, len);
422 (*r_value)[len] = '\0';
423 } else {
424 *r_value = NULL;
425 }
426 }
427
428 return len;
429 }
430
_monodroid_get_network_interface_up_state(const char * p_ifname,mono_bool * r_is_up)431 GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
432 // The JNI code is the equivalent of:
433 //
434 // NetworkInterface.getByName(p_ifname).isUp()
435
436 if (!r_is_up || !p_ifname || strlen(p_ifname) == 0)
437 return 0;
438
439 *r_is_up = 0;
440
441 JNIEnv *env = ThreadAndroid::get_env();
442
443 jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
444 ERR_FAIL_NULL_V(networkInterfaceClass, 0);
445
446 jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
447 ERR_FAIL_NULL_V(getByName, 0);
448
449 jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
450 ERR_FAIL_NULL_V(isUp, 0);
451
452 ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
453 ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
454
455 if (!networkInterface)
456 return 0;
457
458 *r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
459
460 return 1;
461 }
462
_monodroid_get_network_interface_supports_multicast(const char * p_ifname,mono_bool * r_supports_multicast)463 GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
464 // The JNI code is the equivalent of:
465 //
466 // NetworkInterface.getByName(p_ifname).supportsMulticast()
467
468 if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0)
469 return 0;
470
471 *r_supports_multicast = 0;
472
473 JNIEnv *env = ThreadAndroid::get_env();
474
475 jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
476 ERR_FAIL_NULL_V(networkInterfaceClass, 0);
477
478 jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
479 ERR_FAIL_NULL_V(getByName, 0);
480
481 jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
482 ERR_FAIL_NULL_V(supportsMulticast, 0);
483
484 ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
485 ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
486
487 if (!networkInterface)
488 return 0;
489
490 *r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
491
492 return 1;
493 }
494
495 static const int dns_servers_len = 8;
496
interop_get_active_network_dns_servers(char ** r_dns_servers,int * dns_servers_count)497 static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
498 // The JNI code is the equivalent of:
499 //
500 // ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
501 // .getSystemService(Context.CONNECTIVITY_SERVICE);
502 // Network activeNerwork = connectivityManager.getActiveNetwork();
503 // LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
504 // List<String> dnsServers = linkProperties.getDnsServers().stream()
505 // .map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
506
507 #ifdef DEBUG_ENABLED
508 CRASH_COND(get_build_version_sdk_int() < 23);
509 #endif
510
511 JNIEnv *env = ThreadAndroid::get_env();
512
513 GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
514 jobject activity = godot_java->get_activity();
515
516 ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
517 ERR_FAIL_NULL(activityClass);
518
519 jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
520
521 ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
522
523 ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
524 ERR_FAIL_NULL(contextClass);
525
526 jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
527 ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
528
529 jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
530
531 ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
532
533 if (!connectivityManager)
534 return;
535
536 ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
537 ERR_FAIL_NULL(connectivityManagerClass);
538
539 jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
540 ERR_FAIL_NULL(getActiveNetwork);
541
542 ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
543
544 if (!activeNetwork)
545 return;
546
547 jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
548 "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
549 ERR_FAIL_NULL(getLinkProperties);
550
551 ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
552
553 if (!linkProperties)
554 return;
555
556 ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
557 ERR_FAIL_NULL(linkPropertiesClass);
558
559 jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
560 ERR_FAIL_NULL(getDnsServers);
561
562 ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
563
564 if (!dnsServers)
565 return;
566
567 ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
568 ERR_FAIL_NULL(listClass);
569
570 jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
571 ERR_FAIL_NULL(listSize);
572
573 int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
574
575 if (dnsServersCount > dns_servers_len)
576 dnsServersCount = dns_servers_len;
577
578 if (dnsServersCount <= 0)
579 return;
580
581 jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
582 ERR_FAIL_NULL(listGet);
583
584 ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
585 ERR_FAIL_NULL(inetAddressClass);
586
587 jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
588 ERR_FAIL_NULL(getHostAddress);
589
590 for (int i = 0; i < dnsServersCount; i++) {
591 ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
592 if (!dnsServer)
593 continue;
594
595 ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
596 const char *host_address = env->GetStringUTFChars(hostAddress, 0);
597
598 r_dns_servers[i] = strdup(host_address); // freed by the BCL
599 (*dns_servers_count)++;
600
601 env->ReleaseStringUTFChars(hostAddress, host_address);
602 }
603
604 // jesus...
605 }
606
_monodroid_get_dns_servers(void ** r_dns_servers_array)607 GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
608 if (!r_dns_servers_array)
609 return -1;
610
611 *r_dns_servers_array = NULL;
612
613 char *dns_servers[dns_servers_len];
614 int dns_servers_count = 0;
615
616 if (_monodroid_get_android_api_level() < 26) {
617 // The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
618 // https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
619
620 char prop_name[] = "net.dns*";
621
622 for (int i = 0; i < dns_servers_len; i++) {
623 prop_name[7] = (char)(i + 0x31);
624 char *prop_value;
625 int32_t len = monodroid_get_system_property(prop_name, &prop_value);
626
627 if (len > 0) {
628 dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
629 dns_servers_count++;
630 free(prop_value);
631 }
632 }
633 } else {
634 // Alternative for Oreo and greater
635 interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
636 }
637
638 if (dns_servers_count > 0) {
639 size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
640 *r_dns_servers_array = malloc(ret_size); // freed by the BCL
641 memcpy(*r_dns_servers_array, dns_servers, ret_size);
642 }
643
644 return dns_servers_count;
645 }
646
_monodroid_timezone_get_default_id()647 GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
648 // The JNI code is the equivalent of:
649 //
650 // TimeZone.getDefault().getID()
651
652 JNIEnv *env = ThreadAndroid::get_env();
653
654 ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
655 ERR_FAIL_NULL_V(timeZoneClass, NULL);
656
657 jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
658 ERR_FAIL_NULL_V(getDefault, NULL);
659
660 jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
661 ERR_FAIL_NULL_V(getID, NULL);
662
663 ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
664
665 if (!defaultTimeZone)
666 return NULL;
667
668 ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
669
670 if (!defaultTimeZoneID)
671 return NULL;
672
673 const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
674
675 char *result = strdup(default_time_zone_id); // freed by the BCL
676
677 env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
678
679 return result;
680 }
681
_monodroid_getifaddrs(struct ifaddrs ** p_ifap)682 GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
683 return getifaddrs(p_ifap);
684 }
685
_monodroid_freeifaddrs(struct ifaddrs * p_ifap)686 GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
687 freeifaddrs(p_ifap);
688 }
689
690 #endif
691