Files
UnrealEngineUWP/Engine/Source/Developer/iOS/IOSTargetPlatform/Private/Mac/IOSDeviceHelperMac.cpp
Marc Audy 037d007078 Fix shadowed variables
[CL 2471506 by Marc Audy in Main branch]
2015-03-06 15:13:38 -05:00

394 lines
12 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved
#include "../IOSTargetPlatformPrivatePCH.h"
#include <CoreFoundation/CoreFoundation.h>
struct AMDeviceNotificationCallbackInformation
{
void *deviceHandle;
uint32_t msgType;
};
struct AFCCommConnection
{
};
#ifdef __cplusplus
extern "C" {
#endif
int AMDeviceConnect (void *device);
int AMDeviceValidatePairing (void *device);
int AMDeviceStartSession (void *device);
int AMDeviceStopSession (void *device);
int AMDeviceDisconnect (void *device);
int AMDeviceNotificationSubscribe(void*, int, int, int, void**);
int AMDeviceStartService(void*, CFStringRef, void**, int);
CFStringRef AMDeviceCopyValue(void*, int unknown, CFStringRef cfstring);
int AMDeviceSecureUpgradeApplication(void*, void*, CFURLRef, void*, void*, void*);
uint32 AFCConnectionOpen(void*, uint32, void**);
uint32 AFCConnectionClose(void*);
uint32 AFCDirectoryCreate(void*, const char*);
uint32 AFCFileRefOpen(void*, const char*, uint64, uint64*);
uint32 AFCFileRefClose(void*, uint64);
uint32 AFCFileRefWrite(void*, uint64, void*, uint32);
#ifdef __cplusplus
}
#endif
class AFC
{
public:
static int StartService(void* DeviceHandle, void** OutHandle)
{
int Result = AMDeviceStartService(DeviceHandle, CFSTR("com.apple.afc"), OutHandle, 0);
return Result;
}
static int ConnectionOpen(void* ServiceHandle, AFCCommConnection** OutConn)
{
void* Conn = NULL;
int result = AFCConnectionOpen(ServiceHandle, 0, &Conn);
*OutConn = (AFCCommConnection*)Conn;
return result;
}
static int ConnectionClose(AFCCommConnection* Connection)
{
return AFCConnectionClose(Connection);
}
static uint32 DirectoryCreate(AFCCommConnection* Connection, FString Dir)
{
const char* IPAPath = TCHAR_TO_UTF8(*Dir);
uint Result = AFCDirectoryCreate(Connection, IPAPath);
return Result;
}
static uint32 FileRefOpen(AFCCommConnection* Connection, FString Path, int mode, uint64* OutHandle)
{
const char* IPAPath = TCHAR_TO_UTF8(*Path);
uint Result = AFCFileRefOpen(Connection, IPAPath, mode, OutHandle);
return Result;
}
static uint32 FileRefClose(AFCCommConnection* Connection, uint64 Handle)
{
return AFCFileRefClose(Connection, Handle);
}
static uint32 FileRefWrite(AFCCommConnection* Connection, uint64 Handle, void* Data, uint32 Length)
{
return AFCFileRefWrite(Connection, Handle, Data, Length);
}
};
class InstallProxy
{
public:
static int StartService(void* DeviceHandle, void** OutHandle)
{
int Result = AMDeviceStartService(DeviceHandle, CFSTR("com.apple.mobile.installation_proxy"), OutHandle, 0);
return Result;
}
static int SecureUpgradeApplication(void* ServiceConnection, void* DeviceHandle, CFURLRef UrlPath, void* Options, void* InstallCallback, void* UserData)
{
return AMDeviceSecureUpgradeApplication(ServiceConnection, DeviceHandle, UrlPath, Options, InstallCallback, UserData);
}
};
class IOSDevice
{
public:
IOSDevice(void* device)
: DeviceHandle(device)
, AFCHandle(NULL)
, InstallHandle(NULL)
{
}
bool Connect()
{
bool Result = false;
int AppLogLevel = 7;
CFPreferencesSetAppValue(CFSTR("LogLevel"), CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &AppLogLevel), CFSTR("com.apple.MobileDevice"));
// connect to the device
int32 rc = AMDeviceConnect(DeviceHandle);
if (!rc)
{
// validate the pairing
rc = AMDeviceValidatePairing(DeviceHandle);
if (!rc)
{
// start a session
rc = AMDeviceStartSession(DeviceHandle);
Result = !rc;
}
else
{
UE_LOG(LogTemp, Display, TEXT("Couldn't validate pairing to device"));
}
}
else
{
UE_LOG(LogTemp, Display, TEXT("Couldn't connect to device"));
}
return Result;
}
bool Disconnect()
{
bool Result = false;
// close the session
int32 rc = AMDeviceStopSession(DeviceHandle);
if (!rc)
{
// disconnect from the device
rc = AMDeviceDisconnect(DeviceHandle);
Result = !rc;
}
else
{
UE_LOG(LogTemp, Display, TEXT("Couldn't stop session"));
}
return Result;
}
bool CopyFileToPublicStaging(const FString& SourceFile)
{
FString IpaFilename = FPaths::GetCleanFilename(SourceFile);
return CopyFileToDevice(SourceFile, FString(TEXT("/PublicStaging/")) + IpaFilename, 1024*1024);
}
bool TryUpgrade(const FString& IPAPath)
{
// reconnect to the device and start the Installation service
Connect();
InstallProxy::StartService(DeviceHandle, &InstallHandle);
CFURLRef UrlPath = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, FPlatformString::TCHARToCFString(*IPAPath), kCFURLWindowsPathStyle, 0);
int Result = InstallProxy::SecureUpgradeApplication(0, DeviceHandle, UrlPath, 0, 0, 0);
return Result == 0;
}
void* Handle() const
{
return DeviceHandle;
}
bool CreateDirectory(FString Dir)
{
return !(AFC::DirectoryCreate(AFCConnection, Dir) != 0);
}
private:
bool CopyFileToDevice(const FString& IPAPath, FString PathOnDevice, int PacketSize)
{
bool Result = false;
// reconnect to the device and start the AFC service
Connect();
if (!AFC::StartService(DeviceHandle, &AFCHandle))
{
if (!AFC::ConnectionOpen(AFCHandle, &AFCConnection))
{
// ensure the directory on the phone exists
FString DirectoryOnDevice = FPaths::GetPath(PathOnDevice);
CreateDirectory(DirectoryOnDevice + TEXT("/"));
// transfer the file
IPlatformFile& PlatformFile = IPlatformFile::GetPlatformPhysical();
IFileHandle* SourceFile = PlatformFile.OpenRead(*IPAPath);
if (SourceFile != NULL)
{
uint64 DestinationHandle = 0;
if (AFC::FileRefOpen(AFCConnection, PathOnDevice, 3, &DestinationHandle) == 0)
{
int TotalBytes = 0;
int PacketCount = SourceFile->Size() / PacketSize;
uint8* buffer = new uint8[PacketSize];
for (int Index = 0; Index < PacketCount; ++Index)
{
if (SourceFile->Read(buffer, PacketSize))
{
TotalBytes += PacketSize;
AFC::FileRefWrite(AFCConnection, DestinationHandle, buffer, PacketSize);
}
}
if (SourceFile->Read(buffer, SourceFile->Size() - TotalBytes))
{
AFC::FileRefWrite(AFCConnection, DestinationHandle, buffer, SourceFile->Size() - TotalBytes);
}
// flush the destination and close
AFC::FileRefClose(AFCConnection, DestinationHandle);
Result = true;
}
delete SourceFile;
}
// stop the AFC service and disconnect from the device
AFC::ConnectionClose(AFCConnection);
AFCConnection = NULL;
Disconnect();
}
}
return Result;
}
void* DeviceHandle;
void* AFCHandle;
AFCCommConnection* AFCConnection;
void* InstallHandle;
};
/* FIOSDeviceHelper structors
*****************************************************************************/
static TMap<IOSDevice*, FIOSLaunchDaemonPong> ConnectedDevices;
void FIOSDeviceHelper::Initialize()
{
static bool bIsInitialized = false;
if (!bIsInitialized)
{
void *subscribe;
int32 rc = AMDeviceNotificationSubscribe((void*)FIOSDeviceHelper::DeviceCallback, 0, 0, 0, &subscribe);
if (rc < 0)
{
//@todo right out to the log that we can't subscribe
}
bIsInitialized = true;
}
}
void FIOSDeviceHelper::DeviceCallback(void* CallbackInfo)
{
struct AMDeviceNotificationCallbackInformation* cbi = (AMDeviceNotificationCallbackInformation*)CallbackInfo;
void* deviceHandle = cbi->deviceHandle;
switch(cbi->msgType)
{
case 1:
FIOSDeviceHelper::DoDeviceConnect(deviceHandle);
break;
case 2:
FIOSDeviceHelper::DoDeviceDisconnect(deviceHandle);
break;
case 3:
break;
}
}
void FIOSDeviceHelper::DoDeviceConnect(void* deviceHandle)
{
// connect to the device
IOSDevice* Device = new IOSDevice(deviceHandle);
bool Connected = Device->Connect();
if (Connected)
{
// get the needed data
CFStringRef deviceName = AMDeviceCopyValue(deviceHandle, 0, CFSTR("DeviceName"));
CFStringRef deviceId = AMDeviceCopyValue(deviceHandle, 0, CFSTR("UniqueDeviceID"));
// fire the event
TCHAR idBuffer[128];
TCHAR nameBuffer[256];
FPlatformString::CFStringToTCHAR(deviceId, idBuffer);
FPlatformString::CFStringToTCHAR(deviceName, nameBuffer);
FIOSLaunchDaemonPong Event;
Event.DeviceID = FString::Printf(TEXT("IOS@%s"), idBuffer);
Event.DeviceName = FString::Printf(TEXT("%s"), nameBuffer);
Event.bCanReboot = false;
Event.bCanPowerOn = false;
Event.bCanPowerOff = false;
Event.DeviceName = nameBuffer;
FIOSDeviceHelper::OnDeviceConnected().Broadcast(Event);
// add to the device list
ConnectedDevices.Add(Device, Event);
// disconnect the device for now
Device->Disconnect();
}
else
{
UE_LOG(LogTemp, Display, TEXT("Couldn't connect to device"));
}
}
void FIOSDeviceHelper::DoDeviceDisconnect(void* deviceHandle)
{
IOSDevice* device = NULL;
for (auto DeviceIterator = ConnectedDevices.CreateIterator(); DeviceIterator; ++DeviceIterator)
{
if (DeviceIterator.Key()->Handle() == deviceHandle)
{
device = DeviceIterator.Key();
break;
}
}
if (device != NULL)
{
// extract the device id from the connected list¯
FIOSLaunchDaemonPong Event = ConnectedDevices.FindAndRemoveChecked(device);
// fire the event
FIOSDeviceHelper::OnDeviceDisconnected().Broadcast(Event);
// delete the device
delete device;
}
}
bool FIOSDeviceHelper::InstallIPAOnDevice(const FTargetDeviceId& DeviceId, const FString& IPAPath)
{
// check for valid path
if (IPAPath.Len() == 0)
{
return false;
}
// check for valid device
IOSDevice* device = NULL;
FIOSLaunchDaemonPong DeviceMessage;
for (auto DeviceIterator = ConnectedDevices.CreateIterator(); DeviceIterator; ++DeviceIterator)
{
DeviceMessage = DeviceIterator.Value();
if (DeviceMessage.DeviceID == DeviceId.ToString())
{
device = DeviceIterator.Key();
break;
}
}
if (device == NULL)
{
return false;
}
// we have the device and a IPA path
// copy to the stage
if (device->CopyFileToPublicStaging(IPAPath))
{
// install on the device
return device->TryUpgrade(IPAPath);
}
return false;
}
void FIOSDeviceHelper::EnableDeviceCheck(bool OnOff)
{
}