diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml index 56d6eadfbf69..4975056c57f6 100644 --- a/Source/Android/app/src/main/AndroidManifest.xml +++ b/Source/Android/app/src/main/AndroidManifest.xml @@ -19,6 +19,7 @@ + diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/NetworkHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/NetworkHelper.java new file mode 100644 index 000000000000..b7c245c1036b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/NetworkHelper.java @@ -0,0 +1,109 @@ +package org.dolphinemu.dolphinemu.utils; + +import android.app.Service; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkAddress; +import android.net.RouteInfo; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import org.dolphinemu.dolphinemu.NativeLibrary; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; + +public class NetworkHelper +{ + private static ConnectivityManager GetConnectivityManager() + { + Context context = NativeLibrary.getEmulationActivity(); + if (context == null) + { + Log.warning("Cannot get Network IP address as EmulationActivity is null."); + return null; + } + ConnectivityManager manager = + (ConnectivityManager) context.getSystemService(Service.CONNECTIVITY_SERVICE); + if (manager == null) + Log.warning("Cannot get Network link as ConnectivityManager is null."); + return manager; + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private static LinkAddress GetIPv4Link() + { + ConnectivityManager manager = GetConnectivityManager(); + if (manager == null) + return null; + List links = + manager.getLinkProperties(manager.getActiveNetwork()).getLinkAddresses(); + for (LinkAddress link : links) + { + InetAddress address = link.getAddress(); + if (address instanceof Inet4Address) + return link; + } + Log.warning("No IPv4 link found."); + return null; + } + + private static int InetAddressToInt(InetAddress address) + { + byte[] net_addr = address.getAddress(); + int result = 0; + // Convert address to little endian + for (int i = 0; i < net_addr.length; i++) + { + result |= (net_addr[i] & 0xFF) << (8 * i); + } + return result; + } + + public static int GetNetworkIpAddress() + { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) + return 0; + LinkAddress link = GetIPv4Link(); + if (link == null) + return 0; + return InetAddressToInt(link.getAddress()); + } + + public static int GetNetworkPrefixLength() + { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) + return 0; + LinkAddress link = GetIPv4Link(); + if (link == null) + return 0; + return link.getPrefixLength(); + } + + public static int GetNetworkGateway() + { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) + return 0; + ConnectivityManager manager = GetConnectivityManager(); + if (manager == null) + return 0; + List routes = manager.getLinkProperties(manager.getActiveNetwork()).getRoutes(); + try + { + InetAddress addr_out = InetAddress.getByName("8.8.8.8"); + for (RouteInfo route : routes) + { + if (!route.matches(addr_out)) + continue; + return InetAddressToInt(route.getGateway()); + } + } + catch (UnknownHostException ignore) + { + } + return 0; + } +} diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp index 73405b6d8e60..887ae4deab09 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.cpp +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.cpp @@ -65,3 +65,24 @@ bool DeleteAndroidContent(const std::string& uri) return env->CallStaticBooleanMethod(IDCache::GetContentHandlerClass(), IDCache::GetContentHandlerDelete(), ToJString(env, uri)); } + +int GetNetworkIpAddress() +{ + JNIEnv* env = IDCache::GetEnvForThread(); + return env->CallStaticIntMethod(IDCache::GetNetworkHelperClass(), + IDCache::GetNetworkHelperGetNetworkIpAddress()); +} + +int GetNetworkPrefixLength() +{ + JNIEnv* env = IDCache::GetEnvForThread(); + return env->CallStaticIntMethod(IDCache::GetNetworkHelperClass(), + IDCache::GetNetworkHelperGetNetworkPrefixLength()); +} + +int GetNetworkGateway() +{ + JNIEnv* env = IDCache::GetEnvForThread(); + return env->CallStaticIntMethod(IDCache::GetNetworkHelperClass(), + IDCache::GetNetworkHelperGetNetworkGateway()); +} diff --git a/Source/Android/jni/AndroidCommon/AndroidCommon.h b/Source/Android/jni/AndroidCommon/AndroidCommon.h index ca8245182d0b..2ab220eac735 100644 --- a/Source/Android/jni/AndroidCommon/AndroidCommon.h +++ b/Source/Android/jni/AndroidCommon/AndroidCommon.h @@ -14,3 +14,6 @@ std::vector JStringArrayToVector(JNIEnv* env, jobjectArray array); int OpenAndroidContent(const std::string& uri, const std::string& mode); bool DeleteAndroidContent(const std::string& uri); +int GetNetworkIpAddress(); +int GetNetworkPrefixLength(); +int GetNetworkGateway(); diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 308fda1f0465..dc642e3efd54 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -43,6 +43,11 @@ static jclass s_content_handler_class; static jmethodID s_content_handler_open_fd; static jmethodID s_content_handler_delete; +static jclass s_network_helper_class; +static jmethodID s_network_helper_get_network_ip_address; +static jmethodID s_network_helper_get_network_prefix_length; +static jmethodID s_network_helper_get_network_gateway; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -193,6 +198,25 @@ jmethodID GetContentHandlerDelete() return s_content_handler_delete; } +jclass GetNetworkHelperClass() +{ + return s_network_helper_class; +} + +jmethodID GetNetworkHelperGetNetworkIpAddress() +{ + return s_network_helper_get_network_ip_address; +} + +jmethodID GetNetworkHelperGetNetworkPrefixLength() +{ + return s_network_helper_get_network_prefix_length; +} + +jmethodID GetNetworkHelperGetNetworkGateway() +{ + return s_network_helper_get_network_gateway; +} } // namespace IDCache #ifdef __cplusplus @@ -268,6 +292,16 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) s_content_handler_delete = env->GetStaticMethodID(s_content_handler_class, "delete", "(Ljava/lang/String;)Z"); + const jclass network_helper_class = + env->FindClass("org/dolphinemu/dolphinemu/utils/NetworkHelper"); + s_network_helper_class = reinterpret_cast(env->NewGlobalRef(network_helper_class)); + s_network_helper_get_network_ip_address = + env->GetStaticMethodID(s_network_helper_class, "GetNetworkIpAddress", "()I"); + s_network_helper_get_network_prefix_length = + env->GetStaticMethodID(s_network_helper_class, "GetNetworkPrefixLength", "()I"); + s_network_helper_get_network_gateway = + env->GetStaticMethodID(s_network_helper_class, "GetNetworkGateway", "()I"); + return JNI_VERSION; } @@ -286,6 +320,7 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_ini_file_section_class); env->DeleteGlobalRef(s_compress_cb_class); env->DeleteGlobalRef(s_content_handler_class); + env->DeleteGlobalRef(s_network_helper_class); } #ifdef __cplusplus diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 8fd43ea41d4d..32e8fa4e0349 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -45,4 +45,9 @@ jclass GetContentHandlerClass(); jmethodID GetContentHandlerOpenFd(); jmethodID GetContentHandlerDelete(); +jclass GetNetworkHelperClass(); +jmethodID GetNetworkHelperGetNetworkIpAddress(); +jmethodID GetNetworkHelperGetNetworkPrefixLength(); +jmethodID GetNetworkHelperGetNetworkGateway(); + } // namespace IDCache diff --git a/Source/Core/Core/IOS/Network/IP/Top.cpp b/Source/Core/Core/IOS/Network/IP/Top.cpp index 53fd9b26998f..1ad11fdc09a9 100644 --- a/Source/Core/Core/IOS/Network/IP/Top.cpp +++ b/Source/Core/Core/IOS/Network/IP/Top.cpp @@ -50,6 +50,11 @@ #include #endif +#ifdef __ANDROID__ +#include +#include "jni/AndroidCommon/AndroidCommon.h" +#endif + namespace IOS::HLE::Device { enum SOResultCode : s32 @@ -254,8 +259,21 @@ static std::optional GetSystemDefaultInterface() get_addr(iface->ifa_broadaddr)}; } } +#else + u32 addr = 0; + u32 netmask = 0; + u32 gateway = 0; + // The thread prevent a stack corruption from JVM->AttachCurrentThread + std::thread([&] { + addr = GetNetworkIpAddress(); + const u32 prefix_length = GetNetworkPrefixLength(); + netmask = (1 << prefix_length) - 1; + gateway = GetNetworkGateway(); + }).join(); + if (addr || netmask || gateway) + return DefaultInterface{addr, netmask, gateway}; #endif - return {}; + return std::nullopt; } static DefaultInterface GetSystemDefaultInterfaceOrFallback()