summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/SystemInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/SystemInfo.cpp')
-rw-r--r--xbmc/utils/SystemInfo.cpp1469
1 files changed, 1469 insertions, 0 deletions
diff --git a/xbmc/utils/SystemInfo.cpp b/xbmc/utils/SystemInfo.cpp
new file mode 100644
index 0000000..e85c415
--- /dev/null
+++ b/xbmc/utils/SystemInfo.cpp
@@ -0,0 +1,1469 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include <limits.h>
+
+#include "SystemInfo.h"
+#ifndef TARGET_POSIX
+#include <conio.h>
+#else
+#include <sys/utsname.h>
+#endif
+#include "CompileInfo.h"
+#include "ServiceBroker.h"
+#include "filesystem/CurlFile.h"
+#include "filesystem/File.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/guiinfo/GUIInfoLabels.h"
+#include "network/Network.h"
+#include "platform/Filesystem.h"
+#include "rendering/RenderSystem.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/CPUInfo.h"
+#include "utils/log.h"
+
+#ifdef TARGET_WINDOWS
+#include <dwmapi.h>
+#include "utils/CharsetConverter.h"
+#include <VersionHelpers.h>
+
+#ifdef TARGET_WINDOWS_STORE
+#include <winrt/Windows.Security.ExchangeActiveSyncProvisioning.h>
+#include <winrt/Windows.System.Profile.h>
+
+using namespace winrt::Windows::ApplicationModel;
+using namespace winrt::Windows::Security::ExchangeActiveSyncProvisioning;
+using namespace winrt::Windows::System;
+using namespace winrt::Windows::System::Profile;
+#endif
+#include <wincrypt.h>
+#include "platform/win32/CharsetConverter.h"
+#endif
+#if defined(TARGET_DARWIN)
+#include "platform/darwin/DarwinUtils.h"
+#endif
+#include "powermanagement/PowerManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XMLUtils.h"
+#if defined(TARGET_ANDROID)
+#include <androidjni/Build.h>
+#include <androidjni/Context.h>
+#include <androidjni/PackageManager.h>
+#endif
+
+/* Platform identification */
+#if defined(TARGET_DARWIN)
+#include <Availability.h>
+#include <mach-o/arch.h>
+#include <sys/sysctl.h>
+#elif defined(TARGET_ANDROID)
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+#elif defined(TARGET_FREEBSD)
+#include <sys/param.h>
+#elif defined(TARGET_LINUX)
+#include "platform/linux/SysfsPath.h"
+
+#include <linux/version.h>
+#endif
+
+#include <system_error>
+
+/* Expand macro before stringify */
+#define STR_MACRO(x) #x
+#define XSTR_MACRO(x) STR_MACRO(x)
+
+namespace
+{
+auto startTime = std::chrono::steady_clock::now();
+}
+
+using namespace XFILE;
+
+#ifdef TARGET_WINDOWS_DESKTOP
+static bool sysGetVersionExWByRef(OSVERSIONINFOEXW& osVerInfo)
+{
+ osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
+
+ typedef NTSTATUS(__stdcall *RtlGetVersionPtr)(RTL_OSVERSIONINFOEXW* pOsInfo);
+ static HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
+ if (hNtDll != NULL)
+ {
+ static RtlGetVersionPtr RtlGetVer = (RtlGetVersionPtr) GetProcAddress(hNtDll, "RtlGetVersion");
+ if (RtlGetVer && RtlGetVer(&osVerInfo) == 0)
+ return true;
+ }
+ // failed to get OS information directly from ntdll.dll
+ // use GetVersionExW() as fallback
+ // note: starting from Windows 8.1 GetVersionExW() may return unfaithful information
+ if (GetVersionExW((OSVERSIONINFOW*) &osVerInfo) != 0)
+ return true;
+
+ ZeroMemory(&osVerInfo, sizeof(osVerInfo));
+ return false;
+}
+
+static bool appendWindows10NameVersion(std::string& osNameVer)
+{
+ wchar_t versionW[32] = {};
+ DWORD len = sizeof(versionW);
+ bool obtained = false;
+ if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"DisplayVersion",
+ RRF_RT_REG_SZ, nullptr, &versionW, &len))
+ {
+ obtained = true;
+ }
+ else if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"ReleaseId",
+ RRF_RT_REG_SZ, nullptr, &versionW, &len))
+ {
+ obtained = true;
+ }
+ if (obtained)
+ osNameVer.append(StringUtils::Format(" {}", KODI::PLATFORM::WINDOWS::FromW(versionW)));
+
+ return obtained;
+}
+#endif // TARGET_WINDOWS_DESKTOP
+
+#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
+static std::string getValueFromOs_release(std::string key)
+{
+ FILE* os_rel = fopen("/etc/os-release", "r");
+ if (!os_rel)
+ return "";
+
+ char* buf = new char[10 * 1024]; // more than enough
+ size_t len = fread(buf, 1, 10 * 1024, os_rel);
+ fclose(os_rel);
+ if (len == 0)
+ {
+ delete[] buf;
+ return "";
+ }
+
+ std::string content(buf, len);
+ delete[] buf;
+
+ // find begin of value string
+ size_t valStart = 0, seachPos;
+ key += '=';
+ if (content.compare(0, key.length(), key) == 0)
+ valStart = key.length();
+ else
+ {
+ key = "\n" + key;
+ seachPos = 0;
+ do
+ {
+ seachPos = content.find(key, seachPos);
+ if (seachPos == std::string::npos)
+ return "";
+ if (seachPos == 0 || content[seachPos - 1] != '\\')
+ valStart = seachPos + key.length();
+ else
+ seachPos++;
+ } while (valStart == 0);
+ }
+
+ if (content[valStart] == '\n')
+ return "";
+
+ // find end of value string
+ seachPos = valStart;
+ do
+ {
+ seachPos = content.find('\n', seachPos + 1);
+ } while (seachPos != std::string::npos && content[seachPos - 1] == '\\');
+ size_t const valEnd = seachPos;
+
+ std::string value(content, valStart, valEnd - valStart);
+ if (value.empty())
+ return value;
+
+ // remove quotes
+ if (value[0] == '\'' || value[0] == '"')
+ {
+ if (value.length() < 2)
+ return value;
+ size_t qEnd = value.rfind(value[0]);
+ if (qEnd != std::string::npos)
+ {
+ value.erase(qEnd);
+ value.erase(0, 1);
+ }
+ }
+
+ // unescape characters
+ for (size_t slashPos = value.find('\\'); slashPos < value.length() - 1; slashPos = value.find('\\', slashPos))
+ {
+ if (value[slashPos + 1] == '\n')
+ value.erase(slashPos, 2);
+ else
+ {
+ value.erase(slashPos, 1);
+ slashPos++; // skip unescaped character
+ }
+ }
+
+ return value;
+}
+
+enum lsb_rel_info_type
+{
+ lsb_rel_distributor,
+ lsb_rel_description,
+ lsb_rel_release,
+ lsb_rel_codename
+};
+
+static std::string getValueFromLsb_release(enum lsb_rel_info_type infoType)
+{
+ std::string key, command("unset PYTHONHOME; unset PYTHONPATH; lsb_release ");
+ switch (infoType)
+ {
+ case lsb_rel_distributor:
+ command += "-i";
+ key = "Distributor ID:\t";
+ break;
+ case lsb_rel_description:
+ command += "-d";
+ key = "Description:\t";
+ break;
+ case lsb_rel_release:
+ command += "-r";
+ key = "Release:\t";
+ break;
+ case lsb_rel_codename:
+ command += "-c";
+ key = "Codename:\t";
+ break;
+ default:
+ return "";
+ }
+ command += " 2>/dev/null";
+ FILE* lsb_rel = popen(command.c_str(), "r");
+ if (lsb_rel == NULL)
+ return "";
+
+ char buf[300]; // more than enough
+ if (fgets(buf, 300, lsb_rel) == NULL)
+ {
+ pclose(lsb_rel);
+ return "";
+ }
+ pclose(lsb_rel);
+
+ std::string response(buf);
+ if (response.compare(0, key.length(), key) != 0)
+ return "";
+
+ return response.substr(key.length(), response.find('\n') - key.length());
+}
+#endif // TARGET_LINUX && !TARGET_ANDROID
+
+CSysInfo g_sysinfo;
+
+CSysInfoJob::CSysInfoJob() = default;
+
+bool CSysInfoJob::DoWork()
+{
+ m_info.systemUptime = GetSystemUpTime(false);
+ m_info.systemTotalUptime = GetSystemUpTime(true);
+ m_info.internetState = GetInternetState();
+ m_info.videoEncoder = GetVideoEncoder();
+ m_info.cpuFrequency =
+ StringUtils::Format("{:4.0f} MHz", CServiceBroker::GetCPUInfo()->GetCPUFrequency());
+ m_info.osVersionInfo = CSysInfo::GetOsPrettyNameWithVersion() + " (kernel: " + CSysInfo::GetKernelName() + " " + CSysInfo::GetKernelVersionFull() + ")";
+ m_info.macAddress = GetMACAddress();
+ m_info.batteryLevel = GetBatteryLevel();
+ return true;
+}
+
+const CSysData &CSysInfoJob::GetData() const
+{
+ return m_info;
+}
+
+CSysData::INTERNET_STATE CSysInfoJob::GetInternetState()
+{
+ // Internet connection state!
+ XFILE::CCurlFile http;
+ if (http.IsInternet())
+ return CSysData::CONNECTED;
+ return CSysData::DISCONNECTED;
+}
+
+std::string CSysInfoJob::GetMACAddress()
+{
+ CNetworkInterface* iface = CServiceBroker::GetNetwork().GetFirstConnectedInterface();
+ if (iface)
+ return iface->GetMacAddress();
+
+ return "";
+}
+
+std::string CSysInfoJob::GetVideoEncoder()
+{
+ return "GPU: " + CServiceBroker::GetRenderSystem()->GetRenderRenderer();
+}
+
+std::string CSysInfoJob::GetBatteryLevel()
+{
+ return StringUtils::Format("{}%", CServiceBroker::GetPowerManager().BatteryLevel());
+}
+
+bool CSysInfoJob::SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays)
+{
+ iHours = 0; iDays = 0;
+ iMinutes = iInputMinutes;
+ if (iMinutes >= 60) // Hour's
+ {
+ iHours = iMinutes / 60;
+ iMinutes = iMinutes - (iHours *60);
+ }
+ if (iHours >= 24) // Days
+ {
+ iDays = iHours / 24;
+ iHours = iHours - (iDays * 24);
+ }
+ return true;
+}
+
+std::string CSysInfoJob::GetSystemUpTime(bool bTotalUptime)
+{
+ std::string strSystemUptime;
+ int iInputMinutes, iMinutes,iHours,iDays;
+
+ auto now = std::chrono::steady_clock::now();
+ auto duration = std::chrono::duration_cast<std::chrono::minutes>(now - startTime);
+
+ if(bTotalUptime)
+ {
+ //Total Uptime
+ iInputMinutes = g_sysinfo.GetTotalUptime() + duration.count();
+ }
+ else
+ {
+ //Current UpTime
+ iInputMinutes = duration.count();
+ }
+
+ SystemUpTime(iInputMinutes,iMinutes, iHours, iDays);
+ if (iDays > 0)
+ {
+ strSystemUptime =
+ StringUtils::Format("{} {}, {} {}, {} {}", iDays, g_localizeStrings.Get(12393), iHours,
+ g_localizeStrings.Get(12392), iMinutes, g_localizeStrings.Get(12391));
+ }
+ else if (iDays == 0 && iHours >= 1 )
+ {
+ strSystemUptime = StringUtils::Format("{} {}, {} {}", iHours, g_localizeStrings.Get(12392),
+ iMinutes, g_localizeStrings.Get(12391));
+ }
+ else if (iDays == 0 && iHours == 0 && iMinutes >= 0)
+ {
+ strSystemUptime = StringUtils::Format("{} {}", iMinutes, g_localizeStrings.Get(12391));
+ }
+ return strSystemUptime;
+}
+
+std::string CSysInfo::TranslateInfo(int info) const
+{
+ switch(info)
+ {
+ case SYSTEM_VIDEO_ENCODER_INFO:
+ return m_info.videoEncoder;
+ case NETWORK_MAC_ADDRESS:
+ return m_info.macAddress;
+ case SYSTEM_OS_VERSION_INFO:
+ return m_info.osVersionInfo;
+ case SYSTEM_CPUFREQUENCY:
+ return m_info.cpuFrequency;
+ case SYSTEM_UPTIME:
+ return m_info.systemUptime;
+ case SYSTEM_TOTALUPTIME:
+ return m_info.systemTotalUptime;
+ case SYSTEM_INTERNET_STATE:
+ if (m_info.internetState == CSysData::CONNECTED)
+ return g_localizeStrings.Get(13296);
+ else
+ return g_localizeStrings.Get(13297);
+ case SYSTEM_BATTERY_LEVEL:
+ return m_info.batteryLevel;
+ default:
+ return "";
+ }
+}
+
+void CSysInfo::Reset()
+{
+ m_info.Reset();
+}
+
+CSysInfo::CSysInfo(void) : CInfoLoader(15 * 1000)
+{
+ memset(MD5_Sign, 0, sizeof(MD5_Sign));
+ m_iSystemTimeTotalUp = 0;
+}
+
+CSysInfo::~CSysInfo() = default;
+
+bool CSysInfo::Load(const TiXmlNode *settings)
+{
+ if (settings == NULL)
+ return false;
+
+ const TiXmlElement *pElement = settings->FirstChildElement("general");
+ if (pElement)
+ XMLUtils::GetInt(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, INT_MAX);
+
+ return true;
+}
+
+bool CSysInfo::Save(TiXmlNode *settings) const
+{
+ if (settings == NULL)
+ return false;
+
+ TiXmlNode *generalNode = settings->FirstChild("general");
+ if (generalNode == NULL)
+ {
+ TiXmlElement generalNodeNew("general");
+ generalNode = settings->InsertEndChild(generalNodeNew);
+ if (generalNode == NULL)
+ return false;
+ }
+ XMLUtils::SetInt(generalNode, "systemtotaluptime", m_iSystemTimeTotalUp);
+
+ return true;
+}
+
+const std::string& CSysInfo::GetAppName(void)
+{
+ assert(CCompileInfo::GetAppName() != NULL);
+ static const std::string appName(CCompileInfo::GetAppName());
+
+ return appName;
+}
+
+bool CSysInfo::GetDiskSpace(std::string drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed)
+{
+ using namespace KODI::PLATFORM::FILESYSTEM;
+
+ space_info total = {};
+ std::error_code ec;
+
+ // None of this makes sense but the idea of total space
+ // makes no sense on any system really.
+ // Return space for / or for C: as it's correct in a sense
+ // and not much worse than trying to count a total for different
+ // drives/mounts
+ if (drive.empty() || drive == "*")
+ {
+#if defined(TARGET_WINDOWS)
+ drive = "C";
+#elif defined(TARGET_POSIX)
+ drive = "/";
+#endif
+ }
+
+#ifdef TARGET_WINDOWS_DESKTOP
+ using KODI::PLATFORM::WINDOWS::ToW;
+ UINT uidriveType = GetDriveType(ToW(drive + ":\\").c_str());
+ if (uidriveType != DRIVE_UNKNOWN && uidriveType != DRIVE_NO_ROOT_DIR)
+ total = space(drive + ":\\", ec);
+#elif defined(TARGET_POSIX)
+ total = space(drive, ec);
+#endif
+ if (ec.value() != 0)
+ return false;
+
+ iTotal = static_cast<int>(total.capacity / MB);
+ iTotalFree = static_cast<int>(total.free / MB);
+ iTotalUsed = iTotal - iTotalFree;
+ if (total.capacity > 0)
+ iPercentUsed = static_cast<int>(100.0f * (total.capacity - total.free) / total.capacity + 0.5f);
+ else
+ iPercentUsed = 0;
+
+ iPercentFree = 100 - iPercentUsed;
+
+ return true;
+}
+
+std::string CSysInfo::GetKernelName(bool emptyIfUnknown /*= false*/)
+{
+ static std::string kernelName;
+ if (kernelName.empty())
+ {
+#if defined(TARGET_WINDOWS_DESKTOP)
+ OSVERSIONINFOEXW osvi = {};
+ if (sysGetVersionExWByRef(osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ kernelName = "Windows NT";
+#elif defined(TARGET_WINDOWS_STORE)
+ auto e = EasClientDeviceInformation();
+ auto os = e.OperatingSystem();
+ g_charsetConverter.wToUTF8(std::wstring(os.c_str()), kernelName);
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ kernelName.assign(un.sysname);
+#endif // defined(TARGET_POSIX)
+
+ if (kernelName.empty())
+ kernelName = "Unknown kernel"; // can't detect
+ }
+
+ if (emptyIfUnknown && kernelName == "Unknown kernel")
+ return "";
+
+ return kernelName;
+}
+
+std::string CSysInfo::GetKernelVersionFull(void)
+{
+ static std::string kernelVersionFull;
+ if (!kernelVersionFull.empty())
+ return kernelVersionFull;
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ OSVERSIONINFOEXW osvi = {};
+ DWORD dwBuildRevision = 0;
+ DWORD len = sizeof(DWORD);
+
+ if (sysGetVersionExWByRef(osvi))
+ kernelVersionFull = StringUtils::Format("{}.{}.{}", osvi.dwMajorVersion, osvi.dwMinorVersion,
+ osvi.dwBuildNumber);
+ // get UBR (updates build revision)
+ if (ERROR_SUCCESS == RegGetValueW(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, L"UBR",
+ RRF_RT_REG_DWORD, nullptr, &dwBuildRevision, &len))
+ {
+ kernelVersionFull += StringUtils::Format(".{}", dwBuildRevision);
+ }
+
+#elif defined(TARGET_WINDOWS_STORE)
+ // get the system version number
+ auto sv = AnalyticsInfo::VersionInfo().DeviceFamilyVersion();
+ wchar_t* end;
+ unsigned long long v = wcstoull(sv.c_str(), &end, 10);
+ unsigned long long v1 = (v & 0xFFFF000000000000L) >> 48;
+ unsigned long long v2 = (v & 0x0000FFFF00000000L) >> 32;
+ unsigned long long v3 = (v & 0x00000000FFFF0000L) >> 16;
+ unsigned long long v4 = (v & 0x000000000000FFFFL);
+ kernelVersionFull = StringUtils::Format("{}.{}.{}", v1, v2, v3);
+ if (v4)
+ kernelVersionFull += StringUtils::Format(".{}", v4);
+
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ kernelVersionFull.assign(un.release);
+#endif // defined(TARGET_POSIX)
+
+ if (kernelVersionFull.empty())
+ kernelVersionFull = "0.0.0"; // can't detect
+
+ return kernelVersionFull;
+}
+
+std::string CSysInfo::GetKernelVersion(void)
+{
+ static std::string kernelVersionClear;
+ if (kernelVersionClear.empty())
+ {
+ kernelVersionClear = GetKernelVersionFull();
+ const size_t erasePos = kernelVersionClear.find_first_not_of("0123456789.");
+ if (erasePos != std::string::npos)
+ kernelVersionClear.erase(erasePos);
+ }
+
+ return kernelVersionClear;
+}
+
+std::string CSysInfo::GetOsName(bool emptyIfUnknown /* = false*/)
+{
+ static std::string osName;
+ if (osName.empty())
+ {
+#if defined (TARGET_WINDOWS)
+ osName = GetKernelName() + "-based OS";
+#elif defined(TARGET_FREEBSD)
+ osName = GetKernelName(true); // FIXME: for FreeBSD OS name is a kernel name
+#elif defined(TARGET_DARWIN_IOS)
+ osName = "iOS";
+#elif defined(TARGET_DARWIN_TVOS)
+ osName = "tvOS";
+#elif defined(TARGET_DARWIN_OSX)
+ osName = "macOS";
+#elif defined (TARGET_ANDROID)
+ if (CJNIContext::GetPackageManager().hasSystemFeature("android.software.leanback"))
+ osName = "Android TV";
+ else
+ osName = "Android";
+#elif defined(TARGET_LINUX)
+ osName = getValueFromOs_release("NAME");
+ if (osName.empty())
+ osName = getValueFromLsb_release(lsb_rel_distributor);
+ if (osName.empty())
+ osName = getValueFromOs_release("ID");
+#endif // defined(TARGET_LINUX)
+
+ if (osName.empty())
+ osName = "Unknown OS";
+ }
+
+ if (emptyIfUnknown && osName == "Unknown OS")
+ return "";
+
+ return osName;
+}
+
+std::string CSysInfo::GetOsVersion(void)
+{
+ static std::string osVersion;
+ if (!osVersion.empty())
+ return osVersion;
+
+#if defined(TARGET_WINDOWS) || defined(TARGET_FREEBSD)
+ osVersion = GetKernelVersion(); // FIXME: for Win32 and FreeBSD OS version is a kernel version
+#elif defined(TARGET_DARWIN)
+ osVersion = CDarwinUtils::GetVersionString();
+#elif defined(TARGET_ANDROID)
+ char versionCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.build.version.release", versionCStr);
+ osVersion.assign(versionCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+
+ if (osVersion.empty() || std::string("0123456789").find(versionCStr[0]) == std::string::npos)
+ osVersion.clear(); // can't correctly detect Android version
+ else
+ {
+ size_t pointPos = osVersion.find('.');
+ if (pointPos == std::string::npos)
+ osVersion += ".0.0";
+ else if (osVersion.find('.', pointPos + 1) == std::string::npos)
+ osVersion += ".0";
+ }
+#elif defined(TARGET_LINUX)
+ osVersion = getValueFromOs_release("VERSION_ID");
+ if (osVersion.empty())
+ osVersion = getValueFromLsb_release(lsb_rel_release);
+#endif // defined(TARGET_LINUX)
+
+ if (osVersion.empty())
+ osVersion = "0.0";
+
+ return osVersion;
+}
+
+std::string CSysInfo::GetOsPrettyNameWithVersion(void)
+{
+ static std::string osNameVer;
+ if (!osNameVer.empty())
+ return osNameVer;
+
+#if defined (TARGET_WINDOWS_DESKTOP)
+ OSVERSIONINFOEXW osvi = {};
+
+ osNameVer = "Windows ";
+ if (sysGetVersionExWByRef(osvi))
+ {
+ switch (GetWindowsVersion())
+ {
+ case WindowsVersionWin7:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("7");
+ else
+ osNameVer.append("Server 2008 R2");
+ break;
+ case WindowsVersionWin8:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("8");
+ else
+ osNameVer.append("Server 2012");
+ break;
+ case WindowsVersionWin8_1:
+ if (osvi.wProductType == VER_NT_WORKSTATION)
+ osNameVer.append("8.1");
+ else
+ osNameVer.append("Server 2012 R2");
+ break;
+ case WindowsVersionWin10:
+ case WindowsVersionWin10_1709:
+ case WindowsVersionWin10_1803:
+ case WindowsVersionWin10_1809:
+ case WindowsVersionWin10_1903:
+ case WindowsVersionWin10_1909:
+ case WindowsVersionWin10_2004:
+ case WindowsVersionWin10_Future:
+ osNameVer.append("10");
+ appendWindows10NameVersion(osNameVer);
+ break;
+ case WindowsVersionWin11:
+ osNameVer.append("11");
+ appendWindows10NameVersion(osNameVer);
+ break;
+ case WindowsVersionFuture:
+ osNameVer.append("Unknown future version");
+ break;
+ default:
+ osNameVer.append("Unknown version");
+ break;
+ }
+
+ // Append Service Pack version if any
+ if (osvi.wServicePackMajor > 0 || osvi.wServicePackMinor > 0)
+ {
+ osNameVer.append(StringUtils::Format(" SP{}", osvi.wServicePackMajor));
+ if (osvi.wServicePackMinor > 0)
+ {
+ osNameVer.append(StringUtils::Format(".{}", osvi.wServicePackMinor));
+ }
+ }
+ }
+ else
+ osNameVer.append(" unknown");
+#elif defined(TARGET_WINDOWS_STORE)
+ osNameVer = GetKernelName() + " " + GetOsVersion();
+#elif defined(TARGET_FREEBSD)
+ osNameVer = GetOsName() + " " + GetOsVersion();
+#elif defined(TARGET_DARWIN)
+ osNameVer = StringUtils::Format("{} {} ({})", GetOsName(), GetOsVersion(),
+ CDarwinUtils::GetOSVersionString());
+#elif defined(TARGET_ANDROID)
+ osNameVer =
+ GetOsName() + " " + GetOsVersion() + " API level " + std::to_string(CJNIBuild::SDK_INT);
+#elif defined(TARGET_LINUX)
+ osNameVer = getValueFromOs_release("PRETTY_NAME");
+ if (osNameVer.empty())
+ {
+ osNameVer = getValueFromLsb_release(lsb_rel_description);
+ std::string osName(GetOsName(true));
+ if (!osName.empty() && osNameVer.find(osName) == std::string::npos)
+ osNameVer = osName + osNameVer;
+ if (osNameVer.empty())
+ osNameVer = "Unknown Linux Distribution";
+ }
+
+ if (osNameVer.find(GetOsVersion()) == std::string::npos)
+ osNameVer += " " + GetOsVersion();
+#endif // defined(TARGET_LINUX)
+
+ if (osNameVer.empty())
+ osNameVer = "Unknown OS Unknown version";
+
+ return osNameVer;
+
+}
+
+std::string CSysInfo::GetManufacturerName(void)
+{
+ static std::string manufName;
+ static bool inited = false;
+ if (!inited)
+ {
+#if defined(TARGET_ANDROID)
+ char deviceCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.product.manufacturer", deviceCStr);
+ manufName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+#elif defined(TARGET_DARWIN)
+ manufName = CDarwinUtils::GetManufacturer();
+#elif defined(TARGET_WINDOWS_STORE)
+ auto eas = EasClientDeviceInformation();
+ auto manufacturer = eas.SystemManufacturer();
+ g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), manufName);
+#elif defined(TARGET_LINUX)
+
+ auto cpuInfo = CServiceBroker::GetCPUInfo();
+ manufName = cpuInfo->GetCPUSoC();
+
+#elif defined(TARGET_WINDOWS)
+ // We just don't care, might be useful on embedded
+#endif
+ inited = true;
+ }
+
+ return manufName;
+}
+
+std::string CSysInfo::GetModelName(void)
+{
+ static std::string modelName;
+ static bool inited = false;
+ if (!inited)
+ {
+#if defined(TARGET_ANDROID)
+ char deviceCStr[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.product.model", deviceCStr);
+ modelName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
+#elif defined(TARGET_DARWIN_EMBEDDED)
+ modelName = CDarwinUtils::getIosPlatformString();
+#elif defined(TARGET_DARWIN_OSX)
+ size_t nameLen = 0; // 'nameLen' should include terminating null
+ if (sysctlbyname("hw.model", NULL, &nameLen, NULL, 0) == 0 && nameLen > 1)
+ {
+ std::vector<char> buf(nameLen);
+ if (sysctlbyname("hw.model", buf.data(), &nameLen, NULL, 0) == 0 && nameLen == buf.size())
+ modelName.assign(buf.data(),
+ nameLen - 1); // assign exactly 'nameLen-1' characters to 'modelName'
+ }
+#elif defined(TARGET_WINDOWS_STORE)
+ auto eas = EasClientDeviceInformation();
+ auto manufacturer = eas.SystemProductName();
+ g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), modelName);
+#elif defined(TARGET_LINUX)
+ auto cpuInfo = CServiceBroker::GetCPUInfo();
+ modelName = cpuInfo->GetCPUHardware();
+#elif defined(TARGET_WINDOWS)
+ // We just don't care, might be useful on embedded
+#endif
+ inited = true;
+ }
+
+ return modelName;
+}
+
+bool CSysInfo::IsAeroDisabled()
+{
+#ifdef TARGET_WINDOWS_STORE
+ return true; // need to review https://msdn.microsoft.com/en-us/library/windows/desktop/aa969518(v=vs.85).aspx
+#elif defined(TARGET_WINDOWS)
+ BOOL aeroEnabled = FALSE;
+ HRESULT res = DwmIsCompositionEnabled(&aeroEnabled);
+ if (SUCCEEDED(res))
+ return !aeroEnabled;
+#endif
+ return false;
+}
+
+CSysInfo::WindowsVersion CSysInfo::m_WinVer = WindowsVersionUnknown;
+
+bool CSysInfo::IsWindowsVersion(WindowsVersion ver)
+{
+ if (ver == WindowsVersionUnknown)
+ return false;
+ return GetWindowsVersion() == ver;
+}
+
+bool CSysInfo::IsWindowsVersionAtLeast(WindowsVersion ver)
+{
+ if (ver == WindowsVersionUnknown)
+ return false;
+ return GetWindowsVersion() >= ver;
+}
+
+CSysInfo::WindowsVersion CSysInfo::GetWindowsVersion()
+{
+#ifdef TARGET_WINDOWS_DESKTOP
+ if (m_WinVer == WindowsVersionUnknown)
+ {
+ OSVERSIONINFOEXW osvi = {};
+ if (sysGetVersionExWByRef(osvi))
+ {
+ if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
+ m_WinVer = WindowsVersionWin7;
+ else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
+ m_WinVer = WindowsVersionWin8;
+ else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
+ m_WinVer = WindowsVersionWin8_1;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < 16299)
+ m_WinVer = WindowsVersionWin10;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 16299)
+ m_WinVer = WindowsVersionWin10_1709;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 17134)
+ m_WinVer = WindowsVersionWin10_1803;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 17763)
+ m_WinVer = WindowsVersionWin10_1809;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 18362)
+ m_WinVer = WindowsVersionWin10_1903;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 18363)
+ m_WinVer = WindowsVersionWin10_1909;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber == 19041)
+ m_WinVer = WindowsVersionWin10_2004;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 22000)
+ m_WinVer = WindowsVersionWin11;
+ else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber > 19041)
+ m_WinVer = WindowsVersionWin10_Future;
+ /* Insert checks for new Windows versions here */
+ else if ( (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 3) || osvi.dwMajorVersion > 10)
+ m_WinVer = WindowsVersionFuture;
+ }
+ }
+#elif defined(TARGET_WINDOWS_STORE)
+ m_WinVer = WindowsVersionWin10;
+#endif // TARGET_WINDOWS
+ return m_WinVer;
+}
+
+int CSysInfo::GetKernelBitness(void)
+{
+ static int kernelBitness = -1;
+ if (kernelBitness == -1)
+ {
+#ifdef TARGET_WINDOWS_STORE
+ Package package = Package::Current();
+ auto arch = package.Id().Architecture();
+ switch (arch)
+ {
+ case ProcessorArchitecture::X86:
+ kernelBitness = 32;
+ break;
+ case ProcessorArchitecture::X64:
+ kernelBitness = 64;
+ break;
+ case ProcessorArchitecture::Arm:
+ kernelBitness = 32;
+ break;
+ case ProcessorArchitecture::Unknown: // not sure what to do here. guess 32 for now
+ case ProcessorArchitecture::Neutral:
+ kernelBitness = 32;
+ break;
+ }
+#elif defined(TARGET_WINDOWS_DESKTOP)
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ kernelBitness = 32;
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ kernelBitness = 64;
+ else
+ {
+ BOOL isWow64 = FALSE;
+ if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) // fallback
+ kernelBitness = 64;
+ }
+#elif defined(TARGET_DARWIN_EMBEDDED)
+ // Note: OS X return x86 CPU type without CPU_ARCH_ABI64 flag
+ const NXArchInfo* archInfo = NXGetLocalArchInfo();
+ if (archInfo)
+ kernelBitness = ((archInfo->cputype & CPU_ARCH_ABI64) != 0) ? 64 : 32;
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string machine(un.machine);
+ if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" ||
+ machine == "ppc64" || machine == "ppc64el" || machine == "ppc64le" || machine == "ia64" ||
+ machine == "mips64" || machine == "s390x" || machine == "riscv64")
+ kernelBitness = 64;
+ else
+ kernelBitness = 32;
+ }
+#endif
+ if (kernelBitness == -1)
+ kernelBitness = 0; // can't detect
+ }
+
+ return kernelBitness;
+}
+
+const std::string& CSysInfo::GetKernelCpuFamily(void)
+{
+ static std::string kernelCpuFamily;
+ if (kernelCpuFamily.empty())
+ {
+#ifdef TARGET_WINDOWS
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ||
+ si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ kernelCpuFamily = "x86";
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ kernelCpuFamily = "ARM";
+#elif defined(TARGET_DARWIN)
+ const NXArchInfo* archInfo = NXGetLocalArchInfo();
+ if (archInfo)
+ {
+ const cpu_type_t cpuType = (archInfo->cputype & ~CPU_ARCH_ABI64); // get CPU family without 64-bit ABI flag
+ if (cpuType == CPU_TYPE_I386)
+ kernelCpuFamily = "x86";
+ else if (cpuType == CPU_TYPE_ARM)
+ kernelCpuFamily = "ARM";
+#ifdef CPU_TYPE_MIPS
+ else if (cpuType == CPU_TYPE_MIPS)
+ kernelCpuFamily = "MIPS";
+#endif // CPU_TYPE_MIPS
+ }
+#elif defined(TARGET_POSIX)
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string machine(un.machine);
+ if (machine.compare(0, 3, "arm", 3) == 0 || machine.compare(0, 7, "aarch64", 7) == 0)
+ kernelCpuFamily = "ARM";
+ else if (machine.compare(0, 4, "mips", 4) == 0)
+ kernelCpuFamily = "MIPS";
+ else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" || machine.compare(0, 3, "x86", 3) == 0)
+ kernelCpuFamily = "x86";
+ else if (machine.compare(0, 4, "s390", 4) == 0)
+ kernelCpuFamily = "s390";
+ else if (machine.compare(0, 3, "ppc", 3) == 0 || machine.compare(0, 5, "power", 5) == 0)
+ kernelCpuFamily = "PowerPC";
+ else if (machine.compare(0, 5, "riscv", 5) == 0)
+ kernelCpuFamily = "RISC-V";
+ }
+#endif
+ if (kernelCpuFamily.empty())
+ kernelCpuFamily = "unknown CPU family";
+ }
+ return kernelCpuFamily;
+}
+
+int CSysInfo::GetXbmcBitness(void)
+{
+ return static_cast<int>(sizeof(void*) * 8);
+}
+
+bool CSysInfo::HasInternet()
+{
+ if (m_info.internetState != CSysData::UNKNOWN)
+ return m_info.internetState == CSysData::CONNECTED;
+ return (m_info.internetState = CSysInfoJob::GetInternetState()) == CSysData::CONNECTED;
+}
+
+std::string CSysInfo::GetHddSpaceInfo(int drive, bool shortText)
+{
+ int percent;
+ return GetHddSpaceInfo( percent, drive, shortText);
+}
+
+std::string CSysInfo::GetHddSpaceInfo(int& percent, int drive, bool shortText)
+{
+ int total, totalFree, totalUsed, percentFree, percentused;
+ std::string strRet;
+ percent = 0;
+ if (g_sysinfo.GetDiskSpace("", total, totalFree, totalUsed, percentFree, percentused))
+ {
+ if (shortText)
+ {
+ switch(drive)
+ {
+ case SYSTEM_FREE_SPACE:
+ percent = percentFree;
+ break;
+ case SYSTEM_USED_SPACE:
+ percent = percentused;
+ break;
+ }
+ }
+ else
+ {
+ switch(drive)
+ {
+ case SYSTEM_FREE_SPACE:
+ strRet = StringUtils::Format("{} MB {}", totalFree, g_localizeStrings.Get(160));
+ break;
+ case SYSTEM_USED_SPACE:
+ strRet = StringUtils::Format("{} MB {}", totalUsed, g_localizeStrings.Get(20162));
+ break;
+ case SYSTEM_TOTAL_SPACE:
+ strRet = StringUtils::Format("{} MB {}", total, g_localizeStrings.Get(20161));
+ break;
+ case SYSTEM_FREE_SPACE_PERCENT:
+ strRet = StringUtils::Format("{} % {}", percentFree, g_localizeStrings.Get(160));
+ break;
+ case SYSTEM_USED_SPACE_PERCENT:
+ strRet = StringUtils::Format("{} % {}", percentused, g_localizeStrings.Get(20162));
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (shortText)
+ strRet = g_localizeStrings.Get(10006); // N/A
+ else
+ strRet = g_localizeStrings.Get(10005); // Not available
+ }
+ return strRet;
+}
+
+std::string CSysInfo::GetUserAgent()
+{
+ static std::string result;
+ if (!result.empty())
+ return result;
+
+ result = GetAppName() + "/" + CSysInfo::GetVersionShort() + " (";
+#if defined(TARGET_WINDOWS)
+ result += GetKernelName() + " " + GetKernelVersion();
+#ifndef TARGET_WINDOWS_STORE
+ BOOL bIsWow = FALSE;
+ if (IsWow64Process(GetCurrentProcess(), &bIsWow) && bIsWow)
+ result.append("; WOW64");
+ else
+#endif
+ {
+ SYSTEM_INFO si = {};
+ GetSystemInfo(&si);
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
+ result.append("; Win64; x64");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
+ result.append("; Win64; IA64");
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
+ result.append("; ARM");
+ }
+#elif defined(TARGET_DARWIN)
+#if defined(TARGET_DARWIN_EMBEDDED)
+ std::string iDevStr(GetModelName()); // device model name with number of model version
+ size_t iDevStrDigit = iDevStr.find_first_of("0123456789");
+ std::string iDev(iDevStr, 0, iDevStrDigit); // device model name without number
+ if (iDevStrDigit == 0)
+ iDev = "unknown";
+ result += iDev + "; ";
+ std::string iOSVersion(GetOsVersion());
+ size_t lastDotPos = iOSVersion.rfind('.');
+ if (lastDotPos != std::string::npos && iOSVersion.find('.') != lastDotPos
+ && iOSVersion.find_first_not_of('0', lastDotPos + 1) == std::string::npos)
+ iOSVersion.erase(lastDotPos);
+ StringUtils::Replace(iOSVersion, '.', '_');
+ if (iDev == "AppleTV")
+ {
+ // check if it's ATV4 (AppleTV5,3) or later
+ auto modelMajorNumberEndPos = iDevStr.find_first_of(',', iDevStrDigit);
+ std::string s{iDevStr, iDevStrDigit, modelMajorNumberEndPos - iDevStrDigit};
+ if (stoi(s) >= 5)
+ result += "CPU TVOS";
+ else
+ result += "CPU OS";
+ }
+ else if (iDev == "iPad")
+ result += "CPU OS";
+ else
+ result += "CPU iPhone OS ";
+ result += iOSVersion + " like Mac OS X";
+#else
+ result += "Macintosh; ";
+ std::string cpuFam(GetBuildTargetCpuFamily());
+ if (cpuFam == "x86")
+ result += "Intel ";
+ result += "Mac OS X ";
+ std::string OSXVersion(GetOsVersion());
+ StringUtils::Replace(OSXVersion, '.', '_');
+ result += OSXVersion;
+#endif
+#elif defined(TARGET_ANDROID)
+ result += "Linux; Android ";
+ std::string versionStr(GetOsVersion());
+ const size_t verLen = versionStr.length();
+ if (verLen >= 2 && versionStr.compare(verLen - 2, 2, ".0", 2) == 0)
+ versionStr.erase(verLen - 2); // remove last ".0" if any
+ result += versionStr;
+ std::string deviceInfo(GetModelName());
+
+ char buildId[PROP_VALUE_MAX];
+ int propLen = __system_property_get("ro.build.id", buildId);
+ if (propLen > 0 && propLen <= PROP_VALUE_MAX)
+ {
+ if (!deviceInfo.empty())
+ deviceInfo += " ";
+ deviceInfo += "Build/";
+ deviceInfo.append(buildId, propLen);
+ }
+
+ if (!deviceInfo.empty())
+ result += "; " + deviceInfo;
+#elif defined(TARGET_POSIX)
+ result += "X11; ";
+ struct utsname un;
+ if (uname(&un) == 0)
+ {
+ std::string cpuStr(un.machine);
+ if (cpuStr == "x86_64" && GetXbmcBitness() == 32)
+ cpuStr = "i686 on x86_64";
+ result += un.sysname;
+ result += " ";
+ result += cpuStr;
+ }
+ else
+ result += "Unknown";
+#else
+ result += "Unknown";
+#endif
+ result += ")";
+
+ if (GetAppName() != "Kodi")
+ result += " Kodi_Fork_" + GetAppName() + "/1.0"; // default fork number is '1.0', replace it with actual number if necessary
+
+#ifdef TARGET_LINUX
+ // Add distribution name
+ std::string linuxOSName(GetOsName(true));
+ if (!linuxOSName.empty())
+ result += " " + linuxOSName + "/" + GetOsVersion();
+#endif
+
+#if defined(TARGET_DARWIN_IOS)
+ std::string iDevVer;
+ if (iDevStrDigit == std::string::npos)
+ iDevVer = "0.0";
+ else
+ iDevVer.assign(iDevStr, iDevStrDigit, std::string::npos);
+ StringUtils::Replace(iDevVer, ',', '.');
+ result += " HW_" + iDev + "/" + iDevVer;
+#endif
+ // add more device IDs here if needed.
+ // keep only one device ID in result! Form:
+ // result += " HW_" + "deviceID" + "/" + "1.0"; // '1.0' if device has no version
+
+#if defined(TARGET_ANDROID)
+ // Android has no CPU string by default, so add it as additional parameter
+ struct utsname un1;
+ if (uname(&un1) == 0)
+ {
+ std::string cpuStr(un1.machine);
+ StringUtils::Replace(cpuStr, ' ', '_');
+ result += " Sys_CPU/" + cpuStr;
+ }
+#endif
+
+ result += " App_Bitness/" + std::to_string(GetXbmcBitness());
+
+ std::string fullVer(CSysInfo::GetVersion());
+ StringUtils::Replace(fullVer, ' ', '-');
+ result += " Version/" + fullVer;
+
+ return result;
+}
+
+std::string CSysInfo::GetDeviceName()
+{
+ std::string friendlyName = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SERVICES_DEVICENAME);
+ if (StringUtils::EqualsNoCase(friendlyName, CCompileInfo::GetAppName()))
+ {
+ std::string hostname("[unknown]");
+ CServiceBroker::GetNetwork().GetHostName(hostname);
+ return StringUtils::Format("{} ({})", friendlyName, hostname);
+ }
+
+ return friendlyName;
+}
+
+// Version string MUST NOT contain spaces. It is used
+// in the HTTP request user agent.
+std::string CSysInfo::GetVersionShort()
+{
+ if (strlen(CCompileInfo::GetSuffix()) == 0)
+ return StringUtils::Format("{}.{}", CCompileInfo::GetMajor(), CCompileInfo::GetMinor());
+ else
+ return StringUtils::Format("{}.{}-{}", CCompileInfo::GetMajor(), CCompileInfo::GetMinor(),
+ CCompileInfo::GetSuffix());
+}
+
+std::string CSysInfo::GetVersion()
+{
+ return GetVersionShort() + " (" + CCompileInfo::GetVersionCode() + ")" +
+ " Git:" + CCompileInfo::GetSCMID();
+}
+
+std::string CSysInfo::GetVersionCode()
+{
+ return CCompileInfo::GetVersionCode();
+}
+
+std::string CSysInfo::GetVersionGit()
+{
+ return CCompileInfo::GetSCMID();
+}
+
+std::string CSysInfo::GetBuildDate()
+{
+ return CCompileInfo::GetBuildDate();
+}
+
+std::string CSysInfo::GetBuildTargetPlatformName(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+ return "macOS";
+#elif defined(TARGET_DARWIN_IOS)
+ return "iOS";
+#elif defined(TARGET_DARWIN_TVOS)
+ return "tvOS";
+#elif defined(TARGET_FREEBSD)
+ return "FreeBSD";
+#elif defined(TARGET_ANDROID)
+ return "Android";
+#elif defined(TARGET_LINUX)
+ return "Linux";
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ return "Windows NT";
+#else // !NTDDI_VERSION
+ return "unknown Win32 platform";
+#endif // !NTDDI_VERSION
+#else
+ return "unknown platform";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetPlatformVersion(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+ return XSTR_MACRO(__MAC_OS_X_VERSION_MIN_REQUIRED);
+#elif defined(TARGET_DARWIN_IOS)
+ return XSTR_MACRO(__IPHONE_OS_VERSION_MIN_REQUIRED);
+#elif defined(TARGET_DARWIN_TVOS)
+ return XSTR_MACRO(__TV_OS_VERSION_MIN_REQUIRED);
+#elif defined(TARGET_FREEBSD)
+ return XSTR_MACRO(__FreeBSD_version);
+#elif defined(TARGET_ANDROID)
+ return "API level " XSTR_MACRO(__ANDROID_API__);
+#elif defined(TARGET_LINUX)
+ return XSTR_MACRO(LINUX_VERSION_CODE);
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ return XSTR_MACRO(NTDDI_VERSION);
+#else // !NTDDI_VERSION
+ return "(unknown Win32 platform)";
+#endif // !NTDDI_VERSION
+#else
+ return "(unknown platform)";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetPlatformVersionDecoded(void)
+{
+#if defined(TARGET_DARWIN_OSX)
+ if (__MAC_OS_X_VERSION_MIN_REQUIRED % 100)
+ return StringUtils::Format("version {}.{}.{}", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
+ (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100,
+ __MAC_OS_X_VERSION_MIN_REQUIRED % 100);
+ else
+ return StringUtils::Format("version {}.{}", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
+ (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100);
+#elif defined(TARGET_DARWIN_EMBEDDED)
+ std::string versionStr = GetBuildTargetPlatformVersion();
+ static const int major = (std::stoi(versionStr) / 10000) % 100;
+ static const int minor = (std::stoi(versionStr) / 100) % 100;
+ static const int rev = std::stoi(versionStr) % 100;
+ return StringUtils::Format("version {}.{}.{}", major, minor, rev);
+#elif defined(TARGET_FREEBSD)
+ // FIXME: should works well starting from FreeBSD 8.1
+ static const int major = (__FreeBSD_version / 100000) % 100;
+ static const int minor = (__FreeBSD_version / 1000) % 100;
+ static const int Rxx = __FreeBSD_version % 1000;
+ if ((major < 9 && Rxx == 0))
+ return StringUtils::Format("version {}.{}-RELEASE", major, minor);
+ if (Rxx >= 500)
+ return StringUtils::Format("version {}.{}-STABLE", major, minor);
+
+ return StringUtils::Format("version {}.{}-CURRENT", major, minor);
+#elif defined(TARGET_ANDROID)
+ return "API level " XSTR_MACRO(__ANDROID_API__);
+#elif defined(TARGET_LINUX)
+ return StringUtils::Format("version {}.{}.{}", (LINUX_VERSION_CODE >> 16) & 0xFF,
+ (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF);
+#elif defined(TARGET_WINDOWS)
+#ifdef NTDDI_VERSION
+ std::string version(StringUtils::Format("version {}.{}", int(NTDDI_VERSION >> 24) & 0xFF,
+ int(NTDDI_VERSION >> 16) & 0xFF));
+ if (SPVER(NTDDI_VERSION))
+ version += StringUtils::Format(" SP{}", int(SPVER(NTDDI_VERSION)));
+ return version;
+#else // !NTDDI_VERSION
+ return "(unknown Win32 platform)";
+#endif // !NTDDI_VERSION
+#else
+ return "(unknown platform)";
+#endif
+}
+
+std::string CSysInfo::GetBuildTargetCpuFamily(void)
+{
+#if defined(__thumb__) || defined(_M_ARMT)
+ return "ARM (Thumb)";
+#elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
+ return "ARM";
+#elif defined(__mips__) || defined(mips) || defined(__mips)
+ return "MIPS";
+#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
+ defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_)
+ return "x86";
+#elif defined(__s390x__)
+ return "s390";
+#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC)
+ return "PowerPC";
+#elif defined(__riscv)
+ return "RISC-V";
+#else
+ return "unknown CPU family";
+#endif
+}
+
+std::string CSysInfo::GetUsedCompilerNameAndVer(void)
+{
+#if defined(__clang__)
+#ifdef __clang_version__
+ return "Clang " __clang_version__;
+#else // ! __clang_version__
+ return "Clang " XSTR_MACRO(__clang_major__) "." XSTR_MACRO(__clang_minor__) "." XSTR_MACRO(__clang_patchlevel__);
+#endif //! __clang_version__
+#elif defined (__INTEL_COMPILER)
+ return "Intel Compiler " XSTR_MACRO(__INTEL_COMPILER);
+#elif defined (__GNUC__)
+ std::string compilerStr;
+#ifdef __llvm__
+ /* Note: this will not detect GCC + DragonEgg */
+ compilerStr = "llvm-gcc ";
+#else // __llvm__
+ compilerStr = "GCC ";
+#endif // !__llvm__
+ compilerStr += XSTR_MACRO(__GNUC__) "." XSTR_MACRO(__GNUC_MINOR__) "." XSTR_MACRO(__GNUC_PATCHLEVEL__);
+ return compilerStr;
+#elif defined (_MSC_VER)
+ return "MSVC " XSTR_MACRO(_MSC_FULL_VER);
+#else
+ return "unknown compiler";
+#endif
+}
+
+std::string CSysInfo::GetPrivacyPolicy()
+{
+ if (m_privacyPolicy.empty())
+ {
+ CFile file;
+ std::vector<uint8_t> buf;
+ if (file.LoadFile("special://xbmc/privacy-policy.txt", buf) > 0)
+ {
+ m_privacyPolicy = std::string(reinterpret_cast<char*>(buf.data()), buf.size());
+ }
+ else
+ m_privacyPolicy = g_localizeStrings.Get(19055);
+ }
+ return m_privacyPolicy;
+}
+
+CSysInfo::WindowsDeviceFamily CSysInfo::GetWindowsDeviceFamily()
+{
+#ifdef TARGET_WINDOWS_STORE
+ auto familyName = AnalyticsInfo::VersionInfo().DeviceFamily();
+ if (familyName == L"Windows.Desktop")
+ return WindowsDeviceFamily::Desktop;
+ else if (familyName == L"Windows.Mobile")
+ return WindowsDeviceFamily::Mobile;
+ else if (familyName == L"Windows.Universal")
+ return WindowsDeviceFamily::IoT;
+ else if (familyName == L"Windows.Team")
+ return WindowsDeviceFamily::Surface;
+ else if (familyName == L"Windows.Xbox")
+ return WindowsDeviceFamily::Xbox;
+ else
+ return WindowsDeviceFamily::Other;
+#endif // TARGET_WINDOWS_STORE
+ return WindowsDeviceFamily::Desktop;
+}
+
+CJob *CSysInfo::GetJob() const
+{
+ return new CSysInfoJob();
+}
+
+void CSysInfo::OnJobComplete(unsigned int jobID, bool success, CJob *job)
+{
+ m_info = static_cast<CSysInfoJob*>(job)->GetData();
+ CInfoLoader::OnJobComplete(jobID, success, job);
+}