michael@0: /* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "nsSystemInfo.h" michael@0: #include "prsystem.h" michael@0: #include "prio.h" michael@0: #include "prprf.h" michael@0: #include "mozilla/SSE.h" michael@0: #include "mozilla/arm.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include michael@0: #include "base/scoped_handle_win.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidBridge.h" michael@0: using namespace mozilla::widget::android; michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef ANDROID michael@0: extern "C" { michael@0: NS_EXPORT int android_sdk_version; michael@0: } michael@0: #endif michael@0: michael@0: // Slot for NS_InitXPCOM2 to pass information to nsSystemInfo::Init. michael@0: // Only set to nonzero (potentially) if XP_UNIX. On such systems, the michael@0: // system call to discover the appropriate value is not thread-safe, michael@0: // so we must call it before going multithreaded, but nsSystemInfo::Init michael@0: // only happens well after that point. michael@0: uint32_t nsSystemInfo::gUserUmask = 0; michael@0: michael@0: #if defined(XP_WIN) michael@0: namespace { michael@0: nsresult GetHDDInfo(const char* aSpecialDirName, nsAutoCString& aModel, michael@0: nsAutoCString& aRevision) michael@0: { michael@0: aModel.Truncate(); michael@0: aRevision.Truncate(); michael@0: michael@0: nsCOMPtr profDir; michael@0: nsresult rv = NS_GetSpecialDirectory(aSpecialDirName, michael@0: getter_AddRefs(profDir)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsAutoString profDirPath; michael@0: rv = profDir->GetPath(profDirPath); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: wchar_t volumeMountPoint[MAX_PATH] = {L'\\', L'\\', L'.', L'\\'}; michael@0: const size_t PREFIX_LEN = 4; michael@0: if (!::GetVolumePathNameW(profDirPath.get(), volumeMountPoint + PREFIX_LEN, michael@0: mozilla::ArrayLength(volumeMountPoint) - michael@0: PREFIX_LEN)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: size_t volumeMountPointLen = wcslen(volumeMountPoint); michael@0: // Since we would like to open a drive and not a directory, we need to michael@0: // remove any trailing backslash. A drive handle is valid for michael@0: // DeviceIoControl calls, a directory handle is not. michael@0: if (volumeMountPoint[volumeMountPointLen - 1] == L'\\') { michael@0: volumeMountPoint[volumeMountPointLen - 1] = L'\0'; michael@0: } michael@0: ScopedHandle handle(::CreateFileW(volumeMountPoint, 0, michael@0: FILE_SHARE_READ | FILE_SHARE_WRITE, michael@0: nullptr, OPEN_EXISTING, 0, nullptr)); michael@0: if (!handle.IsValid()) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: STORAGE_PROPERTY_QUERY queryParameters = {StorageDeviceProperty, michael@0: PropertyStandardQuery}; michael@0: STORAGE_DEVICE_DESCRIPTOR outputHeader = {sizeof(STORAGE_DEVICE_DESCRIPTOR)}; michael@0: DWORD bytesRead = 0; michael@0: if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, michael@0: &queryParameters, sizeof(queryParameters), michael@0: &outputHeader, sizeof(outputHeader), &bytesRead, michael@0: nullptr)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: PSTORAGE_DEVICE_DESCRIPTOR deviceOutput = michael@0: (PSTORAGE_DEVICE_DESCRIPTOR)malloc(outputHeader.Size); michael@0: if (!::DeviceIoControl(handle, IOCTL_STORAGE_QUERY_PROPERTY, michael@0: &queryParameters, sizeof(queryParameters), michael@0: deviceOutput, outputHeader.Size, &bytesRead, michael@0: nullptr)) { michael@0: free(deviceOutput); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: // Some HDDs are including product ID info in the vendor field. Since PNP michael@0: // IDs include vendor info and product ID concatenated together, we'll do michael@0: // that here and interpret the result as a unique ID for the HDD model. michael@0: if (deviceOutput->VendorIdOffset) { michael@0: aModel = reinterpret_cast(deviceOutput) + michael@0: deviceOutput->VendorIdOffset; michael@0: } michael@0: if (deviceOutput->ProductIdOffset) { michael@0: aModel += reinterpret_cast(deviceOutput) + michael@0: deviceOutput->ProductIdOffset; michael@0: } michael@0: aModel.CompressWhitespace(); michael@0: if (deviceOutput->ProductRevisionOffset) { michael@0: aRevision = reinterpret_cast(deviceOutput) + michael@0: deviceOutput->ProductRevisionOffset; michael@0: aRevision.CompressWhitespace(); michael@0: } michael@0: free(deviceOutput); michael@0: return NS_OK; michael@0: } michael@0: } // anonymous namespace michael@0: #endif // defined(XP_WIN) michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsSystemInfo::nsSystemInfo() michael@0: { michael@0: } michael@0: michael@0: nsSystemInfo::~nsSystemInfo() michael@0: { michael@0: } michael@0: michael@0: // CPU-specific information. michael@0: static const struct PropItems { michael@0: const char *name; michael@0: bool (*propfun)(void); michael@0: } cpuPropItems[] = { michael@0: // x86-specific bits. michael@0: { "hasMMX", mozilla::supports_mmx }, michael@0: { "hasSSE", mozilla::supports_sse }, michael@0: { "hasSSE2", mozilla::supports_sse2 }, michael@0: { "hasSSE3", mozilla::supports_sse3 }, michael@0: { "hasSSSE3", mozilla::supports_ssse3 }, michael@0: { "hasSSE4A", mozilla::supports_sse4a }, michael@0: { "hasSSE4_1", mozilla::supports_sse4_1 }, michael@0: { "hasSSE4_2", mozilla::supports_sse4_2 }, michael@0: // ARM-specific bits. michael@0: { "hasEDSP", mozilla::supports_edsp }, michael@0: { "hasARMv6", mozilla::supports_armv6 }, michael@0: { "hasARMv7", mozilla::supports_armv7 }, michael@0: { "hasNEON", mozilla::supports_neon } michael@0: }; michael@0: michael@0: nsresult michael@0: nsSystemInfo::Init() michael@0: { michael@0: nsresult rv; michael@0: michael@0: static const struct { michael@0: PRSysInfo cmd; michael@0: const char *name; michael@0: } items[] = { michael@0: { PR_SI_SYSNAME, "name" }, michael@0: { PR_SI_HOSTNAME, "host" }, michael@0: { PR_SI_ARCHITECTURE, "arch" }, michael@0: { PR_SI_RELEASE, "version" } michael@0: }; michael@0: michael@0: for (uint32_t i = 0; i < (sizeof(items) / sizeof(items[0])); i++) { michael@0: char buf[SYS_INFO_BUFFER_LENGTH]; michael@0: if (PR_GetSystemInfo(items[i].cmd, buf, sizeof(buf)) == PR_SUCCESS) { michael@0: rv = SetPropertyAsACString(NS_ConvertASCIItoUTF16(items[i].name), michael@0: nsDependentCString(buf)); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: else { michael@0: NS_WARNING("PR_GetSystemInfo failed"); michael@0: } michael@0: } michael@0: michael@0: #if defined(XP_WIN) && defined(MOZ_METRO) michael@0: // Create "hasWindowsTouchInterface" property. michael@0: nsAutoString version; michael@0: rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), version); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: double versionDouble = version.ToDouble(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"), michael@0: versionDouble >= 6.2); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: #else michael@0: rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16("hasWindowsTouchInterface"), false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: #endif michael@0: michael@0: // Additional informations not available through PR_GetSystemInfo. michael@0: SetInt32Property(NS_LITERAL_STRING("pagesize"), PR_GetPageSize()); michael@0: SetInt32Property(NS_LITERAL_STRING("pageshift"), PR_GetPageShift()); michael@0: SetInt32Property(NS_LITERAL_STRING("memmapalign"), PR_GetMemMapAlignment()); michael@0: SetInt32Property(NS_LITERAL_STRING("cpucount"), PR_GetNumberOfProcessors()); michael@0: SetUint64Property(NS_LITERAL_STRING("memsize"), PR_GetPhysicalMemorySize()); michael@0: SetUint32Property(NS_LITERAL_STRING("umask"), nsSystemInfo::gUserUmask); michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(cpuPropItems); i++) { michael@0: rv = SetPropertyAsBool(NS_ConvertASCIItoUTF16(cpuPropItems[i].name), michael@0: cpuPropItems[i].propfun()); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: BOOL isWow64; michael@0: BOOL gotWow64Value = IsWow64Process(GetCurrentProcess(), &isWow64); michael@0: NS_WARN_IF_FALSE(gotWow64Value, "IsWow64Process failed"); michael@0: if (gotWow64Value) { michael@0: rv = SetPropertyAsBool(NS_LITERAL_STRING("isWow64"), !!isWow64); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: nsAutoCString hddModel, hddRevision; michael@0: if (NS_SUCCEEDED(GetHDDInfo(NS_APP_USER_PROFILE_50_DIR, hddModel, michael@0: hddRevision))) { michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDModel"), hddModel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("profileHDDRevision"), michael@0: hddRevision); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: if (NS_SUCCEEDED(GetHDDInfo(NS_GRE_DIR, hddModel, hddRevision))) { michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDModel"), hddModel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("binHDDRevision"), michael@0: hddRevision); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: if (NS_SUCCEEDED(GetHDDInfo(NS_WIN_WINDOWS_DIR, hddModel, hddRevision))) { michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDModel"), hddModel); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("winHDDRevision"), michael@0: hddRevision); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: // This must be done here because NSPR can only separate OS's when compiled, not libraries. michael@0: char* gtkver = PR_smprintf("GTK %u.%u.%u", gtk_major_version, gtk_minor_version, gtk_micro_version); michael@0: if (gtkver) { michael@0: rv = SetPropertyAsACString(NS_LITERAL_STRING("secondaryLibrary"), michael@0: nsDependentCString(gtkver)); michael@0: PR_smprintf_free(gtkver); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) michael@0: return rv; michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: if (mozilla::AndroidBridge::Bridge()) { michael@0: nsAutoString str; michael@0: if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MODEL", str)) michael@0: SetPropertyAsAString(NS_LITERAL_STRING("device"), str); michael@0: if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", str)) michael@0: SetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), str); michael@0: if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "RELEASE", str)) michael@0: SetPropertyAsAString(NS_LITERAL_STRING("release_version"), str); michael@0: int32_t version; michael@0: if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &version)) michael@0: version = 0; michael@0: android_sdk_version = version; michael@0: if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) michael@0: SetPropertyAsAString(NS_LITERAL_STRING("hardware"), str); michael@0: bool isTablet = mozilla::widget::android::GeckoAppShell::IsTablet(); michael@0: SetPropertyAsBool(NS_LITERAL_STRING("tablet"), isTablet); michael@0: // NSPR "version" is the kernel version. For Android we want the Android version. michael@0: // Rename SDK version to version and put the kernel version into kernel_version. michael@0: rv = GetPropertyAsAString(NS_LITERAL_STRING("version"), str); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: SetPropertyAsAString(NS_LITERAL_STRING("kernel_version"), str); michael@0: } michael@0: SetPropertyAsInt32(NS_LITERAL_STRING("version"), android_sdk_version); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: char sdk[PROP_VALUE_MAX], characteristics[PROP_VALUE_MAX]; michael@0: if (__system_property_get("ro.build.version.sdk", sdk)) michael@0: android_sdk_version = atoi(sdk); michael@0: if (__system_property_get("ro.build.characteristics", characteristics)) { michael@0: if (!strcmp(characteristics, "tablet")) michael@0: SetPropertyAsBool(NS_LITERAL_STRING("tablet"), true); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSystemInfo::SetInt32Property(const nsAString &aPropertyName, michael@0: const int32_t aValue) michael@0: { michael@0: NS_WARN_IF_FALSE(aValue > 0, "Unable to read system value"); michael@0: if (aValue > 0) { michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: SetPropertyAsInt32(aPropertyName, aValue); michael@0: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to set property"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSystemInfo::SetUint32Property(const nsAString &aPropertyName, michael@0: const uint32_t aValue) michael@0: { michael@0: // Only one property is currently set via this function. michael@0: // It may legitimately be zero. michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: SetPropertyAsUint32(aPropertyName, aValue); michael@0: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to set property"); michael@0: } michael@0: michael@0: void michael@0: nsSystemInfo::SetUint64Property(const nsAString &aPropertyName, michael@0: const uint64_t aValue) michael@0: { michael@0: NS_WARN_IF_FALSE(aValue > 0, "Unable to read system value"); michael@0: if (aValue > 0) { michael@0: #ifdef DEBUG michael@0: nsresult rv = michael@0: #endif michael@0: SetPropertyAsUint64(aPropertyName, aValue); michael@0: NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to set property"); michael@0: } michael@0: }