PageRenderTime 150ms CodeModel.GetById 30ms app.highlight 110ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/utils/SystemInfo.cpp

http://github.com/xbmc/xbmc
C++ | 1384 lines | 1345 code | 24 blank | 15 comment | 50 complexity | 3f049a32c8580c2130c0a8498aa1b66c MD5 | raw file
   1/*
   2 *  Copyright (C) 2005-2018 Team Kodi
   3 *  This file is part of Kodi - https://kodi.tv
   4 *
   5 *  SPDX-License-Identifier: GPL-2.0-or-later
   6 *  See LICENSES/README.md for more information.
   7 */
   8
   9#include <limits.h>
  10
  11#include "threads/SystemClock.h"
  12#include "SystemInfo.h"
  13#ifndef TARGET_POSIX
  14#include <conio.h>
  15#else
  16#include <sys/utsname.h>
  17#endif
  18#include "CompileInfo.h"
  19#include "ServiceBroker.h"
  20#include "filesystem/CurlFile.h"
  21#include "filesystem/File.h"
  22#include "guilib/LocalizeStrings.h"
  23#include "guilib/guiinfo/GUIInfoLabels.h"
  24#include "network/Network.h"
  25#include "platform/Filesystem.h"
  26#include "rendering/RenderSystem.h"
  27#include "settings/Settings.h"
  28#include "settings/SettingsComponent.h"
  29#include "utils/CPUInfo.h"
  30#include "utils/log.h"
  31
  32#ifdef TARGET_WINDOWS
  33#include <dwmapi.h>
  34#include "utils/CharsetConverter.h"
  35#include <VersionHelpers.h>
  36
  37#ifdef TARGET_WINDOWS_STORE
  38#include <winrt/Windows.Security.ExchangeActiveSyncProvisioning.h>
  39#include <winrt/Windows.System.Profile.h>
  40
  41using namespace winrt::Windows::ApplicationModel;
  42using namespace winrt::Windows::Security::ExchangeActiveSyncProvisioning;
  43using namespace winrt::Windows::System;
  44using namespace winrt::Windows::System::Profile;
  45#endif
  46#include <wincrypt.h>
  47#include "platform/win32/CharsetConverter.h"
  48#endif
  49#if defined(TARGET_DARWIN)
  50#include "platform/darwin/DarwinUtils.h"
  51#endif
  52#include "powermanagement/PowerManager.h"
  53#include "utils/StringUtils.h"
  54#include "utils/XMLUtils.h"
  55#if defined(TARGET_ANDROID)
  56#include <androidjni/Build.h>
  57#endif
  58
  59/* Platform identification */
  60#if defined(TARGET_DARWIN)
  61#include <Availability.h>
  62#include <mach-o/arch.h>
  63#include <sys/sysctl.h>
  64#include "utils/auto_buffer.h"
  65#elif defined(TARGET_ANDROID)
  66#include <android/api-level.h>
  67#include <sys/system_properties.h>
  68#elif defined(TARGET_FREEBSD)
  69#include <sys/param.h>
  70#elif defined(TARGET_LINUX)
  71#include "platform/linux/SysfsPath.h"
  72
  73#include <linux/version.h>
  74#endif
  75
  76#include <system_error>
  77
  78/* Expand macro before stringify */
  79#define STR_MACRO(x) #x
  80#define XSTR_MACRO(x) STR_MACRO(x)
  81
  82using namespace XFILE;
  83
  84#ifdef TARGET_WINDOWS_DESKTOP
  85static bool sysGetVersionExWByRef(OSVERSIONINFOEXW& osVerInfo)
  86{
  87  ZeroMemory(&osVerInfo, sizeof(osVerInfo));
  88  osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
  89
  90  typedef NTSTATUS(__stdcall *RtlGetVersionPtr)(RTL_OSVERSIONINFOEXW* pOsInfo);
  91  static HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
  92  if (hNtDll != NULL)
  93  {
  94    static RtlGetVersionPtr RtlGetVer = (RtlGetVersionPtr) GetProcAddress(hNtDll, "RtlGetVersion");
  95    if (RtlGetVer && RtlGetVer(&osVerInfo) == 0)
  96      return true;
  97  }
  98  // failed to get OS information directly from ntdll.dll
  99  // use GetVersionExW() as fallback
 100  // note: starting from Windows 8.1 GetVersionExW() may return unfaithful information
 101  if (GetVersionExW((OSVERSIONINFOW*) &osVerInfo) != 0)
 102      return true;
 103
 104  ZeroMemory(&osVerInfo, sizeof(osVerInfo));
 105  return false;
 106}
 107#endif // TARGET_WINDOWS_DESKTOP
 108
 109#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
 110static std::string getValueFromOs_release(std::string key)
 111{
 112  FILE* os_rel = fopen("/etc/os-release", "r");
 113  if (!os_rel)
 114    return "";
 115
 116  char* buf = new char[10 * 1024]; // more than enough
 117  size_t len = fread(buf, 1, 10 * 1024, os_rel);
 118  fclose(os_rel);
 119  if (len == 0)
 120  {
 121    delete[] buf;
 122    return "";
 123  }
 124
 125  std::string content(buf, len);
 126  delete[] buf;
 127
 128  // find begin of value string
 129  size_t valStart = 0, seachPos;
 130  key += '=';
 131  if (content.compare(0, key.length(), key) == 0)
 132    valStart = key.length();
 133  else
 134  {
 135    key = "\n" + key;
 136    seachPos = 0;
 137    do
 138    {
 139      seachPos = content.find(key, seachPos);
 140      if (seachPos == std::string::npos)
 141        return "";
 142      if (seachPos == 0 || content[seachPos - 1] != '\\')
 143        valStart = seachPos + key.length();
 144      else
 145        seachPos++;
 146    } while (valStart == 0);
 147  }
 148
 149  if (content[valStart] == '\n')
 150    return "";
 151
 152  // find end of value string
 153  seachPos = valStart;
 154  do
 155  {
 156    seachPos = content.find('\n', seachPos + 1);
 157  } while (seachPos != std::string::npos && content[seachPos - 1] == '\\');
 158  size_t const valEnd = seachPos;
 159
 160  std::string value(content, valStart, valEnd - valStart);
 161  if (value.empty())
 162    return value;
 163
 164  // remove quotes
 165  if (value[0] == '\'' || value[0] == '"')
 166  {
 167    if (value.length() < 2)
 168      return value;
 169    size_t qEnd = value.rfind(value[0]);
 170    if (qEnd != std::string::npos)
 171    {
 172      value.erase(qEnd);
 173      value.erase(0, 1);
 174    }
 175  }
 176
 177  // unescape characters
 178  for (size_t slashPos = value.find('\\'); slashPos < value.length() - 1; slashPos = value.find('\\', slashPos))
 179  {
 180    if (value[slashPos + 1] == '\n')
 181      value.erase(slashPos, 2);
 182    else
 183    {
 184      value.erase(slashPos, 1);
 185      slashPos++; // skip unescaped character
 186    }
 187  }
 188
 189  return value;
 190}
 191
 192enum lsb_rel_info_type
 193{
 194  lsb_rel_distributor,
 195  lsb_rel_description,
 196  lsb_rel_release,
 197  lsb_rel_codename
 198};
 199
 200static std::string getValueFromLsb_release(enum lsb_rel_info_type infoType)
 201{
 202  std::string key, command("unset PYTHONHOME; unset PYTHONPATH; lsb_release ");
 203  switch (infoType)
 204  {
 205  case lsb_rel_distributor:
 206    command += "-i";
 207    key = "Distributor ID:\t";
 208    break;
 209  case lsb_rel_description:
 210    command += "-d";
 211    key = "Description:\t";
 212    break;
 213  case lsb_rel_release:
 214    command += "-r";
 215    key = "Release:\t";
 216    break;
 217  case lsb_rel_codename:
 218    command += "-c";
 219    key = "Codename:\t";
 220    break;
 221  default:
 222    return "";
 223  }
 224  command += " 2>/dev/null";
 225  FILE* lsb_rel = popen(command.c_str(), "r");
 226  if (lsb_rel == NULL)
 227    return "";
 228
 229  char buf[300]; // more than enough
 230  if (fgets(buf, 300, lsb_rel) == NULL)
 231  {
 232    pclose(lsb_rel);
 233    return "";
 234  }
 235  pclose(lsb_rel);
 236
 237  std::string response(buf);
 238  if (response.compare(0, key.length(), key) != 0)
 239    return "";
 240
 241  return response.substr(key.length(), response.find('\n') - key.length());
 242}
 243#endif // TARGET_LINUX && !TARGET_ANDROID
 244
 245CSysInfo g_sysinfo;
 246
 247CSysInfoJob::CSysInfoJob() = default;
 248
 249bool CSysInfoJob::DoWork()
 250{
 251  m_info.systemUptime      = GetSystemUpTime(false);
 252  m_info.systemTotalUptime = GetSystemUpTime(true);
 253  m_info.internetState     = GetInternetState();
 254  m_info.videoEncoder      = GetVideoEncoder();
 255  m_info.cpuFrequency =
 256      StringUtils::Format("%4.0f MHz", CServiceBroker::GetCPUInfo()->GetCPUFrequency());
 257  m_info.osVersionInfo     = CSysInfo::GetOsPrettyNameWithVersion() + " (kernel: " + CSysInfo::GetKernelName() + " " + CSysInfo::GetKernelVersionFull() + ")";
 258  m_info.macAddress        = GetMACAddress();
 259  m_info.batteryLevel      = GetBatteryLevel();
 260  return true;
 261}
 262
 263const CSysData &CSysInfoJob::GetData() const
 264{
 265  return m_info;
 266}
 267
 268CSysData::INTERNET_STATE CSysInfoJob::GetInternetState()
 269{
 270  // Internet connection state!
 271  XFILE::CCurlFile http;
 272  if (http.IsInternet())
 273    return CSysData::CONNECTED;
 274  return CSysData::DISCONNECTED;
 275}
 276
 277std::string CSysInfoJob::GetMACAddress()
 278{
 279  CNetworkInterface* iface = CServiceBroker::GetNetwork().GetFirstConnectedInterface();
 280  if (iface)
 281    return iface->GetMacAddress();
 282
 283  return "";
 284}
 285
 286std::string CSysInfoJob::GetVideoEncoder()
 287{
 288  return "GPU: " + CServiceBroker::GetRenderSystem()->GetRenderRenderer();
 289}
 290
 291std::string CSysInfoJob::GetBatteryLevel()
 292{
 293  return StringUtils::Format("%d%%", CServiceBroker::GetPowerManager().BatteryLevel());
 294}
 295
 296bool CSysInfoJob::SystemUpTime(int iInputMinutes, int &iMinutes, int &iHours, int &iDays)
 297{
 298  iHours = 0; iDays = 0;
 299  iMinutes = iInputMinutes;
 300  if (iMinutes >= 60) // Hour's
 301  {
 302    iHours = iMinutes / 60;
 303    iMinutes = iMinutes - (iHours *60);
 304  }
 305  if (iHours >= 24) // Days
 306  {
 307    iDays = iHours / 24;
 308    iHours = iHours - (iDays * 24);
 309  }
 310  return true;
 311}
 312
 313std::string CSysInfoJob::GetSystemUpTime(bool bTotalUptime)
 314{
 315  std::string strSystemUptime;
 316  int iInputMinutes, iMinutes,iHours,iDays;
 317
 318  if(bTotalUptime)
 319  {
 320    //Total Uptime
 321    iInputMinutes = g_sysinfo.GetTotalUptime() + ((int)(XbmcThreads::SystemClockMillis() / 60000));
 322  }
 323  else
 324  {
 325    //Current UpTime
 326    iInputMinutes = (int)(XbmcThreads::SystemClockMillis() / 60000);
 327  }
 328
 329  SystemUpTime(iInputMinutes,iMinutes, iHours, iDays);
 330  if (iDays > 0)
 331  {
 332    strSystemUptime = StringUtils::Format("%i %s, %i %s, %i %s",
 333                                          iDays, g_localizeStrings.Get(12393).c_str(),
 334                                          iHours, g_localizeStrings.Get(12392).c_str(),
 335                                          iMinutes, g_localizeStrings.Get(12391).c_str());
 336  }
 337  else if (iDays == 0 && iHours >= 1 )
 338  {
 339    strSystemUptime = StringUtils::Format("%i %s, %i %s",
 340                                          iHours, g_localizeStrings.Get(12392).c_str(),
 341                                          iMinutes, g_localizeStrings.Get(12391).c_str());
 342  }
 343  else if (iDays == 0 && iHours == 0 &&  iMinutes >= 0)
 344  {
 345    strSystemUptime = StringUtils::Format("%i %s",
 346                                          iMinutes, g_localizeStrings.Get(12391).c_str());
 347  }
 348  return strSystemUptime;
 349}
 350
 351std::string CSysInfo::TranslateInfo(int info) const
 352{
 353  switch(info)
 354  {
 355  case SYSTEM_VIDEO_ENCODER_INFO:
 356    return m_info.videoEncoder;
 357  case NETWORK_MAC_ADDRESS:
 358    return m_info.macAddress;
 359  case SYSTEM_OS_VERSION_INFO:
 360    return m_info.osVersionInfo;
 361  case SYSTEM_CPUFREQUENCY:
 362    return m_info.cpuFrequency;
 363  case SYSTEM_UPTIME:
 364    return m_info.systemUptime;
 365  case SYSTEM_TOTALUPTIME:
 366    return m_info.systemTotalUptime;
 367  case SYSTEM_INTERNET_STATE:
 368    if (m_info.internetState == CSysData::CONNECTED)
 369      return g_localizeStrings.Get(13296);
 370    else
 371      return g_localizeStrings.Get(13297);
 372  case SYSTEM_BATTERY_LEVEL:
 373    return m_info.batteryLevel;
 374  default:
 375    return "";
 376  }
 377}
 378
 379void CSysInfo::Reset()
 380{
 381  m_info.Reset();
 382}
 383
 384CSysInfo::CSysInfo(void) : CInfoLoader(15 * 1000)
 385{
 386  memset(MD5_Sign, 0, sizeof(MD5_Sign));
 387  m_iSystemTimeTotalUp = 0;
 388}
 389
 390CSysInfo::~CSysInfo() = default;
 391
 392bool CSysInfo::Load(const TiXmlNode *settings)
 393{
 394  if (settings == NULL)
 395    return false;
 396
 397  const TiXmlElement *pElement = settings->FirstChildElement("general");
 398  if (pElement)
 399    XMLUtils::GetInt(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, INT_MAX);
 400
 401  return true;
 402}
 403
 404bool CSysInfo::Save(TiXmlNode *settings) const
 405{
 406  if (settings == NULL)
 407    return false;
 408
 409  TiXmlNode *generalNode = settings->FirstChild("general");
 410  if (generalNode == NULL)
 411  {
 412    TiXmlElement generalNodeNew("general");
 413    generalNode = settings->InsertEndChild(generalNodeNew);
 414    if (generalNode == NULL)
 415      return false;
 416  }
 417  XMLUtils::SetInt(generalNode, "systemtotaluptime", m_iSystemTimeTotalUp);
 418
 419  return true;
 420}
 421
 422const std::string& CSysInfo::GetAppName(void)
 423{
 424  assert(CCompileInfo::GetAppName() != NULL);
 425  static const std::string appName(CCompileInfo::GetAppName());
 426
 427  return appName;
 428}
 429
 430bool CSysInfo::GetDiskSpace(std::string drive,int& iTotal, int& iTotalFree, int& iTotalUsed, int& iPercentFree, int& iPercentUsed)
 431{
 432  using namespace KODI::PLATFORM::FILESYSTEM;
 433
 434  space_info total = { 0 };
 435  std::error_code ec;
 436
 437  // None of this makes sense but the idea of total space
 438  // makes no sense on any system really.
 439  // Return space for / or for C: as it's correct in a sense
 440  // and not much worse than trying to count a total for different
 441  // drives/mounts
 442  if (drive.empty() || drive == "*")
 443  {
 444#if defined(TARGET_WINDOWS)
 445    drive = "C";
 446#elif defined(TARGET_POSIX)
 447    drive = "/";
 448#endif
 449  }
 450
 451#ifdef TARGET_WINDOWS_DESKTOP
 452  using KODI::PLATFORM::WINDOWS::ToW;
 453  UINT uidriveType = GetDriveType(ToW(drive + ":\\").c_str());
 454  if (uidriveType != DRIVE_UNKNOWN && uidriveType != DRIVE_NO_ROOT_DIR)
 455    total = space(drive + ":\\", ec);
 456#elif defined(TARGET_POSIX)
 457  total = space(drive, ec);
 458#endif
 459  if (ec.value() != 0)
 460    return false;
 461
 462  iTotal = static_cast<int>(total.capacity / MB);
 463  iTotalFree = static_cast<int>(total.free / MB);
 464  iTotalUsed = iTotal - iTotalFree;
 465  if (total.capacity > 0)
 466    iPercentUsed = static_cast<int>(100.0f * (total.capacity - total.free) / total.capacity + 0.5f);
 467  else
 468    iPercentUsed = 0;
 469
 470  iPercentFree = 100 - iPercentUsed;
 471
 472  return true;
 473}
 474
 475std::string CSysInfo::GetKernelName(bool emptyIfUnknown /*= false*/)
 476{
 477  static std::string kernelName;
 478  if (kernelName.empty())
 479  {
 480#if defined(TARGET_WINDOWS_DESKTOP)
 481    OSVERSIONINFOEXW osvi;
 482    if (sysGetVersionExWByRef(osvi) && osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
 483      kernelName = "Windows NT";
 484#elif defined(TARGET_WINDOWS_STORE)
 485    auto e = EasClientDeviceInformation();
 486    auto os = e.OperatingSystem();
 487    g_charsetConverter.wToUTF8(std::wstring(os.c_str()), kernelName);
 488#elif defined(TARGET_POSIX)
 489    struct utsname un;
 490    if (uname(&un) == 0)
 491      kernelName.assign(un.sysname);
 492#endif // defined(TARGET_POSIX)
 493
 494    if (kernelName.empty())
 495      kernelName = "Unknown kernel"; // can't detect
 496  }
 497
 498  if (emptyIfUnknown && kernelName == "Unknown kernel")
 499    return "";
 500
 501  return kernelName;
 502}
 503
 504std::string CSysInfo::GetKernelVersionFull(void)
 505{
 506  static std::string kernelVersionFull;
 507  if (!kernelVersionFull.empty())
 508    return kernelVersionFull;
 509
 510#if defined(TARGET_WINDOWS_DESKTOP)
 511  OSVERSIONINFOEXW osvi;
 512  if (sysGetVersionExWByRef(osvi))
 513    kernelVersionFull = StringUtils::Format("%d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
 514#elif  defined(TARGET_WINDOWS_STORE)
 515  // get the system version number
 516  auto sv = AnalyticsInfo::VersionInfo().DeviceFamilyVersion();
 517  wchar_t* end;
 518  unsigned long long  v = wcstoull(sv.c_str(), &end, 10);
 519  unsigned long long v1 = (v & 0xFFFF000000000000L) >> 48;
 520  unsigned long long v2 = (v & 0x0000FFFF00000000L) >> 32;
 521  unsigned long long v3 = (v & 0x00000000FFFF0000L) >> 16;
 522  kernelVersionFull = StringUtils::Format("%lld.%lld.%lld", v1, v2, v3);
 523
 524#elif defined(TARGET_POSIX)
 525  struct utsname un;
 526  if (uname(&un) == 0)
 527    kernelVersionFull.assign(un.release);
 528#endif // defined(TARGET_POSIX)
 529
 530  if (kernelVersionFull.empty())
 531    kernelVersionFull = "0.0.0"; // can't detect
 532
 533  return kernelVersionFull;
 534}
 535
 536std::string CSysInfo::GetKernelVersion(void)
 537{
 538  static std::string kernelVersionClear;
 539  if (kernelVersionClear.empty())
 540  {
 541    kernelVersionClear = GetKernelVersionFull();
 542    const size_t erasePos = kernelVersionClear.find_first_not_of("0123456789.");
 543    if (erasePos != std::string::npos)
 544      kernelVersionClear.erase(erasePos);
 545  }
 546
 547  return kernelVersionClear;
 548}
 549
 550std::string CSysInfo::GetOsName(bool emptyIfUnknown /* = false*/)
 551{
 552  static std::string osName;
 553  if (osName.empty())
 554  {
 555#if defined (TARGET_WINDOWS)
 556    osName = GetKernelName() + "-based OS";
 557#elif defined(TARGET_FREEBSD)
 558    osName = GetKernelName(true); // FIXME: for FreeBSD OS name is a kernel name
 559#elif defined(TARGET_DARWIN_IOS)
 560    osName = "iOS";
 561#elif defined(TARGET_DARWIN_TVOS)
 562    osName = "tvOS";
 563#elif defined(TARGET_DARWIN_OSX)
 564    osName = "OS X";
 565#elif defined (TARGET_ANDROID)
 566    osName = "Android";
 567#elif defined(TARGET_LINUX)
 568    osName = getValueFromOs_release("NAME");
 569    if (osName.empty())
 570      osName = getValueFromLsb_release(lsb_rel_distributor);
 571    if (osName.empty())
 572      osName = getValueFromOs_release("ID");
 573#endif // defined(TARGET_LINUX)
 574
 575    if (osName.empty())
 576      osName = "Unknown OS";
 577  }
 578
 579  if (emptyIfUnknown && osName == "Unknown OS")
 580    return "";
 581
 582  return osName;
 583}
 584
 585std::string CSysInfo::GetOsVersion(void)
 586{
 587  static std::string osVersion;
 588  if (!osVersion.empty())
 589    return osVersion;
 590
 591#if defined(TARGET_WINDOWS) || defined(TARGET_FREEBSD)
 592  osVersion = GetKernelVersion(); // FIXME: for Win32 and FreeBSD OS version is a kernel version
 593#elif defined(TARGET_DARWIN)
 594  osVersion = CDarwinUtils::GetVersionString();
 595#elif defined(TARGET_ANDROID)
 596  char versionCStr[PROP_VALUE_MAX];
 597  int propLen = __system_property_get("ro.build.version.release", versionCStr);
 598  osVersion.assign(versionCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
 599
 600  if (osVersion.empty() || std::string("0123456789").find(versionCStr[0]) == std::string::npos)
 601    osVersion.clear(); // can't correctly detect Android version
 602  else
 603  {
 604    size_t pointPos = osVersion.find('.');
 605    if (pointPos == std::string::npos)
 606      osVersion += ".0.0";
 607    else if (osVersion.find('.', pointPos + 1) == std::string::npos)
 608      osVersion += ".0";
 609  }
 610#elif defined(TARGET_LINUX)
 611  osVersion = getValueFromOs_release("VERSION_ID");
 612  if (osVersion.empty())
 613    osVersion = getValueFromLsb_release(lsb_rel_release);
 614#endif // defined(TARGET_LINUX)
 615
 616  if (osVersion.empty())
 617    osVersion = "0.0";
 618
 619  return osVersion;
 620}
 621
 622std::string CSysInfo::GetOsPrettyNameWithVersion(void)
 623{
 624  static std::string osNameVer;
 625  if (!osNameVer.empty())
 626    return osNameVer;
 627
 628#if defined (TARGET_WINDOWS_DESKTOP)
 629  OSVERSIONINFOEXW osvi = {};
 630
 631  osNameVer = "Windows ";
 632  if (sysGetVersionExWByRef(osvi))
 633  {
 634    switch (GetWindowsVersion())
 635    {
 636    case WindowsVersionWin7:
 637      if (osvi.wProductType == VER_NT_WORKSTATION)
 638        osNameVer.append("7");
 639      else
 640        osNameVer.append("Server 2008 R2");
 641      break;
 642    case WindowsVersionWin8:
 643      if (osvi.wProductType == VER_NT_WORKSTATION)
 644        osNameVer.append("8");
 645      else
 646        osNameVer.append("Server 2012");
 647      break;
 648    case WindowsVersionWin8_1:
 649      if (osvi.wProductType == VER_NT_WORKSTATION)
 650        osNameVer.append("8.1");
 651      else
 652        osNameVer.append("Server 2012 R2");
 653      break;
 654    case WindowsVersionWin10:
 655    case WindowsVersionWin10_FCU:
 656      if (osvi.wProductType == VER_NT_WORKSTATION)
 657        osNameVer.append("10");
 658      else
 659        osNameVer.append("Unknown future server version");
 660      break;
 661    case WindowsVersionFuture:
 662      osNameVer.append("Unknown future version");
 663      break;
 664    default:
 665      osNameVer.append("Unknown version");
 666      break;
 667    }
 668
 669    // Append Service Pack version if any
 670    if (osvi.wServicePackMajor > 0 || osvi.wServicePackMinor > 0)
 671    {
 672      osNameVer.append(StringUtils::Format(" SP%d", osvi.wServicePackMajor));
 673      if (osvi.wServicePackMinor > 0)
 674      {
 675        osNameVer.append(StringUtils::Format(".%d", osvi.wServicePackMinor));
 676      }
 677    }
 678  }
 679  else
 680    osNameVer.append(" unknown");
 681#elif defined(TARGET_WINDOWS_STORE)
 682  osNameVer = GetKernelName() + " " + GetOsVersion();
 683#elif defined(TARGET_FREEBSD) || defined(TARGET_DARWIN)
 684  osNameVer = GetOsName() + " " + GetOsVersion();
 685#elif defined(TARGET_ANDROID)
 686  osNameVer = GetOsName() + " " + GetOsVersion() + " API level " +   StringUtils::Format("%d", CJNIBuild::SDK_INT);
 687#elif defined(TARGET_LINUX)
 688  osNameVer = getValueFromOs_release("PRETTY_NAME");
 689  if (osNameVer.empty())
 690  {
 691    osNameVer = getValueFromLsb_release(lsb_rel_description);
 692    std::string osName(GetOsName(true));
 693    if (!osName.empty() && osNameVer.find(osName) == std::string::npos)
 694      osNameVer = osName + osNameVer;
 695    if (osNameVer.empty())
 696      osNameVer = "Unknown Linux Distribution";
 697  }
 698
 699  if (osNameVer.find(GetOsVersion()) == std::string::npos)
 700    osNameVer += " " + GetOsVersion();
 701#endif // defined(TARGET_LINUX)
 702
 703  if (osNameVer.empty())
 704    osNameVer = "Unknown OS Unknown version";
 705
 706  return osNameVer;
 707
 708}
 709
 710std::string CSysInfo::GetManufacturerName(void)
 711{
 712  static std::string manufName;
 713  static bool inited = false;
 714  if (!inited)
 715  {
 716#if defined(TARGET_ANDROID)
 717    char deviceCStr[PROP_VALUE_MAX];
 718    int propLen = __system_property_get("ro.product.manufacturer", deviceCStr);
 719    manufName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
 720#elif defined(TARGET_DARWIN)
 721    manufName = CDarwinUtils::GetManufacturer();
 722#elif defined(TARGET_WINDOWS_STORE)
 723    auto eas = EasClientDeviceInformation();
 724    auto manufacturer = eas.SystemManufacturer();
 725    g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), manufName);
 726#elif defined(TARGET_LINUX)
 727
 728    auto cpuInfo = CServiceBroker::GetCPUInfo();
 729    manufName = cpuInfo->GetCPUSoC();
 730
 731#elif defined(TARGET_WINDOWS)
 732    // We just don't care, might be useful on embedded
 733#endif
 734    inited = true;
 735  }
 736
 737  return manufName;
 738}
 739
 740std::string CSysInfo::GetModelName(void)
 741{
 742  static std::string modelName;
 743  static bool inited = false;
 744  if (!inited)
 745  {
 746#if defined(TARGET_ANDROID)
 747    char deviceCStr[PROP_VALUE_MAX];
 748    int propLen = __system_property_get("ro.product.model", deviceCStr);
 749    modelName.assign(deviceCStr, (propLen > 0 && propLen <= PROP_VALUE_MAX) ? propLen : 0);
 750#elif defined(TARGET_DARWIN_EMBEDDED)
 751    modelName = CDarwinUtils::getIosPlatformString();
 752#elif defined(TARGET_DARWIN_OSX)
 753    size_t nameLen = 0; // 'nameLen' should include terminating null
 754    if (sysctlbyname("hw.model", NULL, &nameLen, NULL, 0) == 0 && nameLen > 1)
 755    {
 756      XUTILS::auto_buffer buf(nameLen);
 757      if (sysctlbyname("hw.model", buf.get(), &nameLen, NULL, 0) == 0 && nameLen == buf.size())
 758        modelName.assign(buf.get(), nameLen - 1); // assign exactly 'nameLen-1' characters to 'modelName'
 759    }
 760#elif defined(TARGET_WINDOWS_STORE)
 761    auto eas = EasClientDeviceInformation();
 762    auto manufacturer = eas.SystemProductName();
 763    g_charsetConverter.wToUTF8(std::wstring(manufacturer.c_str()), modelName);
 764#elif defined(TARGET_LINUX)
 765    auto cpuInfo = CServiceBroker::GetCPUInfo();
 766    modelName = cpuInfo->GetCPUHardware();
 767#elif defined(TARGET_WINDOWS)
 768    // We just don't care, might be useful on embedded
 769#endif
 770    inited = true;
 771  }
 772
 773  return modelName;
 774}
 775
 776bool CSysInfo::IsAeroDisabled()
 777{
 778#ifdef TARGET_WINDOWS_STORE
 779  return true; // need to review https://msdn.microsoft.com/en-us/library/windows/desktop/aa969518(v=vs.85).aspx
 780#elif defined(TARGET_WINDOWS)
 781  BOOL aeroEnabled = FALSE;
 782  HRESULT res = DwmIsCompositionEnabled(&aeroEnabled);
 783  if (SUCCEEDED(res))
 784    return !aeroEnabled;
 785#endif
 786  return false;
 787}
 788
 789CSysInfo::WindowsVersion CSysInfo::m_WinVer = WindowsVersionUnknown;
 790
 791bool CSysInfo::IsWindowsVersion(WindowsVersion ver)
 792{
 793  if (ver == WindowsVersionUnknown)
 794    return false;
 795  return GetWindowsVersion() == ver;
 796}
 797
 798bool CSysInfo::IsWindowsVersionAtLeast(WindowsVersion ver)
 799{
 800  if (ver == WindowsVersionUnknown)
 801    return false;
 802  return GetWindowsVersion() >= ver;
 803}
 804
 805CSysInfo::WindowsVersion CSysInfo::GetWindowsVersion()
 806{
 807#ifdef TARGET_WINDOWS_DESKTOP
 808  if (m_WinVer == WindowsVersionUnknown)
 809  {
 810    OSVERSIONINFOEXW osvi = {};
 811    if (sysGetVersionExWByRef(osvi))
 812    {
 813      if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
 814        m_WinVer = WindowsVersionWin7;
 815      else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
 816        m_WinVer = WindowsVersionWin8;
 817      else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
 818        m_WinVer = WindowsVersionWin8_1;
 819      else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < 16299)
 820        m_WinVer = WindowsVersionWin10;
 821      else if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= 16299)
 822        m_WinVer = WindowsVersionWin10_FCU;
 823      /* Insert checks for new Windows versions here */
 824      else if ( (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion > 3) || osvi.dwMajorVersion > 10)
 825        m_WinVer = WindowsVersionFuture;
 826    }
 827  }
 828#elif  defined(TARGET_WINDOWS_STORE)
 829  m_WinVer = WindowsVersionWin10;
 830#endif // TARGET_WINDOWS
 831  return m_WinVer;
 832}
 833
 834int CSysInfo::GetKernelBitness(void)
 835{
 836  static int kernelBitness = -1;
 837  if (kernelBitness == -1)
 838  {
 839#ifdef TARGET_WINDOWS_STORE
 840    Package package = Package::Current();
 841    auto arch = package.Id().Architecture();
 842    switch (arch)
 843    {
 844    case ProcessorArchitecture::X86:
 845      kernelBitness = 32;
 846      break;
 847    case ProcessorArchitecture::X64:
 848      kernelBitness = 64;
 849      break;
 850    case ProcessorArchitecture::Arm:
 851      kernelBitness = 32;
 852      break;
 853    case ProcessorArchitecture::Unknown: // not sure what to do here. guess 32 for now
 854    case ProcessorArchitecture::Neutral:
 855      kernelBitness = 32;
 856      break;
 857    }
 858#elif defined(TARGET_WINDOWS_DESKTOP)
 859    SYSTEM_INFO si;
 860    GetNativeSystemInfo(&si);
 861    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
 862      kernelBitness = 32;
 863    else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
 864      kernelBitness = 64;
 865    else
 866    {
 867      BOOL isWow64 = FALSE;
 868      if (IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64) // fallback
 869        kernelBitness = 64;
 870    }
 871#elif defined(TARGET_DARWIN_EMBEDDED)
 872    // Note: OS X return x86 CPU type without CPU_ARCH_ABI64 flag
 873    const NXArchInfo* archInfo = NXGetLocalArchInfo();
 874    if (archInfo)
 875      kernelBitness = ((archInfo->cputype & CPU_ARCH_ABI64) != 0) ? 64 : 32;
 876#elif defined(TARGET_POSIX)
 877    struct utsname un;
 878    if (uname(&un) == 0)
 879    {
 880      std::string machine(un.machine);
 881      if (machine == "x86_64" || machine == "amd64" || machine == "arm64" || machine == "aarch64" || machine == "ppc64" ||
 882          machine == "ia64" || machine == "mips64" || machine == "s390x")
 883        kernelBitness = 64;
 884      else
 885        kernelBitness = 32;
 886    }
 887#endif
 888    if (kernelBitness == -1)
 889      kernelBitness = 0; // can't detect
 890  }
 891
 892  return kernelBitness;
 893}
 894
 895const std::string& CSysInfo::GetKernelCpuFamily(void)
 896{
 897  static std::string kernelCpuFamily;
 898  if (kernelCpuFamily.empty())
 899  {
 900#ifdef TARGET_WINDOWS
 901    SYSTEM_INFO si;
 902    GetNativeSystemInfo(&si);
 903    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ||
 904        si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
 905        kernelCpuFamily = "x86";
 906    else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
 907      kernelCpuFamily = "ARM";
 908#elif defined(TARGET_DARWIN)
 909    const NXArchInfo* archInfo = NXGetLocalArchInfo();
 910    if (archInfo)
 911    {
 912      const cpu_type_t cpuType = (archInfo->cputype & ~CPU_ARCH_ABI64); // get CPU family without 64-bit ABI flag
 913      if (cpuType == CPU_TYPE_I386)
 914        kernelCpuFamily = "x86";
 915      else if (cpuType == CPU_TYPE_ARM)
 916        kernelCpuFamily = "ARM";
 917#ifdef CPU_TYPE_MIPS
 918      else if (cpuType == CPU_TYPE_MIPS)
 919        kernelCpuFamily = "MIPS";
 920#endif // CPU_TYPE_MIPS
 921    }
 922#elif defined(TARGET_POSIX)
 923    struct utsname un;
 924    if (uname(&un) == 0)
 925    {
 926      std::string machine(un.machine);
 927      if (machine.compare(0, 3, "arm", 3) == 0 || machine.compare(0, 7, "aarch64", 7) == 0)
 928        kernelCpuFamily = "ARM";
 929      else if (machine.compare(0, 4, "mips", 4) == 0)
 930        kernelCpuFamily = "MIPS";
 931      else if (machine.compare(0, 4, "i686", 4) == 0 || machine == "i386" || machine == "amd64" ||  machine.compare(0, 3, "x86", 3) == 0)
 932        kernelCpuFamily = "x86";
 933      else if (machine.compare(0, 4, "s390", 4) == 0)
 934        kernelCpuFamily = "s390";
 935      else if (machine.compare(0, 3, "ppc", 3) == 0 || machine.compare(0, 5, "power", 5) == 0)
 936        kernelCpuFamily = "PowerPC";
 937    }
 938#endif
 939    if (kernelCpuFamily.empty())
 940      kernelCpuFamily = "unknown CPU family";
 941  }
 942  return kernelCpuFamily;
 943}
 944
 945int CSysInfo::GetXbmcBitness(void)
 946{
 947  return static_cast<int>(sizeof(void*) * 8);
 948}
 949
 950bool CSysInfo::HasInternet()
 951{
 952  if (m_info.internetState != CSysData::UNKNOWN)
 953    return m_info.internetState == CSysData::CONNECTED;
 954  return (m_info.internetState = CSysInfoJob::GetInternetState()) == CSysData::CONNECTED;
 955}
 956
 957std::string CSysInfo::GetHddSpaceInfo(int drive, bool shortText)
 958{
 959 int percent;
 960 return GetHddSpaceInfo( percent, drive, shortText);
 961}
 962
 963std::string CSysInfo::GetHddSpaceInfo(int& percent, int drive, bool shortText)
 964{
 965  int total, totalFree, totalUsed, percentFree, percentused;
 966  std::string strRet;
 967  percent = 0;
 968  if (g_sysinfo.GetDiskSpace("", total, totalFree, totalUsed, percentFree, percentused))
 969  {
 970    if (shortText)
 971    {
 972      switch(drive)
 973      {
 974        case SYSTEM_FREE_SPACE:
 975          percent = percentFree;
 976          break;
 977        case SYSTEM_USED_SPACE:
 978          percent = percentused;
 979          break;
 980      }
 981    }
 982    else
 983    {
 984      switch(drive)
 985      {
 986      case SYSTEM_FREE_SPACE:
 987        strRet = StringUtils::Format("%i MB %s", totalFree, g_localizeStrings.Get(160).c_str());
 988        break;
 989      case SYSTEM_USED_SPACE:
 990        strRet = StringUtils::Format("%i MB %s", totalUsed, g_localizeStrings.Get(20162).c_str());
 991        break;
 992      case SYSTEM_TOTAL_SPACE:
 993        strRet = StringUtils::Format("%i MB %s", total, g_localizeStrings.Get(20161).c_str());
 994        break;
 995      case SYSTEM_FREE_SPACE_PERCENT:
 996        strRet = StringUtils::Format("%i %% %s", percentFree, g_localizeStrings.Get(160).c_str());
 997        break;
 998      case SYSTEM_USED_SPACE_PERCENT:
 999        strRet = StringUtils::Format("%i %% %s", percentused, g_localizeStrings.Get(20162).c_str());
1000        break;
1001      }
1002    }
1003  }
1004  else
1005  {
1006    if (shortText)
1007      strRet = g_localizeStrings.Get(10006); // N/A
1008    else
1009      strRet = g_localizeStrings.Get(10005); // Not available
1010  }
1011  return strRet;
1012}
1013
1014std::string CSysInfo::GetUserAgent()
1015{
1016  static std::string result;
1017  if (!result.empty())
1018    return result;
1019
1020  result = GetAppName() + "/" + CSysInfo::GetVersionShort() + " (";
1021#if defined(TARGET_WINDOWS)
1022  result += GetKernelName() + " " + GetKernelVersion();
1023#ifndef TARGET_WINDOWS_STORE
1024  BOOL bIsWow = FALSE;
1025  if (IsWow64Process(GetCurrentProcess(), &bIsWow) && bIsWow)
1026      result.append("; WOW64");
1027  else
1028#endif
1029  {
1030    SYSTEM_INFO si = {};
1031    GetSystemInfo(&si);
1032    if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
1033      result.append("; Win64; x64");
1034    else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
1035      result.append("; Win64; IA64");
1036    else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM)
1037      result.append("; ARM");
1038  }
1039#elif defined(TARGET_DARWIN)
1040#if defined(TARGET_DARWIN_EMBEDDED)
1041  std::string iDevStr(GetModelName()); // device model name with number of model version
1042  size_t iDevStrDigit = iDevStr.find_first_of("0123456789");
1043  std::string iDev(iDevStr, 0, iDevStrDigit);  // device model name without number
1044  if (iDevStrDigit == 0)
1045    iDev = "unknown";
1046  result += iDev + "; ";
1047  std::string iOSVersion(GetOsVersion());
1048  size_t lastDotPos = iOSVersion.rfind('.');
1049  if (lastDotPos != std::string::npos && iOSVersion.find('.') != lastDotPos
1050      && iOSVersion.find_first_not_of('0', lastDotPos + 1) == std::string::npos)
1051    iOSVersion.erase(lastDotPos);
1052  StringUtils::Replace(iOSVersion, '.', '_');
1053  if (iDev == "AppleTV")
1054  {
1055    // check if it's ATV4 (AppleTV5,3) or later
1056    auto modelMajorNumberEndPos = iDevStr.find_first_of(',', iDevStrDigit);
1057    std::string s{iDevStr, iDevStrDigit, modelMajorNumberEndPos - iDevStrDigit};
1058    if (stoi(s) >= 5)
1059      result += "CPU TVOS";
1060    else
1061      result += "CPU OS";
1062  }
1063  else if (iDev == "iPad")
1064    result += "CPU OS";
1065  else
1066    result += "CPU iPhone OS ";
1067  result += iOSVersion + " like Mac OS X";
1068#else
1069  result += "Macintosh; ";
1070  std::string cpuFam(GetBuildTargetCpuFamily());
1071  if (cpuFam == "x86")
1072    result += "Intel ";
1073  result += "Mac OS X ";
1074  std::string OSXVersion(GetOsVersion());
1075  StringUtils::Replace(OSXVersion, '.', '_');
1076  result += OSXVersion;
1077#endif
1078#elif defined(TARGET_ANDROID)
1079  result += "Linux; Android ";
1080  std::string versionStr(GetOsVersion());
1081  const size_t verLen = versionStr.length();
1082  if (verLen >= 2 && versionStr.compare(verLen - 2, 2, ".0", 2) == 0)
1083    versionStr.erase(verLen - 2); // remove last ".0" if any
1084  result += versionStr;
1085  std::string deviceInfo(GetModelName());
1086
1087  char buildId[PROP_VALUE_MAX];
1088  int propLen = __system_property_get("ro.build.id", buildId);
1089  if (propLen > 0 && propLen <= PROP_VALUE_MAX)
1090  {
1091    if (!deviceInfo.empty())
1092      deviceInfo += " ";
1093    deviceInfo += "Build/";
1094    deviceInfo.append(buildId, propLen);
1095  }
1096
1097  if (!deviceInfo.empty())
1098    result += "; " + deviceInfo;
1099#elif defined(TARGET_POSIX)
1100  result += "X11; ";
1101  struct utsname un;
1102  if (uname(&un) == 0)
1103  {
1104    std::string cpuStr(un.machine);
1105    if (cpuStr == "x86_64" && GetXbmcBitness() == 32)
1106      cpuStr = "i686 (x86_64)";
1107    result += un.sysname;
1108    result += " ";
1109    result += cpuStr;
1110  }
1111  else
1112    result += "Unknown";
1113#else
1114  result += "Unknown";
1115#endif
1116  result += ")";
1117
1118  if (GetAppName() != "Kodi")
1119    result += " Kodi_Fork_" + GetAppName() + "/1.0"; // default fork number is '1.0', replace it with actual number if necessary
1120
1121#ifdef TARGET_LINUX
1122  // Add distribution name
1123  std::string linuxOSName(GetOsName(true));
1124  if (!linuxOSName.empty())
1125    result += " " + linuxOSName + "/" + GetOsVersion();
1126#endif
1127
1128#ifdef TARGET_RASPBERRY_PI
1129  result += " HW_RaspberryPi/1.0";
1130#elif defined (TARGET_DARWIN_EMBEDDED)
1131  std::string iDevVer;
1132  if (iDevStrDigit == std::string::npos)
1133    iDevVer = "0.0";
1134  else
1135    iDevVer.assign(iDevStr, iDevStrDigit, std::string::npos);
1136  StringUtils::Replace(iDevVer, ',', '.');
1137  result += " HW_" + iDev + "/" + iDevVer;
1138#endif
1139  // add more device IDs here if needed.
1140  // keep only one device ID in result! Form:
1141  // result += " HW_" + "deviceID" + "/" + "1.0"; // '1.0' if device has no version
1142
1143#if defined(TARGET_ANDROID)
1144  // Android has no CPU string by default, so add it as additional parameter
1145  struct utsname un1;
1146  if (uname(&un1) == 0)
1147  {
1148    std::string cpuStr(un1.machine);
1149    StringUtils::Replace(cpuStr, ' ', '_');
1150    result += " Sys_CPU/" + cpuStr;
1151  }
1152#endif
1153
1154  result += " App_Bitness/" + StringUtils::Format("%d", GetXbmcBitness());
1155
1156  std::string fullVer(CSysInfo::GetVersion());
1157  StringUtils::Replace(fullVer, ' ', '-');
1158  result += " Version/" + fullVer;
1159
1160  return result;
1161}
1162
1163std::string CSysInfo::GetDeviceName()
1164{
1165  std::string friendlyName = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SERVICES_DEVICENAME);
1166  if (StringUtils::EqualsNoCase(friendlyName, CCompileInfo::GetAppName()))
1167  {
1168    std::string hostname("[unknown]");
1169    CServiceBroker::GetNetwork().GetHostName(hostname);
1170    return StringUtils::Format("%s (%s)", friendlyName.c_str(), hostname.c_str());
1171  }
1172
1173  return friendlyName;
1174}
1175
1176// Version string MUST NOT contain spaces.  It is used
1177// in the HTTP request user agent.
1178std::string CSysInfo::GetVersionShort()
1179{
1180  if (strlen(CCompileInfo::GetSuffix()) == 0)
1181    return StringUtils::Format("%d.%d", CCompileInfo::GetMajor(), CCompileInfo::GetMinor());
1182  else
1183    return StringUtils::Format("%d.%d-%s", CCompileInfo::GetMajor(), CCompileInfo::GetMinor(), CCompileInfo::GetSuffix());
1184}
1185
1186std::string CSysInfo::GetVersion()
1187{
1188  return GetVersionShort() + " Git:" + CCompileInfo::GetSCMID();
1189}
1190
1191std::string CSysInfo::GetBuildDate()
1192{
1193  return CCompileInfo::GetBuildDate();
1194}
1195
1196std::string CSysInfo::GetBuildTargetPlatformName(void)
1197{
1198#if defined(TARGET_DARWIN_OSX)
1199  return "OS X";
1200#elif defined(TARGET_DARWIN_IOS)
1201  return "iOS";
1202#elif defined(TARGET_DARWIN_TVOS)
1203  return "tvOS";
1204#elif defined(TARGET_FREEBSD)
1205  return "FreeBSD";
1206#elif defined(TARGET_ANDROID)
1207  return "Android";
1208#elif defined(TARGET_LINUX)
1209  return "Linux";
1210#elif defined(TARGET_WINDOWS)
1211#ifdef NTDDI_VERSION
1212  return "Windows NT";
1213#else // !NTDDI_VERSION
1214  return "unknown Win32 platform";
1215#endif // !NTDDI_VERSION
1216#else
1217  return "unknown platform";
1218#endif
1219}
1220
1221std::string CSysInfo::GetBuildTargetPlatformVersion(void)
1222{
1223#if defined(TARGET_DARWIN_OSX)
1224  return XSTR_MACRO(__MAC_OS_X_VERSION_MIN_REQUIRED);
1225#elif defined(TARGET_DARWIN_IOS)
1226  return XSTR_MACRO(__IPHONE_OS_VERSION_MIN_REQUIRED);
1227#elif defined(TARGET_DARWIN_TVOS)
1228  return XSTR_MACRO(__TV_OS_VERSION_MIN_REQUIRED);
1229#elif defined(TARGET_FREEBSD)
1230  return XSTR_MACRO(__FreeBSD_version);
1231#elif defined(TARGET_ANDROID)
1232  return "API level " XSTR_MACRO(__ANDROID_API__);
1233#elif defined(TARGET_LINUX)
1234  return XSTR_MACRO(LINUX_VERSION_CODE);
1235#elif defined(TARGET_WINDOWS)
1236#ifdef NTDDI_VERSION
1237  return XSTR_MACRO(NTDDI_VERSION);
1238#else // !NTDDI_VERSION
1239  return "(unknown Win32 platform)";
1240#endif // !NTDDI_VERSION
1241#else
1242  return "(unknown platform)";
1243#endif
1244}
1245
1246std::string CSysInfo::GetBuildTargetPlatformVersionDecoded(void)
1247{
1248#if defined(TARGET_DARWIN_OSX)
1249  if (__MAC_OS_X_VERSION_MIN_REQUIRED % 100)
1250    return StringUtils::Format("version %d.%d.%d", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
1251                               (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100,
1252                               __MAC_OS_X_VERSION_MIN_REQUIRED % 100);
1253  else
1254    return StringUtils::Format("version %d.%d", __MAC_OS_X_VERSION_MIN_REQUIRED / 10000,
1255                               (__MAC_OS_X_VERSION_MIN_REQUIRED / 100) % 100);
1256#elif defined(TARGET_DARWIN_EMBEDDED)
1257  std::string versionStr = GetBuildTargetPlatformVersion();
1258  static const int major = (std::stoi(versionStr) / 10000) % 100;
1259  static const int minor = (std::stoi(versionStr) / 100) % 100;
1260  static const int rev = std::stoi(versionStr) % 100;
1261  return StringUtils::Format("version %d.%d.%d", major, minor, rev);
1262#elif defined(TARGET_FREEBSD)
1263  // FIXME: should works well starting from FreeBSD 8.1
1264  static const int major = (__FreeBSD_version / 100000) % 100;
1265  static const int minor = (__FreeBSD_version / 1000) % 100;
1266  static const int Rxx = __FreeBSD_version % 1000;
1267  if ((major < 9 && Rxx == 0))
1268    return StringUtils::Format("version %d.%d-RELEASE", major, minor);
1269  if (Rxx >= 500)
1270    return StringUtils::Format("version %d.%d-STABLE", major, minor);
1271
1272  return StringUtils::Format("version %d.%d-CURRENT", major, minor);
1273#elif defined(TARGET_ANDROID)
1274  return "API level " XSTR_MACRO(__ANDROID_API__);
1275#elif defined(TARGET_LINUX)
1276  return StringUtils::Format("version %d.%d.%d", (LINUX_VERSION_CODE >> 16) & 0xFF , (LINUX_VERSION_CODE >> 8) & 0xFF, LINUX_VERSION_CODE & 0xFF);
1277#elif defined(TARGET_WINDOWS)
1278#ifdef NTDDI_VERSION
1279  std::string version(StringUtils::Format("version %d.%d", int(NTDDI_VERSION >> 24) & 0xFF, int(NTDDI_VERSION >> 16) & 0xFF));
1280  if (SPVER(NTDDI_VERSION))
1281    version += StringUtils::Format(" SP%d", int(SPVER(NTDDI_VERSION)));
1282  return version;
1283#else // !NTDDI_VERSION
1284  return "(unknown Win32 platform)";
1285#endif // !NTDDI_VERSION
1286#else
1287  return "(unknown platform)";
1288#endif
1289}
1290
1291std::string CSysInfo::GetBuildTargetCpuFamily(void)
1292{
1293#if defined(__thumb__) || defined(_M_ARMT)
1294  return "ARM (Thumb)";
1295#elif defined(__arm__) || defined(_M_ARM) || defined (__aarch64__)
1296  return "ARM";
1297#elif defined(__mips__) || defined(mips) || defined(__mips)
1298  return "MIPS";
1299#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) || \
1300   defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(_X86_)
1301  return "x86";
1302#elif defined(__s390x__)
1303  return "s390";
1304#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC)
1305  return "PowerPC";
1306#else
1307  return "unknown CPU family";
1308#endif
1309}
1310
1311std::string CSysInfo::GetUsedCompilerNameAndVer(void)
1312{
1313#if defined(__clang__)
1314#ifdef __clang_version__
1315  return "Clang " __clang_version__;
1316#else // ! __clang_version__
1317  return "Clang " XSTR_MACRO(__clang_major__) "." XSTR_MACRO(__clang_minor__) "." XSTR_MACRO(__clang_patchlevel__);
1318#endif //! __clang_version__
1319#elif defined (__INTEL_COMPILER)
1320  return "Intel Compiler " XSTR_MACRO(__INTEL_COMPILER);
1321#elif defined (__GNUC__)
1322  std::string compilerStr;
1323#ifdef __llvm__
1324  /* Note: this will not detect GCC + DragonEgg */
1325  compilerStr = "llvm-gcc ";
1326#else // __llvm__
1327  compilerStr = "GCC ";
1328#endif // !__llvm__
1329  compilerStr += XSTR_MACRO(__GNUC__) "." XSTR_MACRO(__GNUC_MINOR__) "." XSTR_MACRO(__GNUC_PATCHLEVEL__);
1330  return compilerStr;
1331#elif defined (_MSC_VER)
1332  return "MSVC " XSTR_MACRO(_MSC_FULL_VER);
1333#else
1334  return "unknown compiler";
1335#endif
1336}
1337
1338std::string CSysInfo::GetPrivacyPolicy()
1339{
1340  if (m_privacyPolicy.empty())
1341  {
1342    CFile file;
1343    XFILE::auto_buffer buf;
1344    if (file.LoadFile("special://xbmc/privacy-policy.txt", buf) > 0)
1345    {
1346      std::string strBuf(buf.get(), buf.length());
1347      m_privacyPolicy = strBuf;
1348    }
1349    else
1350      m_privacyPolicy = g_localizeStrings.Get(19055);
1351  }
1352  return m_privacyPolicy;
1353}
1354
1355CSysInfo::WindowsDeviceFamily CSysInfo::GetWindowsDeviceFamily()
1356{
1357#ifdef TARGET_WINDOWS_STORE
1358  auto familyName = AnalyticsInfo::VersionInfo().DeviceFamily();
1359  if (familyName == L"Windows.Desktop")
1360    return WindowsDeviceFamily::Desktop;
1361  else if (familyName == L"Windows.Mobile")
1362    return WindowsDeviceFamily::Mobile;
1363  else if (familyName == L"Windows.Universal")
1364    return WindowsDeviceFamily::IoT;
1365  else if (familyName == L"Windows.Team")
1366    return WindowsDeviceFamily::Surface;
1367  else if (familyName == L"Windows.Xbox")
1368    return WindowsDeviceFamily::Xbox;
1369  else
1370    return WindowsDeviceFamily::Other;
1371#endif // TARGET_WINDOWS_STORE
1372  return WindowsDeviceFamily::Desktop;
1373}
1374
1375CJob *CSysInfo::GetJob() const
1376{
1377  return new CSysInfoJob();
1378}
1379
1380void CSysInfo::OnJobComplete(unsigned int jobID, bool success, CJob *job)
1381{
1382  m_info = static_cast<CSysInfoJob*>(job)->GetData();
1383  CInfoLoader::OnJobComplete(jobID, success, job);
1384}