| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/logging.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/system/sys_info.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "build/branding_buildflags.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h" |
| #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h" |
| #include "chrome/browser/google/google_brand.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/sync/sync_service_factory.h" |
| #include "chrome/common/channel_info.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/sync/driver/sync_internals_util.h" |
| #include "components/sync/driver/sync_service.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "extensions/browser/api/power/power_api.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/api/power.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_set.h" |
| #include "ui/display/types/display_constants.h" |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "ash/public/ash_interfaces.h" |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/arc/policy/arc_policy_bridge.h" |
| #include "chrome/browser/ash/crosapi/browser_manager.h" |
| #include "chrome/browser/ash/crosapi/browser_util.h" |
| #include "chrome/browser/ash/login/demo_mode/demo_session.h" |
| #include "chrome/browser/ash/login/login_pref_names.h" |
| #include "chrome/browser/ash/settings/cros_settings.h" |
| #include "chrome/browser/metrics/chromeos_metrics_provider.h" |
| #include "chrome/browser/metrics/enrollment_status.h" |
| #include "chromeos/dbus/util/version_loader.h" |
| #include "chromeos/settings/cros_settings_names.h" |
| #include "chromeos/system/statistics_provider.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/win/win_util.h" |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| #include "chrome/browser/google/google_update_win.h" |
| #endif |
| #include "ui/base/win/hidden_window.h" |
| #endif |
| |
| #if defined(OS_MAC) |
| #include "base/mac/mac_util.h" |
| #endif |
| |
| namespace system_logs { |
| |
| namespace { |
| |
| constexpr char kSyncDataKey[] = "about_sync_data"; |
| constexpr char kExtensionsListKey[] = "extensions"; |
| constexpr char kPowerApiListKey[] = "chrome.power extensions"; |
| constexpr char kDataReductionProxyKey[] = "data_reduction_proxy"; |
| constexpr char kChromeVersionTag[] = "CHROME VERSION"; |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| constexpr char kLacrosChromeVersionPrefix[] = "Lacros "; |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| constexpr char kAshChromeVersionPrefix[] = "Ash "; |
| constexpr char kArcPolicyComplianceReportKey[] = |
| "CHROMEOS_ARC_POLICY_COMPLIANCE_REPORT"; |
| constexpr char kArcPolicyKey[] = "CHROMEOS_ARC_POLICY"; |
| constexpr char kChromeOsFirmwareVersion[] = "CHROMEOS_FIRMWARE_VERSION"; |
| constexpr char kChromeEnrollmentTag[] = "ENTERPRISE_ENROLLED"; |
| constexpr char kHWIDKey[] = "HWID"; |
| constexpr char kSettingsKey[] = "settings"; |
| constexpr char kLocalStateSettingsResponseKey[] = "Local State: settings"; |
| constexpr char kLTSChromeVersionPrefix[] = "LTS "; |
| constexpr char kArcStatusKey[] = "CHROMEOS_ARC_STATUS"; |
| constexpr char kMonitorInfoKey[] = "monitor_info"; |
| constexpr char kAccountTypeKey[] = "account_type"; |
| constexpr char kDemoModeConfigKey[] = "demo_mode_config"; |
| constexpr char kOnboardingTime[] = "ONBOARDING_TIME"; |
| #else |
| constexpr char kOsVersionTag[] = "OS VERSION"; |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| #if defined(OS_WIN) |
| constexpr char kUsbKeyboardDetected[] = "usb_keyboard_detected"; |
| constexpr char kIsEnrolledToDomain[] = "enrolled_to_domain"; |
| constexpr char kInstallerBrandCode[] = "installer_brand_code"; |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| constexpr char kUpdateErrorCode[] = "update_error_code"; |
| constexpr char kUpdateHresult[] = "update_hresult"; |
| constexpr char kInstallResultCode[] = "install_result_code"; |
| constexpr char kInstallLocation[] = "install_location"; |
| #endif |
| #endif // OS_WIN |
| |
| #if defined(OS_MAC) |
| constexpr char kCpuArch[] = "cpu_arch"; |
| #endif |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| std::string GetPrimaryAccountTypeString() { |
| DCHECK(user_manager::UserManager::Get()); |
| const user_manager::User* primary_user = |
| user_manager::UserManager::Get()->GetPrimaryUser(); |
| |
| // In case we're on the login screen, we won't have a logged in user. |
| if (!primary_user) |
| return "none"; |
| |
| switch (primary_user->GetType()) { |
| case user_manager::USER_TYPE_REGULAR: |
| return "regular"; |
| case user_manager::USER_TYPE_GUEST: |
| return "guest"; |
| case user_manager::USER_TYPE_PUBLIC_ACCOUNT: |
| return "public_account"; |
| case user_manager::USER_TYPE_SUPERVISED_DEPRECATED: |
| return "supervised"; |
| case user_manager::USER_TYPE_KIOSK_APP: |
| return "kiosk_app"; |
| case user_manager::USER_TYPE_CHILD: |
| return "child"; |
| case user_manager::USER_TYPE_ARC_KIOSK_APP: |
| return "arc_kiosk_app"; |
| case user_manager::USER_TYPE_ACTIVE_DIRECTORY: |
| return "active_directory"; |
| case user_manager::USER_TYPE_WEB_KIOSK_APP: |
| return "web_kiosk_app"; |
| case user_manager::NUM_USER_TYPES: |
| NOTREACHED(); |
| break; |
| } |
| return std::string(); |
| } |
| |
| std::string GetEnrollmentStatusString() { |
| switch (ChromeOSMetricsProvider::GetEnrollmentStatus()) { |
| case EnrollmentStatus::kNonManaged: |
| return "Not managed"; |
| case EnrollmentStatus::kManaged: |
| return "Managed"; |
| case EnrollmentStatus::kUnused: |
| case EnrollmentStatus::kErrorGettingStatus: |
| return "Error retrieving status"; |
| } |
| } |
| |
| std::string GetDisplayInfoString( |
| const ash::mojom::DisplayUnitInfo& display_info) { |
| std::string entry; |
| if (!display_info.name.empty()) |
| base::StringAppendF(&entry, "%s : ", display_info.name.c_str()); |
| if (!display_info.edid) |
| return entry; |
| const ash::mojom::Edid& edid = *display_info.edid; |
| if (!edid.manufacturer_id.empty()) { |
| base::StringAppendF(&entry, "Manufacturer: %s - ", |
| edid.manufacturer_id.c_str()); |
| } |
| if (!edid.product_id.empty()) { |
| base::StringAppendF(&entry, "Product ID: %s - ", edid.product_id.c_str()); |
| } |
| if (edid.year_of_manufacture != display::kInvalidYearOfManufacture) { |
| base::StringAppendF(&entry, "Year of Manufacture: %d", |
| edid.year_of_manufacture); |
| } |
| return entry; |
| } |
| |
| // Called from the main (UI) thread, invokes |callback| when complete. |
| void PopulateMonitorInfoAsync( |
| ash::mojom::CrosDisplayConfigController* cros_display_config_ptr, |
| SystemLogsResponse* response, |
| base::OnceCallback<void()> callback) { |
| cros_display_config_ptr->GetDisplayUnitInfoList( |
| false /* single_unified */, |
| base::BindOnce( |
| [](SystemLogsResponse* response, base::OnceCallback<void()> callback, |
| std::vector<ash::mojom::DisplayUnitInfoPtr> info_list) { |
| std::string entry; |
| for (const ash::mojom::DisplayUnitInfoPtr& info : info_list) { |
| if (!entry.empty()) |
| base::StringAppendF(&entry, "\n"); |
| entry += GetDisplayInfoString(*info); |
| } |
| response->emplace(kMonitorInfoKey, entry); |
| std::move(callback).Run(); |
| }, |
| response, std::move(callback))); |
| } |
| |
| // Called from a worker thread via PostTaskAndReply. |
| void PopulateEntriesAsync(SystemLogsResponse* response) { |
| DCHECK(response); |
| |
| chromeos::system::StatisticsProvider* stats = |
| chromeos::system::StatisticsProvider::GetInstance(); |
| DCHECK(stats); |
| |
| // Get the HWID. |
| std::string hwid; |
| if (!stats->GetMachineStatistic(chromeos::system::kHardwareClassKey, &hwid)) |
| VLOG(1) << "Couldn't get machine statistic 'hardware_class'."; |
| else |
| response->emplace(kHWIDKey, hwid); |
| |
| // Get the firmware version. |
| response->emplace(kChromeOsFirmwareVersion, |
| chromeos::version_loader::GetFirmware()); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| std::string GetChromeVersionString() { |
| // Version of the current running browser. |
| std::string browser_version = |
| chrome::GetVersionString(chrome::WithExtendedStable(true)); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // If the device is receiving LTS updates, add a prefix to the version string. |
| // The value of the policy is ignored here. |
| std::string value; |
| const bool is_lts = |
| ash::CrosSettings::Get()->GetString(chromeos::kReleaseLtsTag, &value); |
| if (is_lts) |
| browser_version = kLTSChromeVersionPrefix + browser_version; |
| |
| // If lacros-chrome is allowed & supported, and launched before, which |
| // is indicated by |browser_version| in BrowserManager being set to non-empty |
| // string during lacros startup, attach its version in the chrome |
| // version string. |
| if (crosapi::browser_util::IsLacrosEnabled() && |
| !crosapi::BrowserManager::Get()->browser_version().empty()) { |
| std::string lacros_version = |
| crosapi::BrowserManager::Get()->browser_version(); |
| return kLacrosChromeVersionPrefix + lacros_version + ", " + |
| kAshChromeVersionPrefix + browser_version; |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| return browser_version; |
| } |
| |
| #if defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| // Returns true if the path identified by |key| with the PathService is a parent |
| // or ancestor of |child|. |
| bool IsParentOf(int key, const base::FilePath& child) { |
| base::FilePath path; |
| return base::PathService::Get(key, &path) && path.IsParent(child); |
| } |
| |
| // Returns a string representing the overall install location of the browser. |
| // "Program Files" and "Program Files (x86)" are both considered "per-machine" |
| // locations (for all users), whereas anything in a user's local app data dir is |
| // considered a "per-user" location. This function returns an answer that gives, |
| // in essence, the broad category of location without checking that the browser |
| // is operating out of the exact expected install directory. It is interesting |
| // to know via feedback reports if updates are failing with |
| // CANNOT_UPGRADE_CHROME_IN_THIS_DIRECTORY, which checks the exact directory, |
| // yet the reported install_location is not "unknown". |
| std::string DetermineInstallLocation() { |
| base::FilePath exe_path; |
| |
| if (base::PathService::Get(base::FILE_EXE, &exe_path)) { |
| if (IsParentOf(base::DIR_PROGRAM_FILESX86, exe_path) || |
| IsParentOf(base::DIR_PROGRAM_FILES, exe_path)) { |
| return "per-machine"; |
| } |
| if (IsParentOf(base::DIR_LOCAL_APP_DATA, exe_path)) |
| return "per-user"; |
| } |
| return "unknown"; |
| } |
| #endif // defined(OS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| |
| #if defined(OS_MAC) |
| std::string MacCpuArchAsString() { |
| switch (base::mac::GetCPUType()) { |
| case base::mac::CPUType::kIntel: |
| return "x86-64"; |
| case base::mac::CPUType::kTranslatedIntel: |
| return "x86-64/translated"; |
| case base::mac::CPUType::kArm: |
| return "arm64"; |
| } |
| } |
| #endif |
| |
| } // namespace |
| |
| ChromeInternalLogSource::ChromeInternalLogSource() |
| : SystemLogsSource("ChromeInternal") { |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| ash::BindCrosDisplayConfigController( |
| cros_display_config_.BindNewPipeAndPassReceiver()); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| } |
| |
| ChromeInternalLogSource::~ChromeInternalLogSource() { |
| } |
| |
| void ChromeInternalLogSource::Fetch(SysLogsSourceCallback callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DCHECK(!callback.is_null()); |
| |
| auto response = std::make_unique<SystemLogsResponse>(); |
| response->emplace(kChromeVersionTag, GetChromeVersionString()); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| response->emplace(kChromeEnrollmentTag, GetEnrollmentStatusString()); |
| #else |
| // On ChromeOS, this will be pulled in from the LSB_RELEASE. |
| std::string os_version = base::SysInfo::OperatingSystemName() + ": " + |
| base::SysInfo::OperatingSystemVersion(); |
| response->emplace(kOsVersionTag, os_version); |
| #endif |
| |
| PopulateSyncLogs(response.get()); |
| PopulateExtensionInfoLogs(response.get()); |
| PopulatePowerApiLogs(response.get()); |
| PopulateDataReductionProxyLogs(response.get()); |
| #if defined(OS_WIN) |
| PopulateUsbKeyboardDetected(response.get()); |
| PopulateEnrolledToDomain(response.get()); |
| PopulateInstallerBrandCode(response.get()); |
| PopulateLastUpdateState(response.get()); |
| #endif |
| |
| #if defined(OS_MAC) |
| response->emplace(kCpuArch, MacCpuArchAsString()); |
| #endif |
| |
| if (ProfileManager::GetLastUsedProfile()->IsChild()) |
| response->emplace("account_type", "child"); |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| // Store ARC enabled status. |
| bool is_arc_enabled = arc::IsArcPlayStoreEnabledForProfile( |
| ProfileManager::GetLastUsedProfile()); |
| response->emplace(kArcStatusKey, is_arc_enabled ? "enabled" : "disabled"); |
| if (is_arc_enabled) { |
| PopulateArcPolicyStatus(response.get()); |
| } |
| response->emplace(kAccountTypeKey, GetPrimaryAccountTypeString()); |
| response->emplace(kDemoModeConfigKey, ash::DemoSession::DemoConfigToString( |
| ash::DemoSession::GetDemoConfig())); |
| PopulateLocalStateSettings(response.get()); |
| PopulateOnboardingTime(response.get()); |
| |
| // Chain asynchronous fetchers: PopulateMonitorInfoAsync, PopulateEntriesAsync |
| PopulateMonitorInfoAsync( |
| cros_display_config_.get(), response.get(), |
| base::BindOnce( |
| [](std::unique_ptr<SystemLogsResponse> response, |
| SysLogsSourceCallback callback) { |
| SystemLogsResponse* response_ptr = response.get(); |
| base::ThreadPool::PostTaskAndReply( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, |
| base::BindOnce(&PopulateEntriesAsync, response_ptr), |
| base::BindOnce(std::move(callback), std::move(response))); |
| }, |
| std::move(response), std::move(callback))); |
| #else |
| // On other platforms, we're done. Invoke the callback. |
| std::move(callback).Run(std::move(response)); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| } |
| |
| void ChromeInternalLogSource::PopulateSyncLogs(SystemLogsResponse* response) { |
| // We are only interested in sync logs for the primary user profile. |
| Profile* profile = ProfileManager::GetPrimaryUserProfile(); |
| if (!profile || !SyncServiceFactory::HasSyncService(profile)) |
| return; |
| |
| // Add sync logs to |response|. |
| std::unique_ptr<base::DictionaryValue> sync_logs = |
| syncer::sync_ui_util::ConstructAboutInformation( |
| syncer::sync_ui_util::IncludeSensitiveData(false), |
| SyncServiceFactory::GetForProfile(profile), |
| chrome::GetChannelName(chrome::WithExtendedStable(true))); |
| std::string serialized_sync_logs; |
| JSONStringValueSerializer(&serialized_sync_logs).Serialize(*sync_logs); |
| response->emplace(kSyncDataKey, serialized_sync_logs); |
| } |
| |
| void ChromeInternalLogSource::PopulateExtensionInfoLogs( |
| SystemLogsResponse* response) { |
| Profile* profile = ProfileManager::GetLastUsedProfile(); |
| if (!profile) |
| return; |
| |
| extensions::ExtensionRegistry* extension_registry = |
| extensions::ExtensionRegistry::Get(profile); |
| std::string extensions_list; |
| for (const scoped_refptr<const extensions::Extension>& extension : |
| extension_registry->enabled_extensions()) { |
| // Format the list as: |
| // "extension_id" : "extension_name" : "extension_version". |
| |
| // Work around the anonymizer tool recognizing some versions as IPv4s. |
| // Replaces dots "." by underscores "_". |
| // We shouldn't change the anonymizer tool as it is working as intended; it |
| // must err on the side of safety. |
| std::string version; |
| base::ReplaceChars(extension->VersionString(), ".", "_", &version); |
| extensions_list += extension->id() + " : " + extension->name() + |
| " : version " + version + "\n"; |
| } |
| |
| if (!extensions_list.empty()) |
| response->emplace(kExtensionsListKey, extensions_list); |
| } |
| |
| void ChromeInternalLogSource::PopulatePowerApiLogs( |
| SystemLogsResponse* response) { |
| std::string info; |
| for (auto* profile : |
| g_browser_process->profile_manager()->GetLoadedProfiles()) { |
| for (const auto& it : |
| extensions::PowerAPI::Get(profile)->extension_levels()) { |
| if (!info.empty()) |
| info += ",\n"; |
| info += it.first + ": " + extensions::api::power::ToString(it.second); |
| } |
| } |
| |
| if (!info.empty()) |
| response->emplace(kPowerApiListKey, info); |
| } |
| |
| void ChromeInternalLogSource::PopulateDataReductionProxyLogs( |
| SystemLogsResponse* response) { |
| data_reduction_proxy::DataReductionProxySettings* |
| data_reduction_proxy_settings = |
| DataReductionProxyChromeSettingsFactory::GetForBrowserContext( |
| ProfileManager::GetActiveUserProfile()); |
| bool data_saver_enabled = |
| data_reduction_proxy_settings && |
| data_reduction_proxy_settings->IsDataReductionProxyEnabled(); |
| response->emplace(kDataReductionProxyKey, |
| data_saver_enabled ? "enabled" : "disabled"); |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| void ChromeInternalLogSource::PopulateLocalStateSettings( |
| SystemLogsResponse* response) { |
| // Extract the "settings" entry in the local state and serialize back to |
| // a string. |
| base::Value local_state = |
| g_browser_process->local_state()->GetPreferenceValues( |
| PrefService::EXCLUDE_DEFAULTS); |
| const base::Value* local_state_settings = |
| local_state.FindDictKey(kSettingsKey); |
| if (!local_state_settings) { |
| VLOG(1) << "Failed to extract the settings entry from Local State."; |
| return; |
| } |
| std::string serialized_settings; |
| JSONStringValueSerializer serializer(&serialized_settings); |
| if (!serializer.Serialize(*local_state_settings)) |
| return; |
| |
| response->emplace(kLocalStateSettingsResponseKey, serialized_settings); |
| } |
| |
| void ChromeInternalLogSource::PopulateArcPolicyStatus( |
| SystemLogsResponse* response) { |
| response->emplace(kArcPolicyKey, arc::ArcPolicyBridge::GetForBrowserContext( |
| ProfileManager::GetLastUsedProfile()) |
| ->get_arc_policy_for_reporting()); |
| response->emplace(kArcPolicyComplianceReportKey, |
| arc::ArcPolicyBridge::GetForBrowserContext( |
| ProfileManager::GetLastUsedProfile()) |
| ->get_arc_policy_compliance_report()); |
| } |
| |
| void ChromeInternalLogSource::PopulateOnboardingTime( |
| SystemLogsResponse* response) { |
| Profile* profile = ProfileManager::GetPrimaryUserProfile(); |
| if (!profile) |
| return; |
| base::Time time = |
| profile->GetPrefs()->GetTime(chromeos::prefs::kOobeOnboardingTime); |
| if (time.is_null()) |
| return; |
| |
| base::Time::Exploded exploded; |
| time.UTCExplode(&exploded); |
| response->emplace(kOnboardingTime, |
| base::StringPrintf("%04d-%02d-%02d", exploded.year, |
| exploded.month, exploded.day_of_month)); |
| } |
| |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| #if defined(OS_WIN) |
| void ChromeInternalLogSource::PopulateUsbKeyboardDetected( |
| SystemLogsResponse* response) { |
| std::string reason; |
| bool result = |
| base::win::IsKeyboardPresentOnSlate(ui::GetHiddenWindow(), &reason); |
| reason.insert(0, result ? "Keyboard Detected:\n" : "No Keyboard:\n"); |
| response->emplace(kUsbKeyboardDetected, reason); |
| } |
| |
| void ChromeInternalLogSource::PopulateEnrolledToDomain( |
| SystemLogsResponse* response) { |
| response->emplace(kIsEnrolledToDomain, base::win::IsEnrolledToDomain() |
| ? "Enrolled to domain" |
| : "Not enrolled to domain"); |
| } |
| |
| void ChromeInternalLogSource::PopulateInstallerBrandCode( |
| SystemLogsResponse* response) { |
| std::string brand; |
| google_brand::GetBrand(&brand); |
| response->emplace(kInstallerBrandCode, |
| brand.empty() ? "Unknown brand code" : brand); |
| } |
| |
| void ChromeInternalLogSource::PopulateLastUpdateState( |
| SystemLogsResponse* response) { |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| const absl::optional<UpdateState> update_state = GetLastUpdateState(); |
| if (!update_state) |
| return; // There is nothing to include if no update check has completed. |
| |
| response->emplace(kUpdateErrorCode, |
| base::NumberToString(update_state->error_code)); |
| response->emplace(kInstallLocation, DetermineInstallLocation()); |
| |
| if (update_state->error_code == GOOGLE_UPDATE_NO_ERROR) |
| return; // There is nothing more to include if the last check succeeded. |
| |
| response->emplace(kUpdateHresult, |
| base::StringPrintf("0x%08lX", update_state->hresult)); |
| if (update_state->installer_exit_code) { |
| response->emplace(kInstallResultCode, |
| base::NumberToString(*update_state->installer_exit_code)); |
| } |
| #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| } |
| #endif // defined(OS_WIN) |
| |
| } // namespace system_logs |