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