You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Moved the blocking portions (adb calls) of Android device detection off to a thread.
- This happens in a new module that the android target platforms depend on so that there aren't 6 different detection threads spun up. - Thread isn't created if android sdk isn't found. [CL 2038329 by JJ Hoesing in Main branch]
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
/*=============================================================================
|
||||
AndroidDeviceDetectionModule.cpp: Implements the FAndroidDeviceDetectionModule class.
|
||||
=============================================================================*/
|
||||
|
||||
#include "AndroidDeviceDetectionPrivatePCH.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAndroidDeviceDetectionModule"
|
||||
|
||||
|
||||
class FAndroidDeviceDetectionRunnable : public FRunnable
|
||||
{
|
||||
public:
|
||||
FAndroidDeviceDetectionRunnable(const FString& InADBPath, TMap<FString,FAndroidDeviceInfo>& InDeviceMap, FCriticalSection* InDeviceMapLock) :
|
||||
ADBPath(InADBPath),
|
||||
StopTaskCounter(0),
|
||||
DeviceMap(InDeviceMap),
|
||||
DeviceMapLock(InDeviceMapLock)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// FRunnable interface.
|
||||
virtual bool Init(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Stop(void)
|
||||
{
|
||||
StopTaskCounter.Increment();
|
||||
}
|
||||
|
||||
virtual uint32 Run(void)
|
||||
{
|
||||
int LoopCount = 1;
|
||||
|
||||
while (StopTaskCounter.GetValue() == 0)
|
||||
{
|
||||
// query every 10 seconds
|
||||
if (LoopCount++ >= 10)
|
||||
{
|
||||
QueryConnectedDevices();
|
||||
LoopCount = 0;
|
||||
}
|
||||
|
||||
FPlatformProcess::Sleep(1.0f);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool ExecuteAdbCommand( const FString& CommandLine, FString* OutStdOut, FString* OutStdErr ) const
|
||||
{
|
||||
// execute the command
|
||||
int32 ReturnCode;
|
||||
FString DefaultError;
|
||||
|
||||
// make sure there's a place for error output to go if the caller specified nullptr
|
||||
if (!OutStdErr)
|
||||
{
|
||||
OutStdErr = &DefaultError;
|
||||
}
|
||||
|
||||
FPlatformProcess::ExecProcess(*ADBPath, *CommandLine, &ReturnCode, OutStdOut, OutStdErr);
|
||||
|
||||
if (ReturnCode != 0)
|
||||
{
|
||||
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("The Android SDK command '%s' failed to run. Return code: %d, Error: %s\n"), *CommandLine, ReturnCode, **OutStdErr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QueryConnectedDevices()
|
||||
{
|
||||
// grab the list of devices via adb
|
||||
FString StdOut;
|
||||
if (!ExecuteAdbCommand(TEXT("devices -l"), &StdOut, nullptr))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// separate out each line
|
||||
TArray<FString> DeviceStrings;
|
||||
StdOut = StdOut.Replace(TEXT("\r"), TEXT("\n"));
|
||||
StdOut.ParseIntoArray(&DeviceStrings, TEXT("\n"), true);
|
||||
|
||||
// a list containing all devices found this time, so we can remove anything not in this list
|
||||
TArray<FString> CurrentlyConnectedDevices;
|
||||
|
||||
for (int32 StringIndex = 0; StringIndex < DeviceStrings.Num(); ++StringIndex)
|
||||
{
|
||||
const FString& DeviceString = DeviceStrings[StringIndex];
|
||||
|
||||
// skip over non-device lines
|
||||
if (DeviceString.StartsWith("* ") || DeviceString.StartsWith("List "))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// grab the device serial number
|
||||
int32 TabIndex;
|
||||
|
||||
if (!DeviceString.FindChar(TCHAR(' '), TabIndex))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FString SerialNumber = DeviceString.Left(TabIndex);
|
||||
|
||||
// add it to our list of currently connected devices
|
||||
CurrentlyConnectedDevices.Add(SerialNumber);
|
||||
|
||||
// move on to next device if this one is already a known device
|
||||
if (DeviceMap.Contains(SerialNumber))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the GL extensions string (and a bunch of other stuff)
|
||||
FString GLESExtensions;
|
||||
|
||||
FString ExtensionsCommand = FString::Printf(TEXT("-s %s shell dumpsys SurfaceFlinger"), *SerialNumber);
|
||||
if (!ExecuteAdbCommand(*ExtensionsCommand, &GLESExtensions, nullptr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// grab the GL ES version
|
||||
FString GLESVersionString;
|
||||
|
||||
FString VersionCommand = FString::Printf(TEXT("-s %s shell getprop ro.opengles.version"), *SerialNumber);
|
||||
if (!ExecuteAdbCommand(*VersionCommand, &GLESVersionString, nullptr))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int GLESVersion = FCString::Atoi(*GLESVersionString);
|
||||
|
||||
// parse the device model
|
||||
FString Model;
|
||||
FParse::Value(*DeviceString, TEXT("model:"), Model);
|
||||
|
||||
// parse the device model
|
||||
FString DeviceName;
|
||||
FParse::Value(*DeviceString, TEXT("device:"), DeviceName);
|
||||
|
||||
// add the device to the map
|
||||
{
|
||||
FScopeLock ScopeLock(DeviceMapLock);
|
||||
|
||||
FAndroidDeviceInfo& DeviceInfo = DeviceMap.Add(SerialNumber);
|
||||
DeviceInfo.SerialNumber = SerialNumber;
|
||||
DeviceInfo.Model = Model;
|
||||
DeviceInfo.DeviceName = DeviceName;
|
||||
DeviceInfo.GLESExtenstions = GLESExtensions;
|
||||
DeviceInfo.GLESVersion = GLESVersion;
|
||||
}
|
||||
}
|
||||
|
||||
// loop through the previously connected devices list and remove any that aren't still connected from the updated DeviceMap
|
||||
TArray<FString> DevicesToRemove;
|
||||
|
||||
for (auto It = DeviceMap.CreateConstIterator(); It; ++It)
|
||||
{
|
||||
if (!CurrentlyConnectedDevices.Contains(It.Key()))
|
||||
{
|
||||
DevicesToRemove.Add(It.Key());
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// enter the critical section and remove the devices from the map
|
||||
FScopeLock ScopeLock(DeviceMapLock);
|
||||
|
||||
for (auto It = DevicesToRemove.CreateConstIterator(); It; ++It)
|
||||
{
|
||||
DeviceMap.Remove(*It);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// path to the adb command
|
||||
FString ADBPath;
|
||||
|
||||
// > 0 if we've been asked to abort work in progress at the next opportunity
|
||||
FThreadSafeCounter StopTaskCounter;
|
||||
|
||||
TMap<FString,FAndroidDeviceInfo>& DeviceMap;
|
||||
FCriticalSection* DeviceMapLock;
|
||||
};
|
||||
|
||||
class FAndroidDeviceDetection : public IAndroidDeviceDetection
|
||||
{
|
||||
public:
|
||||
|
||||
FAndroidDeviceDetection() :
|
||||
DetectionThread(nullptr),
|
||||
DetectionThreadRunnable(nullptr)
|
||||
{
|
||||
// get the SDK binaries folder
|
||||
TCHAR AndroidDirectory[32768] = { 0 };
|
||||
FPlatformMisc::GetEnvironmentVariable(TEXT("ANDROID_HOME"), AndroidDirectory, 32768);
|
||||
|
||||
if (AndroidDirectory[0] == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FString ADBPath = FString::Printf(TEXT("%s\\platform-tools\\adb.exe"), AndroidDirectory);
|
||||
|
||||
// if it doesn't exist, no SDK installed, so don't bother creating a thread to look for devices
|
||||
if (!FPaths::FileExists(*ADBPath))
|
||||
{
|
||||
ADBPath.Empty();
|
||||
return;
|
||||
}
|
||||
|
||||
// create and fire off our device detection thread
|
||||
DetectionThreadRunnable = new FAndroidDeviceDetectionRunnable(ADBPath, DeviceMap, &DeviceMapLock);
|
||||
DetectionThread = FRunnableThread::Create(DetectionThreadRunnable, TEXT("FAndroidDeviceDetectionRunnable"), false);
|
||||
}
|
||||
|
||||
virtual ~FAndroidDeviceDetection()
|
||||
{
|
||||
if (DetectionThreadRunnable && DetectionThread)
|
||||
{
|
||||
DetectionThreadRunnable->Stop();
|
||||
DetectionThread->WaitForCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
virtual const TMap<FString,FAndroidDeviceInfo>& GetDeviceMap() OVERRIDE
|
||||
{
|
||||
return DeviceMap;
|
||||
}
|
||||
|
||||
virtual FCriticalSection* GetDeviceMapLock() OVERRIDE
|
||||
{
|
||||
return &DeviceMapLock;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
FRunnableThread* DetectionThread;
|
||||
FAndroidDeviceDetectionRunnable* DetectionThreadRunnable;
|
||||
|
||||
TMap<FString,FAndroidDeviceInfo> DeviceMap;
|
||||
FCriticalSection DeviceMapLock;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Holds the target platform singleton.
|
||||
*/
|
||||
static FAndroidDeviceDetection* AndroidDeviceDetectionSingleton = nullptr;
|
||||
|
||||
|
||||
/**
|
||||
* Module for detecting android devices.
|
||||
*/
|
||||
class FAndroidDeviceDetectionModule : public IAndroidDeviceDetectionModule
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~FAndroidDeviceDetectionModule( )
|
||||
{
|
||||
if (AndroidDeviceDetectionSingleton != nullptr)
|
||||
{
|
||||
delete AndroidDeviceDetectionSingleton;
|
||||
}
|
||||
|
||||
AndroidDeviceDetectionSingleton = nullptr;
|
||||
}
|
||||
|
||||
virtual IAndroidDeviceDetection* GetAndroidDeviceDetection() OVERRIDE
|
||||
{
|
||||
if (AndroidDeviceDetectionSingleton == nullptr)
|
||||
{
|
||||
AndroidDeviceDetectionSingleton = new FAndroidDeviceDetection();
|
||||
}
|
||||
|
||||
return AndroidDeviceDetectionSingleton;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
|
||||
IMPLEMENT_MODULE( FAndroidDeviceDetectionModule, AndroidDeviceDetection);
|
||||
Reference in New Issue
Block a user