| // Copyright 2017 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <stdlib.h> |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "starboard/common/device_type.h" |
| #include "starboard/common/log.h" |
| #include "starboard/common/string.h" |
| #include "starboard/configuration_constants.h" |
| #include "starboard/memory.h" |
| #include "starboard/shared/uwp/application_uwp.h" |
| #include "starboard/shared/uwp/keys.h" |
| #include "starboard/shared/win32/wchar_utils.h" |
| #include "starboard/system.h" |
| #include "starboard/xb1/shared/internal_shims.h" |
| #include "starboard/xb1/system_properties.h" |
| |
| using starboard::shared::win32::platformStringToString; |
| using Windows::Security::ExchangeActiveSyncProvisioning:: |
| EasClientDeviceInformation; |
| using Windows::System::Profile::AnalyticsInfo; |
| using Windows::System::Profile::AnalyticsVersionInfo; |
| using Windows::System::UserProfile::AdvertisingManager; |
| |
| namespace { |
| |
| #define arraysize(array) (sizeof(array) / sizeof(*array)) |
| |
| struct UwpDevice { |
| const char* deviceForm; |
| const char* chipsetModel; |
| const char* model; |
| const char* year; |
| } UwpDevices; |
| |
| // Array of model name and year for known UWP devices. |
| struct UwpDevice kDevices[] = { |
| {"Xbox One", "XboxOne", "XboxOne", "2013"}, |
| {"Xbox One S", "XboxOne", "XboxOne S", "2016"}, |
| {"Xbox One X", "XboxOne", "XboxOne X", "2017"}, |
| {"Xbox One X DevKit", "XboxOne", "XboxOne X", "2017"}, |
| {"Xbox Series X", "XboxScarlett", "XboxScarlett Series X", "2020"}, |
| {"Xbox Series X Devkit", "XboxScarlett", "XboxScarlett Series X", "2020"}, |
| {"Xbox Series S", "XboxScarlett", "XboxScarlett Series S", "2020"}, |
| }; |
| |
| const char kSystemIntegrator[] = "YouTube"; |
| const char kXboxDeviceFormField[] = "Xbox"; |
| |
| // Year for unknown Uwp devices. This assumes that they will be as |
| // capable as the most recent known device. |
| const char kUnknownModelYear[] = "2020"; |
| |
| // Chipset for unidentified device forms. |
| const char kUnknownChipset[] = "UwpUnknown"; |
| |
| bool CopyStringAndTestIfSuccess(char* out_value, |
| int value_length, |
| const char* from_value) { |
| if (strlen(from_value) + 1 > value_length) |
| return false; |
| starboard::strlcpy(out_value, from_value, value_length); |
| return true; |
| } |
| |
| bool CopyStringAndTestIfSuccess(char* out_value, |
| int value_length, |
| const wchar_t* from_value) { |
| char* from_value_str = new char[value_length]; |
| int len = wcstombs(from_value_str, from_value, value_length); |
| bool result = len < 0 ? false |
| : CopyStringAndTestIfSuccess(out_value, value_length, |
| from_value_str); |
| delete from_value_str; |
| return result; |
| } |
| |
| const std::size_t kOsVersionSize = 128; |
| |
| struct WindowsVersion { |
| uint16_t major_version; |
| uint16_t minor_version; |
| uint16_t build_version; |
| uint16_t revision; |
| }; |
| |
| bool GetWindowsVersion(WindowsVersion* version) { |
| SB_DCHECK(version); |
| AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo; |
| std::string device_family_version = |
| platformStringToString(version_info->DeviceFamilyVersion); |
| if (device_family_version.empty()) { |
| return false; |
| } |
| uint64_t version_info_all = |
| strtoull(device_family_version.c_str(), nullptr, 10); |
| if (version_info_all == 0) { |
| return false; |
| } |
| version->major_version = (version_info_all >> 48) & 0xFFFF; |
| version->minor_version = (version_info_all >> 32) & 0xFFFF; |
| version->build_version = (version_info_all >> 16) & 0xFFFF; |
| version->revision = version_info_all & 0xFFFF; |
| return true; |
| } |
| |
| std::string GetDeviceForm() { |
| Platform::String ^ device_form = AnalyticsInfo::DeviceForm; |
| return platformStringToString(device_form); |
| } |
| |
| bool GetBrandName(char* out_value, int value_length) { |
| EasClientDeviceInformation ^ current_device_info = |
| ref new EasClientDeviceInformation(); |
| std::string brand_name = |
| platformStringToString(current_device_info->SystemManufacturer); |
| if (brand_name.empty()) { |
| return false; |
| } |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| brand_name.c_str()); |
| } |
| |
| bool GetChipsetModelNumber(char* out_value, int value_length) { |
| std::string deviceForm = GetDeviceForm(); |
| // If the device form is a known device, return the chipset model from the |
| // table. |
| for (size_t i = 0; i < arraysize(kDevices); i++) { |
| const UwpDevice* device = kDevices + i; |
| if (deviceForm == device->deviceForm) { |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| device->chipsetModel); |
| } |
| } |
| |
| // The device form is not a known value, return unknown chipset. |
| return CopyStringAndTestIfSuccess(out_value, value_length, kUnknownChipset); |
| } |
| |
| bool GetFirmwareVersion(char* out_value, int value_length) { |
| WindowsVersion version = {0}; |
| if (!GetWindowsVersion(&version)) { |
| return false; |
| } |
| // The caller expects that the the output string will only be written if |
| // when true is returned. Therefore we have to buffer the string in case |
| // there is a false condition, such as small memory input size to hold |
| // the output parameter. |
| std::vector<char> out_path_copy(kSbFileMaxPath + 1, 0); |
| int len = std::min<int>(kSbFileMaxPath, value_length); |
| int return_value = SbStringFormatF( |
| out_path_copy.data(), len, "%u.%u.%u.%u", version.major_version, |
| version.minor_version, version.build_version, version.revision); |
| |
| const bool ok = ((return_value > 0) && (return_value < value_length)); |
| if (ok) { |
| starboard::strlcpy(out_value, out_path_copy.data(), len); |
| } |
| return ok; |
| } |
| |
| bool GetFriendlyName(char* out_value, int value_length) { |
| EasClientDeviceInformation ^ current_device_info = |
| ref new EasClientDeviceInformation(); |
| std::string friendly_name = |
| platformStringToString(current_device_info->FriendlyName); |
| if (friendly_name.empty()) { |
| return false; |
| } |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| friendly_name.c_str()); |
| } |
| |
| bool GetModelYear(char* out_value, int value_length) { |
| std::string deviceForm = GetDeviceForm(); |
| for (size_t i = 0; i < arraysize(kDevices); i++) { |
| const UwpDevice* device = kDevices + i; |
| if (deviceForm == device->deviceForm) { |
| return CopyStringAndTestIfSuccess(out_value, value_length, device->year); |
| } |
| } |
| |
| return CopyStringAndTestIfSuccess(out_value, value_length, kUnknownModelYear); |
| } |
| |
| bool GetModelName(char* out_value, int value_length) { |
| std::string deviceForm = GetDeviceForm(); |
| for (size_t i = 0; i < arraysize(kDevices); i++) { |
| const UwpDevice* device = kDevices + i; |
| if (deviceForm == device->deviceForm) { |
| return CopyStringAndTestIfSuccess(out_value, value_length, device->model); |
| } |
| } |
| |
| // The device form is not a known value, return the device form verbatim. |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| deviceForm.c_str()); |
| } |
| |
| bool GetPlatformName(char* out_value, int value_length) { |
| EasClientDeviceInformation ^ current_device_info = |
| ref new EasClientDeviceInformation(); |
| std::string operating_system = |
| platformStringToString(current_device_info->OperatingSystem); |
| |
| AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo; |
| std::string os_name_and_version = |
| platformStringToString(current_device_info->OperatingSystem); |
| if (os_name_and_version.empty()) { |
| return false; |
| } |
| |
| WindowsVersion os_version; |
| if (!GetWindowsVersion(&os_version)) { |
| return false; |
| } |
| |
| os_name_and_version += " "; |
| char os_version_buffer[kOsVersionSize]; |
| os_version_buffer[0] = '\0'; |
| |
| int return_value = |
| SbStringFormatF(os_version_buffer, value_length, "%u.%u", |
| os_version.major_version, os_version.minor_version); |
| if ((return_value < 0) || (return_value >= value_length)) { |
| return false; |
| } |
| |
| os_name_and_version.append(os_version_buffer); |
| |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| os_name_and_version.c_str()); |
| } |
| |
| bool GetAppXVersion(char* out_value, int value_length) { |
| Windows::ApplicationModel::PackageVersion version = |
| Windows::ApplicationModel::Package::Current->Id->Version; |
| std::stringstream version_string; |
| version_string << version.Major << '.' << version.Minor << '.' |
| << version.Build << '.' << version.Revision; |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| version_string.str().c_str()); |
| } |
| |
| std::string GetAdvertisingId() { |
| Platform::String ^ advertising_id = AdvertisingManager::AdvertisingId; |
| return platformStringToString(advertising_id); |
| } |
| |
| bool GetDeviceType(char* out_value, int value_length) { |
| AnalyticsVersionInfo ^ version_info = AnalyticsInfo::VersionInfo; |
| std::string family = starboard::shared::win32::platformStringToString( |
| version_info->DeviceFamily); |
| std::string device_type; |
| if (family.compare("Windows.Desktop") == 0) { |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| starboard::kSystemDeviceTypeDesktopPC); |
| } |
| if (family.compare("Windows.Xbox") == 0) { |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| starboard::kSystemDeviceTypeGameConsole); |
| } |
| SB_NOTREACHED(); |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| starboard::kSystemDeviceTypeUnknown); |
| } |
| |
| } // namespace |
| |
| bool SbSystemGetProperty(SbSystemPropertyId property_id, |
| char* out_value, |
| int value_length) { |
| if (!out_value || !value_length) { |
| return false; |
| } |
| |
| using starboard::shared::uwp::SpeechApiKey; |
| |
| switch (property_id) { |
| case kSbSystemPropertyCertificationScope: { |
| Platform::String ^ scope = starboard::xb1::shared::GetCertScope(); |
| if (scope->IsEmpty()) { |
| if (kCertificationScope[0] == '\0') |
| return false; |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| kCertificationScope); |
| } |
| bool result = |
| CopyStringAndTestIfSuccess(out_value, value_length, scope->Data()); |
| return result; |
| } |
| case kSbSystemPropertyChipsetModelNumber: |
| return GetChipsetModelNumber(out_value, value_length); |
| case kSbSystemPropertyFirmwareVersion: |
| return GetFirmwareVersion(out_value, value_length); |
| case kSbSystemPropertyFriendlyName: |
| return GetFriendlyName(out_value, value_length); |
| case kSbSystemPropertyBrandName: |
| return GetBrandName(out_value, value_length); |
| case kSbSystemPropertyModelName: |
| return GetModelName(out_value, value_length); |
| case kSbSystemPropertyModelYear: |
| return GetModelYear(out_value, value_length); |
| case kSbSystemPropertySystemIntegratorName: |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| kSystemIntegrator); |
| case kSbSystemPropertyPlatformName: |
| return GetPlatformName(out_value, value_length); |
| case kSbSystemPropertySpeechApiKey: |
| CopyStringAndTestIfSuccess(out_value, value_length, SpeechApiKey()); |
| return true; |
| case kSbSystemPropertyUserAgentAuxField: |
| return GetAppXVersion(out_value, value_length); |
| #if SB_API_VERSION >= 14 |
| case kSbSystemPropertyAdvertisingId: { |
| std::string advertising_id = GetAdvertisingId(); |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| advertising_id.c_str()); |
| } |
| case kSbSystemPropertyLimitAdTracking: { |
| std::string advertising_id = GetAdvertisingId(); |
| // If we get an empty ID, that means the user disabled it. |
| return CopyStringAndTestIfSuccess(out_value, value_length, |
| advertising_id.empty() ? "1" : "0"); |
| } |
| #endif // SB_API_VERSION >= 14 |
| #if SB_API_VERSION >= 15 |
| case kSbSystemPropertyDeviceType: |
| return GetDeviceType(out_value, value_length); |
| #endif |
| default: |
| SB_DLOG(WARNING) << __FUNCTION__ |
| << ": Unrecognized property: " << property_id; |
| break; |
| } |
| return false; |
| } |