You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Updates the SocketSubsystem to better support multiple protocol types and handling platforms that are locked to a specific protocol as well. Also removes the enum usage in the sockets/subsystem classes which reverts and better implements 4504445. #JIRA: UE-68869, UENET-911, UENET-891 #rb: ryan.gerleve [CL 4849963 by Jake Leonard in Dev-Networking branch]
228 lines
6.9 KiB
C++
228 lines
6.9 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SocketSubsystemAndroid.h"
|
|
#include "SocketSubsystemModule.h"
|
|
#include "IPAddress.h"
|
|
#include "Misc/Parse.h"
|
|
#include "Misc/CommandLine.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "BSDSockets/IPAddressBSD.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
|
|
FSocketSubsystemAndroid* FSocketSubsystemAndroid::SocketSingleton = NULL;
|
|
|
|
FName CreateSocketSubsystem( FSocketSubsystemModule& SocketSubsystemModule )
|
|
{
|
|
FName SubsystemName(TEXT("ANDROID"));
|
|
// Create and register our singleton factor with the main online subsystem for easy access
|
|
FSocketSubsystemAndroid* SocketSubsystem = FSocketSubsystemAndroid::Create();
|
|
FString Error;
|
|
if (SocketSubsystem->Init(Error))
|
|
{
|
|
SocketSubsystemModule.RegisterSocketSubsystem(SubsystemName, SocketSubsystem);
|
|
return SubsystemName;
|
|
}
|
|
else
|
|
{
|
|
FSocketSubsystemAndroid::Destroy();
|
|
return NAME_None;
|
|
}
|
|
}
|
|
|
|
void DestroySocketSubsystem( FSocketSubsystemModule& SocketSubsystemModule )
|
|
{
|
|
SocketSubsystemModule.UnregisterSocketSubsystem(FName(TEXT("ANDROID")));
|
|
FSocketSubsystemAndroid::Destroy();
|
|
}
|
|
|
|
/**
|
|
* Singleton interface for the Android socket subsystem
|
|
* @return the only instance of the Android socket subsystem
|
|
*/
|
|
FSocketSubsystemAndroid* FSocketSubsystemAndroid::Create()
|
|
{
|
|
if (SocketSingleton == NULL)
|
|
{
|
|
SocketSingleton = new FSocketSubsystemAndroid();
|
|
}
|
|
|
|
return SocketSingleton;
|
|
}
|
|
|
|
/**
|
|
* Destroy the singleton Android socket subsystem
|
|
*/
|
|
void FSocketSubsystemAndroid::Destroy()
|
|
{
|
|
if (SocketSingleton != NULL)
|
|
{
|
|
SocketSingleton->Shutdown();
|
|
delete SocketSingleton;
|
|
SocketSingleton = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Does Android platform initialization of the sockets library
|
|
*
|
|
* @param Error a string that is filled with error information
|
|
*
|
|
* @return TRUE if initialized ok, FALSE otherwise
|
|
*/
|
|
bool FSocketSubsystemAndroid::Init(FString& Error)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Performs Android specific socket clean up
|
|
*/
|
|
void FSocketSubsystemAndroid::Shutdown(void)
|
|
{
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Whether the device has a properly configured network device or not
|
|
*/
|
|
bool FSocketSubsystemAndroid::HasNetworkDevice()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return Label explicitly as Android as behavior is slightly different for BSD @refer GetLocalHostAddr
|
|
*/
|
|
const TCHAR* FSocketSubsystemAndroid::GetSocketAPIName() const
|
|
{
|
|
return TEXT("BSD_Android");
|
|
}
|
|
|
|
TSharedRef<FInternetAddr> FSocketSubsystemAndroid::GetLocalHostAddr(FOutputDevice& Out, bool& bCanBindAll)
|
|
{
|
|
// Get parent address first
|
|
TSharedRef<FInternetAddr> Addr = FSocketSubsystemBSD::GetLocalHostAddr(Out, bCanBindAll);
|
|
|
|
// If the address is not a loopback one (or none), return it.
|
|
// NOTE:
|
|
// Depreciated function gethostname() returns 'localhost' on (all?) Android devices
|
|
// Which in turn means that FSocketSubsystemBSD::GetLocalHostAddr() resolves to 127.0.0.1
|
|
// Getting info from android.net.wifi.WifiManager is a little messy due to UE4 modular architecture and JNI
|
|
// IPv4 code using ioctl(.., SIOCGIFCONF, ..) based on formally FSocketSubsytemLinux::GetLocalHostAddr works fine for now...
|
|
//
|
|
// Also NOTE: Network can flip out behind applications back when connectivity changes. eg. Move out of wifi range.
|
|
// This seems to recover OK between matches as subsystems are reinited each session Host/Join.
|
|
if (Addr->GetProtocolType() == FNetworkProtocolTypes::IPv4 && Addr->GetRawIp()[0] == 127)
|
|
{
|
|
return Addr;
|
|
}
|
|
|
|
int TempSocket = socket(PF_INET, SOCK_STREAM, 0);
|
|
if (TempSocket)
|
|
{
|
|
ifreq IfReqs[8];
|
|
|
|
ifconf IfConfig;
|
|
FMemory::Memzero(IfConfig);
|
|
IfConfig.ifc_req = IfReqs;
|
|
IfConfig.ifc_len = sizeof(IfReqs);
|
|
|
|
int Result = ioctl(TempSocket, SIOCGIFCONF, &IfConfig);
|
|
if (Result == 0)
|
|
{
|
|
// Temporary cache of the address we get per interface
|
|
sockaddr_storage TemporaryAddress;
|
|
// Interface specific address containers
|
|
sockaddr_storage WifiAddress;
|
|
sockaddr_storage CellularAddress;
|
|
sockaddr_storage OtherAddress;
|
|
|
|
// Clear these out
|
|
FMemory::Memzero(&WifiAddress, sizeof(sockaddr_storage));
|
|
FMemory::Memzero(&CellularAddress, sizeof(sockaddr_storage));
|
|
FMemory::Memzero(&OtherAddress, sizeof(sockaddr_storage));
|
|
|
|
for (int32 IdxReq = 0; IdxReq < ARRAY_COUNT(IfReqs); ++IdxReq)
|
|
{
|
|
// Cache the address information, as the following flag lookup will
|
|
// write into the ifr_addr field.
|
|
FMemory::Memcpy((void*)&TemporaryAddress, (void*)(&IfReqs[IdxReq].ifr_addr), sizeof(sockaddr_in));
|
|
|
|
// Examine interfaces that are up and not loop back
|
|
if (ioctl(TempSocket, SIOCGIFFLAGS, &IfReqs[IdxReq]) == 0 &&
|
|
(IfReqs[IdxReq].ifr_flags & IFF_UP) &&
|
|
(IfReqs[IdxReq].ifr_flags & IFF_LOOPBACK) == 0)
|
|
{
|
|
if (strcmp(IfReqs[IdxReq].ifr_name, "wlan0") == 0)
|
|
{
|
|
// 'Usually' wifi, Prefer wifi
|
|
FMemory::Memcpy((void*)&WifiAddress, (void*)(&TemporaryAddress), sizeof(sockaddr_in));
|
|
break;
|
|
}
|
|
else if (strcmp(IfReqs[IdxReq].ifr_name, "rmnet0") == 0)
|
|
{
|
|
// 'Usually' cellular
|
|
FMemory::Memcpy((void*)&CellularAddress, (void*)(&TemporaryAddress), sizeof(sockaddr_in));
|
|
}
|
|
else if (OtherAddress.ss_family == AF_UNSPEC)
|
|
{
|
|
// First alternate found
|
|
FMemory::Memcpy((void*)&OtherAddress, (void*)(&TemporaryAddress), sizeof(sockaddr_in));
|
|
}
|
|
}
|
|
}
|
|
|
|
TSharedRef<FInternetAddrBSD> NewAddrRef = StaticCastSharedRef<FInternetAddrBSD>(Addr);
|
|
// Prioritize results found
|
|
if (WifiAddress.ss_family != AF_UNSPEC)
|
|
{
|
|
// Prefer Wifi
|
|
NewAddrRef->SetIp(WifiAddress);
|
|
UE_LOG(LogSockets, Log, TEXT("(%s) Wifi Adapter IP %s"), GetSocketAPIName(), *Addr->ToString(false));
|
|
}
|
|
else if (CellularAddress.ss_family != AF_UNSPEC)
|
|
{
|
|
// Then cellular
|
|
NewAddrRef->SetIp(CellularAddress);
|
|
UE_LOG(LogSockets, Log, TEXT("(%s) Cellular Adapter IP %s"), GetSocketAPIName(), *Addr->ToString(false));
|
|
}
|
|
else if (OtherAddress.ss_family != AF_UNSPEC)
|
|
{
|
|
// Then whatever else was found
|
|
NewAddrRef->SetIp(OtherAddress);
|
|
UE_LOG(LogSockets, Log, TEXT("(%s) Adapter IP %s"), GetSocketAPIName(), *Addr->ToString(false));
|
|
}
|
|
else
|
|
{
|
|
// Give up
|
|
Addr->SetLoopbackAddress(); // 127.0.0.1
|
|
UE_LOG(LogSockets, Warning, TEXT("(%s) NO 'UP' ADAPTER FOUND! using: %s"), GetSocketAPIName(), *Addr->ToString(false));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int ErrNo = errno;
|
|
UE_LOG(LogSockets, Warning, TEXT("ioctl( ,SIOGCIFCONF, ) failed, errno=%d (%s)"), ErrNo, ANSI_TO_TCHAR(strerror(ErrNo)));
|
|
}
|
|
|
|
close(TempSocket);
|
|
}
|
|
|
|
return Addr;
|
|
}
|
|
|
|
/**
|
|
* Translates an ESocketAddressInfoFlags into a value usable by getaddrinfo
|
|
*/
|
|
int32 FSocketSubsystemAndroid::GetAddressInfoHintFlag(EAddressInfoFlags InFlags) const
|
|
{
|
|
// On Android, you cannot explicitly use AI_ALL and AI_V4MAPPED
|
|
// However, if GetAddressInfo is passed with no hint flags, the query will execute with
|
|
// AI_V4MAPPED automatically (flag can only be set by the kernel)
|
|
EAddressInfoFlags ModifiedInFlags = (InFlags & ~EAddressInfoFlags::AllResultsWithMapping);
|
|
return FSocketSubsystemBSD::GetAddressInfoHintFlag(ModifiedInFlags);
|
|
}
|