android_support.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. /**************************************************************************/
  2. /* android_support.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  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. #include "android_support.h"
  31. #if defined(ANDROID_ENABLED)
  32. #include <dlfcn.h> // dlopen, dlsym
  33. #include <mono/utils/mono-dl-fallback.h>
  34. #include <sys/system_properties.h>
  35. #include <cstddef>
  36. #if __ANDROID_API__ < 24
  37. #include "thirdparty/misc/ifaddrs-android.h"
  38. #else
  39. #include <ifaddrs.h>
  40. #endif
  41. #include "core/os/os.h"
  42. #include "core/ustring.h"
  43. #include "platform/android/java_godot_wrapper.h"
  44. #include "platform/android/os_android.h"
  45. #include "platform/android/thread_jandroid.h"
  46. #include "../../utils/path_utils.h"
  47. #include "../../utils/string_utils.h"
  48. #include "../gd_mono_cache.h"
  49. #include "../gd_mono_marshal.h"
  50. // Warning: JNI boilerplate ahead... continue at your own risk
  51. namespace gdmono {
  52. namespace android {
  53. namespace support {
  54. template <typename T>
  55. struct ScopedLocalRef {
  56. JNIEnv *env;
  57. T local_ref;
  58. _FORCE_INLINE_ T get() const { return local_ref; }
  59. _FORCE_INLINE_ operator T() const { return local_ref; }
  60. _FORCE_INLINE_ operator jvalue() const { return (jvalue)local_ref; }
  61. _FORCE_INLINE_ operator bool() const { return local_ref != NULL; }
  62. _FORCE_INLINE_ bool operator==(std::nullptr_t) const {
  63. return local_ref == nullptr;
  64. }
  65. _FORCE_INLINE_ bool operator!=(std::nullptr_t) const {
  66. return local_ref != nullptr;
  67. }
  68. ScopedLocalRef(const ScopedLocalRef &) = delete;
  69. ScopedLocalRef &operator=(const ScopedLocalRef &) = delete;
  70. ScopedLocalRef(JNIEnv *p_env, T p_local_ref) :
  71. env(p_env),
  72. local_ref(p_local_ref) {
  73. }
  74. ~ScopedLocalRef() {
  75. if (local_ref) {
  76. env->DeleteLocalRef(local_ref);
  77. }
  78. }
  79. };
  80. bool jni_exception_check(JNIEnv *p_env) {
  81. if (p_env->ExceptionCheck()) {
  82. // Print the exception to logcat
  83. p_env->ExceptionDescribe();
  84. p_env->ExceptionClear();
  85. return true;
  86. }
  87. return false;
  88. }
  89. String app_native_lib_dir_cache;
  90. String determine_app_native_lib_dir() {
  91. // The JNI code is the equivalent of:
  92. //
  93. // return godotActivity.getApplicationInfo().nativeLibraryDir;
  94. JNIEnv *env = get_jni_env();
  95. GodotJavaWrapper *godot_java = static_cast<OS_Android *>(OS::get_singleton())->get_godot_java();
  96. jobject activity = godot_java->get_activity();
  97. ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
  98. jmethodID getApplicationInfo = env->GetMethodID(contextClass, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
  99. ScopedLocalRef<jobject> applicationInfo(env, env->CallObjectMethod(activity, getApplicationInfo));
  100. jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
  101. ScopedLocalRef<jstring> nativeLibraryDir(env, (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField));
  102. String result;
  103. const char *const nativeLibraryDirUtf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
  104. if (nativeLibraryDirUtf8) {
  105. result.parse_utf8(nativeLibraryDirUtf8);
  106. env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDirUtf8);
  107. }
  108. return result;
  109. }
  110. String get_app_native_lib_dir() {
  111. if (app_native_lib_dir_cache.empty())
  112. app_native_lib_dir_cache = determine_app_native_lib_dir();
  113. return app_native_lib_dir_cache;
  114. }
  115. int gd_mono_convert_dl_flags(int flags) {
  116. // from mono's runtime-bootstrap.c
  117. int lflags = flags & MONO_DL_LOCAL ? 0 : RTLD_GLOBAL;
  118. if (flags & MONO_DL_LAZY)
  119. lflags |= RTLD_LAZY;
  120. else
  121. lflags |= RTLD_NOW;
  122. return lflags;
  123. }
  124. #ifndef GD_MONO_SO_NAME
  125. #define GD_MONO_SO_NAME "libmonosgen-2.0.so"
  126. #endif
  127. const char *mono_so_name = GD_MONO_SO_NAME;
  128. const char *godot_so_name = "libgodot_android.so";
  129. void *mono_dl_handle = NULL;
  130. void *godot_dl_handle = NULL;
  131. void *_try_dlopen_file_path(const String &p_so_path, int p_flags) {
  132. if (!FileAccess::exists(p_so_path)) {
  133. if (OS::get_singleton()->is_stdout_verbose()) {
  134. OS::get_singleton()->print("Cannot find shared library: '%s'\n", p_so_path.utf8().get_data());
  135. }
  136. return nullptr;
  137. }
  138. int lflags = gd_mono_convert_dl_flags(p_flags);
  139. void *handle = dlopen(p_so_path.utf8().get_data(), lflags);
  140. if (!handle) {
  141. if (OS::get_singleton()->is_stdout_verbose()) {
  142. OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", p_so_path.utf8().get_data(), dlerror());
  143. }
  144. return nullptr;
  145. }
  146. if (OS::get_singleton()->is_stdout_verbose()) {
  147. OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", p_so_path.utf8().get_data());
  148. }
  149. return handle;
  150. }
  151. void *try_dlopen(const String &p_so_path, int p_flags) {
  152. void *handle = _try_dlopen_file_path(p_so_path, p_flags);
  153. if (handle) {
  154. return handle;
  155. }
  156. // Try only with the file name, without specifying the location.
  157. // This is needed when installing from Android App Bundles, as the native
  158. // libraries are not extracted. They are loaded directly from the APK.
  159. // See: https://stackoverflow.com/a/56551499
  160. // If we pass only the file name to dlopen without the location, it should
  161. // search the native libraries in all locations, including inside the apk.
  162. String so_name = p_so_path.get_file();
  163. int lflags = gd_mono_convert_dl_flags(p_flags);
  164. handle = dlopen(so_name.utf8().get_data(), lflags);
  165. if (!handle) {
  166. if (OS::get_singleton()->is_stdout_verbose()) {
  167. OS::get_singleton()->print("Failed to open shared library: '%s'. Error: '%s'\n", so_name.utf8().get_data(), dlerror());
  168. }
  169. return nullptr;
  170. }
  171. if (OS::get_singleton()->is_stdout_verbose()) {
  172. OS::get_singleton()->print("Successfully loaded shared library: '%s'\n", so_name.utf8().get_data());
  173. }
  174. return handle;
  175. }
  176. void *gd_mono_android_dlopen(const char *p_name, int p_flags, char **r_err, void *p_user_data) {
  177. if (p_name == NULL) {
  178. // __Internal
  179. if (!mono_dl_handle) {
  180. String app_native_lib_dir = get_app_native_lib_dir();
  181. String so_path = path::join(app_native_lib_dir, mono_so_name);
  182. mono_dl_handle = try_dlopen(so_path, p_flags);
  183. ERR_FAIL_COND_V_MSG(!mono_dl_handle, nullptr, "Failed to load Mono native library from path");
  184. }
  185. return mono_dl_handle;
  186. }
  187. String name = String::utf8(p_name);
  188. if (name.ends_with(".dll.so") || name.ends_with(".exe.so")) {
  189. String app_native_lib_dir = get_app_native_lib_dir();
  190. String orig_so_name = name.get_file();
  191. String so_name = "lib-aot-" + orig_so_name;
  192. String so_path = path::join(app_native_lib_dir, so_name);
  193. return try_dlopen(so_path, p_flags);
  194. }
  195. return NULL;
  196. }
  197. void *gd_mono_android_dlsym(void *p_handle, const char *p_name, char **r_err, void *p_user_data) {
  198. void *sym_addr = dlsym(p_handle, p_name);
  199. if (sym_addr)
  200. return sym_addr;
  201. if (p_handle == mono_dl_handle && godot_dl_handle) {
  202. // Looking up for '__Internal' P/Invoke. We want to search in both the Mono and Godot shared libraries.
  203. // This is needed to resolve the monodroid P/Invoke functions that are defined at the bottom of the file.
  204. sym_addr = dlsym(godot_dl_handle, p_name);
  205. if (sym_addr)
  206. return sym_addr;
  207. }
  208. if (r_err)
  209. *r_err = str_format_new("%s\n", dlerror());
  210. return NULL;
  211. }
  212. void *gd_mono_android_dlclose(void *p_handle, void *p_user_data) {
  213. dlclose(p_handle);
  214. // Not sure if this ever happens. Does Mono close the handle for the main module?
  215. if (p_handle == mono_dl_handle)
  216. mono_dl_handle = NULL;
  217. return NULL;
  218. }
  219. int32_t build_version_sdk_int = 0;
  220. int32_t get_build_version_sdk_int() {
  221. // The JNI code is the equivalent of:
  222. //
  223. // android.os.Build.VERSION.SDK_INT
  224. if (build_version_sdk_int == 0) {
  225. JNIEnv *env = get_jni_env();
  226. jclass versionClass = env->FindClass("android/os/Build$VERSION");
  227. ERR_FAIL_NULL_V(versionClass, 0);
  228. jfieldID sdkIntField = env->GetStaticFieldID(versionClass, "SDK_INT", "I");
  229. ERR_FAIL_NULL_V(sdkIntField, 0);
  230. build_version_sdk_int = (int32_t)env->GetStaticIntField(versionClass, sdkIntField);
  231. }
  232. return build_version_sdk_int;
  233. }
  234. jobject certStore = NULL; // KeyStore
  235. MonoBoolean _gd_mono_init_cert_store() {
  236. // The JNI code is the equivalent of:
  237. //
  238. // try {
  239. // certStoreLocal = KeyStore.getInstance("AndroidCAStore");
  240. // certStoreLocal.load(null);
  241. // certStore = certStoreLocal;
  242. // return true;
  243. // } catch (Exception e) {
  244. // return false;
  245. // }
  246. JNIEnv *env = get_jni_env();
  247. ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
  248. jmethodID getInstance = env->GetStaticMethodID(keyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;");
  249. jmethodID load = env->GetMethodID(keyStoreClass, "load", "(Ljava/security/KeyStore$LoadStoreParameter;)V");
  250. ScopedLocalRef<jstring> androidCAStoreString(env, env->NewStringUTF("AndroidCAStore"));
  251. ScopedLocalRef<jobject> certStoreLocal(env, env->CallStaticObjectMethod(keyStoreClass, getInstance, androidCAStoreString.get()));
  252. if (jni_exception_check(env))
  253. return 0;
  254. env->CallVoidMethod(certStoreLocal, load, NULL);
  255. if (jni_exception_check(env))
  256. return 0;
  257. certStore = env->NewGlobalRef(certStoreLocal);
  258. return 1;
  259. }
  260. MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
  261. // The JNI code is the equivalent of:
  262. //
  263. // Certificate certificate = certStore.getCertificate(alias);
  264. // if (certificate == null)
  265. // return null;
  266. // return certificate.getEncoded();
  267. MonoError mono_error;
  268. char *alias_utf8 = mono_string_to_utf8_checked(p_alias, &mono_error);
  269. if (!mono_error_ok(&mono_error)) {
  270. ERR_PRINT(String() + "Failed to convert MonoString* to UTF-8: '" + mono_error_get_message(&mono_error) + "'.");
  271. mono_error_cleanup(&mono_error);
  272. return NULL;
  273. }
  274. JNIEnv *env = get_jni_env();
  275. ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
  276. mono_free(alias_utf8);
  277. ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
  278. ERR_FAIL_NULL_V(keyStoreClass, NULL);
  279. ScopedLocalRef<jclass> certificateClass(env, env->FindClass("java/security/cert/Certificate"));
  280. ERR_FAIL_NULL_V(certificateClass, NULL);
  281. jmethodID getCertificate = env->GetMethodID(keyStoreClass, "getCertificate", "(Ljava/lang/String;)Ljava/security/cert/Certificate;");
  282. ERR_FAIL_NULL_V(getCertificate, NULL);
  283. jmethodID getEncoded = env->GetMethodID(certificateClass, "getEncoded", "()[B");
  284. ERR_FAIL_NULL_V(getEncoded, NULL);
  285. ScopedLocalRef<jobject> certificate(env, env->CallObjectMethod(certStore, getCertificate, js_alias.get()));
  286. if (!certificate)
  287. return NULL;
  288. ScopedLocalRef<jbyteArray> encoded(env, (jbyteArray)env->CallObjectMethod(certificate, getEncoded));
  289. jsize encodedLength = env->GetArrayLength(encoded);
  290. MonoArray *encoded_ret = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(uint8_t), encodedLength);
  291. uint8_t *dest = (uint8_t *)mono_array_addr(encoded_ret, uint8_t, 0);
  292. env->GetByteArrayRegion(encoded, 0, encodedLength, reinterpret_cast<jbyte *>(dest));
  293. return encoded_ret;
  294. }
  295. void register_internal_calls() {
  296. GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store);
  297. GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup);
  298. }
  299. void initialize() {
  300. // We need to set this environment variable to make the monodroid BCL use btls instead of legacy as the default provider
  301. OS::get_singleton()->set_environment("XA_TLS_PROVIDER", "btls");
  302. mono_dl_fallback_register(gd_mono_android_dlopen, gd_mono_android_dlsym, gd_mono_android_dlclose, NULL);
  303. String app_native_lib_dir = get_app_native_lib_dir();
  304. String so_path = path::join(app_native_lib_dir, godot_so_name);
  305. godot_dl_handle = try_dlopen(so_path, gd_mono_convert_dl_flags(MONO_DL_LAZY));
  306. ERR_FAIL_COND_MSG(!godot_dl_handle, "Failed to load Godot native library");
  307. }
  308. void cleanup() {
  309. // This is called after shutting down the Mono runtime
  310. if (mono_dl_handle)
  311. gd_mono_android_dlclose(mono_dl_handle, NULL);
  312. if (godot_dl_handle)
  313. gd_mono_android_dlclose(godot_dl_handle, NULL);
  314. JNIEnv *env = get_jni_env();
  315. if (certStore) {
  316. env->DeleteGlobalRef(certStore);
  317. certStore = NULL;
  318. }
  319. }
  320. } // namespace support
  321. } // namespace android
  322. } // namespace gdmono
  323. using namespace gdmono::android::support;
  324. // The following are P/Invoke functions required by the monodroid profile of the BCL.
  325. // These are P/Invoke functions and not internal calls, hence why they use
  326. // 'mono_bool' and 'const char*' instead of 'MonoBoolean' and 'MonoString*'.
  327. #define GD_PINVOKE_EXPORT extern "C" __attribute__((visibility("default")))
  328. GD_PINVOKE_EXPORT int32_t _monodroid_get_android_api_level() {
  329. return get_build_version_sdk_int();
  330. }
  331. GD_PINVOKE_EXPORT void monodroid_free(void *ptr) {
  332. free(ptr);
  333. }
  334. GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char **r_value) {
  335. char prop_value_str[PROP_VALUE_MAX + 1] = { 0 };
  336. int len = __system_property_get(p_name, prop_value_str);
  337. if (r_value) {
  338. if (len >= 0) {
  339. *r_value = (char *)malloc(len + 1);
  340. ERR_FAIL_NULL_V_MSG(*r_value, -1, "Out of memory.");
  341. memcpy(*r_value, prop_value_str, len);
  342. (*r_value)[len] = '\0';
  343. } else {
  344. *r_value = NULL;
  345. }
  346. }
  347. return len;
  348. }
  349. GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char *p_ifname, mono_bool *r_is_up) {
  350. // The JNI code is the equivalent of:
  351. //
  352. // NetworkInterface.getByName(p_ifname).isUp()
  353. if (!r_is_up || !p_ifname || strlen(p_ifname) == 0)
  354. return 0;
  355. *r_is_up = 0;
  356. JNIEnv *env = get_jni_env();
  357. jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
  358. ERR_FAIL_NULL_V(networkInterfaceClass, 0);
  359. jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
  360. ERR_FAIL_NULL_V(getByName, 0);
  361. jmethodID isUp = env->GetMethodID(networkInterfaceClass, "isUp", "()Z");
  362. ERR_FAIL_NULL_V(isUp, 0);
  363. ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
  364. ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
  365. if (!networkInterface)
  366. return 0;
  367. *r_is_up = (mono_bool)env->CallBooleanMethod(networkInterface, isUp);
  368. return 1;
  369. }
  370. GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(const char *p_ifname, mono_bool *r_supports_multicast) {
  371. // The JNI code is the equivalent of:
  372. //
  373. // NetworkInterface.getByName(p_ifname).supportsMulticast()
  374. if (!r_supports_multicast || !p_ifname || strlen(p_ifname) == 0)
  375. return 0;
  376. *r_supports_multicast = 0;
  377. JNIEnv *env = get_jni_env();
  378. jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
  379. ERR_FAIL_NULL_V(networkInterfaceClass, 0);
  380. jmethodID getByName = env->GetStaticMethodID(networkInterfaceClass, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
  381. ERR_FAIL_NULL_V(getByName, 0);
  382. jmethodID supportsMulticast = env->GetMethodID(networkInterfaceClass, "supportsMulticast", "()Z");
  383. ERR_FAIL_NULL_V(supportsMulticast, 0);
  384. ScopedLocalRef<jstring> js_ifname(env, env->NewStringUTF(p_ifname));
  385. ScopedLocalRef<jobject> networkInterface(env, env->CallStaticObjectMethod(networkInterfaceClass, getByName, js_ifname.get()));
  386. if (!networkInterface)
  387. return 0;
  388. *r_supports_multicast = (mono_bool)env->CallBooleanMethod(networkInterface, supportsMulticast);
  389. return 1;
  390. }
  391. static const int dns_servers_len = 8;
  392. static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dns_servers_count) {
  393. // The JNI code is the equivalent of:
  394. //
  395. // ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext()
  396. // .getSystemService(Context.CONNECTIVITY_SERVICE);
  397. // Network activeNerwork = connectivityManager.getActiveNetwork();
  398. // LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNerwork);
  399. // List<String> dnsServers = linkProperties.getDnsServers().stream()
  400. // .map(inetAddress -> inetAddress.getHostAddress()).collect(Collectors.toList());
  401. #ifdef DEBUG_ENABLED
  402. CRASH_COND(get_build_version_sdk_int() < 23);
  403. #endif
  404. JNIEnv *env = get_jni_env();
  405. GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
  406. jobject activity = godot_java->get_activity();
  407. ScopedLocalRef<jclass> activityClass(env, env->GetObjectClass(activity));
  408. ERR_FAIL_NULL(activityClass);
  409. jmethodID getApplicationContext = env->GetMethodID(activityClass, "getApplicationContext", "()Landroid/content/Context;");
  410. ScopedLocalRef<jobject> applicationContext(env, env->CallObjectMethod(activity, getApplicationContext));
  411. ScopedLocalRef<jclass> contextClass(env, env->FindClass("android/content/Context"));
  412. ERR_FAIL_NULL(contextClass);
  413. jfieldID connectivityServiceField = env->GetStaticFieldID(contextClass, "CONNECTIVITY_SERVICE", "Ljava/lang/String;");
  414. ScopedLocalRef<jstring> connectivityServiceString(env, (jstring)env->GetStaticObjectField(contextClass, connectivityServiceField));
  415. jmethodID getSystemService = env->GetMethodID(contextClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
  416. ScopedLocalRef<jobject> connectivityManager(env, env->CallObjectMethod(applicationContext, getSystemService, connectivityServiceString.get()));
  417. if (!connectivityManager)
  418. return;
  419. ScopedLocalRef<jclass> connectivityManagerClass(env, env->FindClass("android/net/ConnectivityManager"));
  420. ERR_FAIL_NULL(connectivityManagerClass);
  421. jmethodID getActiveNetwork = env->GetMethodID(connectivityManagerClass, "getActiveNetwork", "()Landroid/net/Network;");
  422. ERR_FAIL_NULL(getActiveNetwork);
  423. ScopedLocalRef<jobject> activeNetwork(env, env->CallObjectMethod(connectivityManager, getActiveNetwork));
  424. if (!activeNetwork)
  425. return;
  426. jmethodID getLinkProperties = env->GetMethodID(connectivityManagerClass,
  427. "getLinkProperties", "(Landroid/net/Network;)Landroid/net/LinkProperties;");
  428. ERR_FAIL_NULL(getLinkProperties);
  429. ScopedLocalRef<jobject> linkProperties(env, env->CallObjectMethod(connectivityManager, getLinkProperties, activeNetwork.get()));
  430. if (!linkProperties)
  431. return;
  432. ScopedLocalRef<jclass> linkPropertiesClass(env, env->FindClass("android/net/LinkProperties"));
  433. ERR_FAIL_NULL(linkPropertiesClass);
  434. jmethodID getDnsServers = env->GetMethodID(linkPropertiesClass, "getDnsServers", "()Ljava/util/List;");
  435. ERR_FAIL_NULL(getDnsServers);
  436. ScopedLocalRef<jobject> dnsServers(env, env->CallObjectMethod(linkProperties, getDnsServers));
  437. if (!dnsServers)
  438. return;
  439. ScopedLocalRef<jclass> listClass(env, env->FindClass("java/util/List"));
  440. ERR_FAIL_NULL(listClass);
  441. jmethodID listSize = env->GetMethodID(listClass, "size", "()I");
  442. ERR_FAIL_NULL(listSize);
  443. int dnsServersCount = env->CallIntMethod(dnsServers, listSize);
  444. if (dnsServersCount > dns_servers_len)
  445. dnsServersCount = dns_servers_len;
  446. if (dnsServersCount <= 0)
  447. return;
  448. jmethodID listGet = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
  449. ERR_FAIL_NULL(listGet);
  450. ScopedLocalRef<jclass> inetAddressClass(env, env->FindClass("java/net/InetAddress"));
  451. ERR_FAIL_NULL(inetAddressClass);
  452. jmethodID getHostAddress = env->GetMethodID(inetAddressClass, "getHostAddress", "()Ljava/lang/String;");
  453. ERR_FAIL_NULL(getHostAddress);
  454. for (int i = 0; i < dnsServersCount; i++) {
  455. ScopedLocalRef<jobject> dnsServer(env, env->CallObjectMethod(dnsServers, listGet, (jint)i));
  456. if (!dnsServer)
  457. continue;
  458. ScopedLocalRef<jstring> hostAddress(env, (jstring)env->CallObjectMethod(dnsServer, getHostAddress));
  459. const char *host_address = env->GetStringUTFChars(hostAddress, 0);
  460. r_dns_servers[i] = strdup(host_address); // freed by the BCL
  461. (*dns_servers_count)++;
  462. env->ReleaseStringUTFChars(hostAddress, host_address);
  463. }
  464. // jesus...
  465. }
  466. GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array) {
  467. if (!r_dns_servers_array)
  468. return -1;
  469. *r_dns_servers_array = NULL;
  470. char *dns_servers[dns_servers_len];
  471. int dns_servers_count = 0;
  472. if (_monodroid_get_android_api_level() < 26) {
  473. // The 'net.dns*' system properties are no longer available in Android 8.0 (API level 26) and greater:
  474. // https://developer.android.com/about/versions/oreo/android-8.0-changes.html#o-pri
  475. char prop_name[] = "net.dns*";
  476. for (int i = 0; i < dns_servers_len; i++) {
  477. prop_name[7] = (char)(i + 0x31);
  478. char *prop_value;
  479. int32_t len = monodroid_get_system_property(prop_name, &prop_value);
  480. if (len > 0) {
  481. dns_servers[dns_servers_count] = strndup(prop_value, (size_t)len); // freed by the BCL
  482. dns_servers_count++;
  483. free(prop_value);
  484. }
  485. }
  486. } else {
  487. // Alternative for Oreo and greater
  488. interop_get_active_network_dns_servers(dns_servers, &dns_servers_count);
  489. }
  490. if (dns_servers_count > 0) {
  491. size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
  492. *r_dns_servers_array = malloc(ret_size); // freed by the BCL
  493. ERR_FAIL_NULL_V_MSG(*r_dns_servers_array, -1, "Out of memory.");
  494. memcpy(*r_dns_servers_array, dns_servers, ret_size);
  495. }
  496. return dns_servers_count;
  497. }
  498. GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
  499. // The JNI code is the equivalent of:
  500. //
  501. // TimeZone.getDefault().getID()
  502. JNIEnv *env = get_jni_env();
  503. ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
  504. ERR_FAIL_NULL_V(timeZoneClass, NULL);
  505. jmethodID getDefault = env->GetStaticMethodID(timeZoneClass, "getDefault", "()Ljava/util/TimeZone;");
  506. ERR_FAIL_NULL_V(getDefault, NULL);
  507. jmethodID getID = env->GetMethodID(timeZoneClass, "getID", "()Ljava/lang/String;");
  508. ERR_FAIL_NULL_V(getID, NULL);
  509. ScopedLocalRef<jobject> defaultTimeZone(env, env->CallStaticObjectMethod(timeZoneClass, getDefault));
  510. if (!defaultTimeZone)
  511. return NULL;
  512. ScopedLocalRef<jstring> defaultTimeZoneID(env, (jstring)env->CallObjectMethod(defaultTimeZone, getID));
  513. if (!defaultTimeZoneID)
  514. return NULL;
  515. const char *default_time_zone_id = env->GetStringUTFChars(defaultTimeZoneID, 0);
  516. char *result = strdup(default_time_zone_id); // freed by the BCL
  517. env->ReleaseStringUTFChars(defaultTimeZoneID, default_time_zone_id);
  518. return result;
  519. }
  520. GD_PINVOKE_EXPORT int32_t _monodroid_getifaddrs(struct ifaddrs **p_ifap) {
  521. return getifaddrs(p_ifap);
  522. }
  523. GD_PINVOKE_EXPORT void _monodroid_freeifaddrs(struct ifaddrs *p_ifap) {
  524. freeifaddrs(p_ifap);
  525. }
  526. #endif