Implement GetCurrentIpConfigInfo

Needed for almost every LAN game
This commit is contained in:
Dima 2023-04-24 14:46:42 +03:00
parent 635f06bf50
commit 54041ceba1
6 changed files with 226 additions and 4 deletions

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature

View File

@ -64,7 +64,8 @@ namespace skyline {
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")},
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")} {
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")},
getDhcpInfoId{environ->GetMethodID(instanceClass, "getDhcpInfo", "()Landroid/net/DhcpInfo;")} {
env.Initialize(environ);
}
@ -132,6 +133,23 @@ namespace skyline {
return {static_cast<KeyboardCloseResult>(env->CallIntMethod(buttonInteger, getIntegerValueId)), input};
}
DhcpInfo JvmManager::GetDhcpInfo() {
jobject dhcpInfo{env->CallObjectMethod(instance, getDhcpInfoId)};
jclass dhcpInfoClass{env->GetObjectClass(dhcpInfo)};
jfieldID ipAddressFieldId{env->GetFieldID(dhcpInfoClass, "ipAddress", "I")};
jfieldID subnetFieldId{env->GetFieldID(dhcpInfoClass, "netmask", "I")};
jfieldID gatewayFieldId{env->GetFieldID(dhcpInfoClass, "gateway", "I")};
jfieldID dns1FieldId{env->GetFieldID(dhcpInfoClass, "dns1", "I")};
jfieldID dns2FieldId{env->GetFieldID(dhcpInfoClass, "dns2", "I")};
jint ipAddress{env->GetIntField(dhcpInfo, ipAddressFieldId)};
jint subnet{env->GetIntField(dhcpInfo, subnetFieldId)};
jint gateway{env->GetIntField(dhcpInfo, gatewayFieldId)};
jint dns1{env->GetIntField(dhcpInfo, dns1FieldId)};
jint dns2{env->GetIntField(dhcpInfo, dns2FieldId)};
return DhcpInfo{ipAddress, subnet, gateway, dns1, dns2};
}
void JvmManager::CloseKeyboard(jobject dialog) {
env->CallVoidMethod(instance, closeKeyboardId, dialog);
env->DeleteGlobalRef(dialog);

View File

@ -7,6 +7,14 @@
#include <jni.h>
namespace skyline {
struct DhcpInfo {
i32 ipAddress;
i32 subnet;
i32 gateway;
i32 dns1;
i32 dns2;
};
/**
* @brief A wrapper over std::string that supports construction using a JNI jstring
*/
@ -182,6 +190,11 @@ namespace skyline {
*/
i32 GetVersionCode();
/**
* @brief A call to EmulationActivity.getDhcpInfo in Kotlin
*/
DhcpInfo GetDhcpInfo();
private:
jmethodID initializeControllersId;
jmethodID vibrateDeviceId;
@ -191,6 +204,7 @@ namespace skyline {
jmethodID closeKeyboardId;
jmethodID showValidationResultId;
jmethodID getVersionCodeId;
jmethodID getDhcpInfoId;
jmethodID getIntegerValueId;
};

View File

@ -4,8 +4,22 @@
#include "IScanRequest.h"
#include "IRequest.h"
#include "IGeneralService.h"
#include <common/settings.h>
#include <jvm.h>
namespace skyline::service::nifm {
/**
* @brief Converts integer value to an array of bytes ordered in little-endian format
*/
static std::array<u8, 4> ConvertIntToByteArray(i32 value) {
std::array<u8, 4> result{};
result[0] = value & 0xFF;
result[1] = (value >> 8) & 0xFF;
result[2] = (value >> 16) & 0xFF;
result[3] = (value >> 24) & 0xFF;
return result;
}
IGeneralService::IGeneralService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IGeneralService::CreateScanRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
@ -18,13 +32,89 @@ namespace skyline::service::nifm {
return {};
}
Result IGeneralService::GetCurrentNetworkProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (!(*state.settings->isInternetEnabled))
return result::NoInternetConnection;
const UUID uuid{static_cast<u128>(0xdeadbeef) << 64};
auto dhcpInfo{state.jvm->GetDhcpInfo()};
SfNetworkProfileData networkProfileData{
.ipSettingData{
.ipAddressSetting{
true,
.currentAddress{ConvertIntToByteArray(dhcpInfo.ipAddress)},
.subnetMask{ConvertIntToByteArray(dhcpInfo.subnet)},
.gateway{ConvertIntToByteArray(dhcpInfo.gateway)},
},
.dnsSetting{
true,
.primaryDns{ConvertIntToByteArray(dhcpInfo.dns1)},
.secondaryDns{ConvertIntToByteArray(dhcpInfo.dns2)},
},
.proxySetting{
false,
.port{},
.proxyServer{},
.automaticAuthEnabled{},
.user{},
.password{},
},
1500,
},
.uuid{uuid},
.networkName{"Skyline Network"},
.wirelessSettingData{
12,
.ssid{"Skyline Network"},
.passphrase{"skylinepassword"},
},
};
request.outputBuf.at(0).as<SfNetworkProfileData>() = networkProfileData;
return {};
}
Result IGeneralService::GetCurrentIpAddress(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return result::NoInternetConnection;
if (!(*state.settings->isInternetEnabled))
return result::NoInternetConnection;
auto dhcpInfo{state.jvm->GetDhcpInfo()};
response.Push(ConvertIntToByteArray(dhcpInfo.ipAddress));
return {};
}
Result IGeneralService::GetCurrentIpConfigInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (!(*state.settings->isInternetEnabled))
return result::NoInternetConnection;
auto dhcpInfo{state.jvm->GetDhcpInfo()};
struct IpConfigInfo {
IpAddressSetting ipAddressSetting;
DnsSetting dnsSetting;
};
IpConfigInfo ipConfigInfo{
.ipAddressSetting{
true,
.currentAddress{ConvertIntToByteArray(dhcpInfo.ipAddress)},
.subnetMask{ConvertIntToByteArray(dhcpInfo.subnet)},
.gateway{ConvertIntToByteArray(dhcpInfo.gateway)},
},
.dnsSetting{
true,
.primaryDns{ConvertIntToByteArray(dhcpInfo.dns1)},
.secondaryDns{ConvertIntToByteArray(dhcpInfo.dns2)},
},
};
response.Push(ipConfigInfo);
return {};
}
Result IGeneralService::IsAnyInternetRequestAccepted(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
// We don't emulate networking so always return false
response.Push(false);
response.Push<u8>(*state.settings->isInternetEnabled);
return {};
}
}

View File

@ -4,11 +4,90 @@
#pragma once
#include <services/serviceman.h>
#include "common/uuid.h"
namespace skyline::service::nifm {
namespace result {
constexpr Result NoInternetConnection{110, 300};
}
struct IpAddressSetting {
bool isAutomatic{};
std::array<u8, 4> currentAddress{};
std::array<u8, 4> subnetMask{};
std::array<u8, 4> gateway{};
};
static_assert(sizeof(IpAddressSetting) == 0xD);
struct DnsSetting {
bool isAutomatic{};
std::array<u8, 4> primaryDns{};
std::array<u8, 4> secondaryDns{};
};
static_assert(sizeof(DnsSetting) == 0x9);
struct ProxySetting {
bool enabled{};
u8 _pad0_[0x1];
u16 port{};
std::array<char, 0x64> proxyServer{};
bool automaticAuthEnabled{};
std::array<char, 0x20> user{};
std::array<char, 0x20> password{};
u8 _pad1_[0x1];
};
static_assert(sizeof(ProxySetting) == 0xAA);
struct IpSettingData {
IpAddressSetting ipAddressSetting{};
DnsSetting dnsSetting{};
ProxySetting proxySetting{};
u16 mtu{};
};
static_assert(sizeof(IpSettingData) == 0xC2);
struct SfWirelessSettingData {
u8 ssidLength{};
std::array<char, 0x20> ssid{};
u8 _unk0_[0x3];
std::array<char, 0x41> passphrase{};
};
static_assert(sizeof(SfWirelessSettingData) == 0x65);
struct NifmWirelessSettingData {
u8 ssidLength{};
std::array<char, 0x21> ssid{};
u8 _unk0_[0x1];
u8 _pad0_[0x1];
u32 _unk1_[0x2];
std::array<char, 0x41> passphrase{};
u8 _pad1_[0x3];
};
static_assert(sizeof(NifmWirelessSettingData) == 0x70);
#pragma pack(push, 1)
struct SfNetworkProfileData {
IpSettingData ipSettingData{};
UUID uuid{};
std::array<char, 0x40> networkName{};
u8 _unk0_[0x4];
SfWirelessSettingData wirelessSettingData{};
u8 _pad0_[0x1];
};
static_assert(sizeof(SfNetworkProfileData) == 0x17C);
struct NifmNetworkProfileData {
UUID uuid{};
std::array<char, 0x40> networkName{};
u32 _unk0_[0x2];
u8 _unk1_[0x2];
u8 _pad0_[0x2];
NifmWirelessSettingData wirelessSettingData{};
IpSettingData ipSettingData{};
};
static_assert(sizeof(NifmNetworkProfileData) == 0x18E);
#pragma pack(pop)
/**
* @brief IGeneralService is used by applications to control the network connection
* @url https://switchbrew.org/wiki/Network_Interface_services#IGeneralService
@ -27,11 +106,21 @@ namespace skyline::service::nifm {
*/
Result CreateRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentNetworkProfile
*/
Result GetCurrentNetworkProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentIpAddress
*/
Result GetCurrentIpAddress(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @url https://switchbrew.org/wiki/Network_Interface_services#GetCurrentIpConfigInfo
*/
Result GetCurrentIpConfigInfo(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @url https://switchbrew.org/wiki/Network_Interface_services#IsAnyInternetRequestAccepted
*/
@ -40,7 +129,9 @@ namespace skyline::service::nifm {
SERVICE_DECL(
SFUNC(0x1, IGeneralService, CreateScanRequest),
SFUNC(0x4, IGeneralService, CreateRequest),
SFUNC(0x5, IGeneralService, GetCurrentNetworkProfile),
SFUNC(0xC, IGeneralService, GetCurrentIpAddress),
SFUNC(0xF, IGeneralService, GetCurrentIpConfigInfo),
SFUNC(0x15, IGeneralService, IsAnyInternetRequestAccepted)
)
};

View File

@ -18,6 +18,8 @@ import android.content.res.Configuration
import android.graphics.PointF
import android.graphics.drawable.Icon
import android.hardware.display.DisplayManager
import android.net.DhcpInfo
import android.net.wifi.WifiManager
import android.os.*
import android.util.Log
import android.util.Rational
@ -626,6 +628,12 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
return dialog.waitForSubmitOrCancel().let { arrayOf(if (it.cancelled) 1 else 0, it.text) }
}
@Suppress("unused")
fun getDhcpInfo() : DhcpInfo {
val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
return wifiManager.dhcpInfo
}
@Suppress("unused")
fun closeKeyboard(dialog : SoftwareKeyboardDialog) {
runOnUiThread { dialog.dismiss() }