// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/system/statistics_provider_impl.h"
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include "ash/constants/ash_paths.h"
#include "ash/constants/ash_switches.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_map.h"
#include "base/files/file_util.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/ash/components/system/kiosk_oem_manifest_parser.h"
namespace ash::system {
namespace {
// Path to the tool used to get system info, and special values for the
// output of the tool.
const char kCrosSystemTool[] = "/usr/bin/crossystem";
const char kCrosSystemValueError[] = "(error)";
// Path to the tool to get VPD info.
const char kFilteredVpdTool[] = "/usr/sbin/dump_filtered_vpd";
// Exit codes for the dump_filtered_vpd tool.
enum class DumpVpdExitCodes : int {
kValid = 0,
kRoInvalid = 1,
kRwInvalid = 2,
kBothInvalid = kRoInvalid | kRwInvalid,
};
// The location of OEM manifest file used to trigger OOBE flow for kiosk mode.
const base::CommandLine::CharType kOemManifestFilePath[] =
FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json");
// File to get regional data from.
const char kCrosRegions[] = "/usr/share/misc/cros-regions.json";
const char kHardwareClassCrosSystemKey[] = "hwid";
const char kHardwareClassValueUnknown[] = "unknown";
const char kIsVmCrosSystemKey[] = "inside_vm";
// ChromeOS should allow debug features.
const char kIsCrosDebugCrosSystemKey[] = "cros_debug";
// Items in region dictionary.
const char kKeyboardsPath[] = "keyboards";
const char kLocalesPath[] = "locales";
const char kTimeZonesPath[] = "time_zones";
const char kKeyboardMechanicalLayoutPath[] = "keyboard_mechanical_layout";
// Timeout that we should wait for statistics to get loaded.
constexpr base::TimeDelta kLoadTimeout = base::Seconds(3);
// A default activation date for providing results in tests.
constexpr char kDefaultActivateDateStub[] = "2000-01";
constexpr char kStatisticLoadingTimeMetricNamePrefix[] =
"ChromeOS.MachineStatistic.";
// Gets the list from the given `dictionary` by given `key`, and returns it as a
// string with all list values joined by ','. Returns nullopt if `key` is not
// found.
std::optional<std::string> JoinListValuesToString(
const base::Value::Dict& dictionary,
std::string_view key) {
const base::Value::List* list_value = dictionary.FindList(key);
if (list_value == nullptr) {
return std::nullopt;
}
std::string buffer;
bool first = true;
for (const auto& v : *list_value) {
const std::string* value = v.GetIfString();
if (!value) {
return std::nullopt;
}
if (first) {
first = false;
} else {
buffer += ',';
}
buffer += *value;
}
return buffer;
}
// Gets the list from the given `dictionary` by given `key`, and returns the
// first value of the list as string. Returns nullopt if `key` is not found.
std::optional<std::string> GetFirstListValueAsString(
const base::Value::Dict& dictionary,
std::string_view key) {
const base::Value::List* list_value = dictionary.FindList(key);
if (list_value == nullptr || list_value->empty()) {
return std::nullopt;
}
const std::string* value = list_value->begin()->GetIfString();
if (value == nullptr) {
return std::nullopt;
}
return *value;
}
std::optional<std::string> GetKeyboardLayoutFromRegionalData(
const base::Value::Dict& region_dict) {
return JoinListValuesToString(region_dict, kKeyboardsPath);
}
std::optional<std::string> GetKeyboardMechanicalLayoutFromRegionalData(
const base::Value::Dict& region_dict) {
const std::string* value =
region_dict.FindString(kKeyboardMechanicalLayoutPath);
if (value == nullptr) {
return std::nullopt;
}
return *value;
}
std::optional<std::string> GetInitialTimezoneFromRegionalData(
const base::Value::Dict& region_dict) {
return GetFirstListValueAsString(region_dict, kTimeZonesPath);
}
std::optional<std::string> GetInitialLocaleFromRegionalData(
const base::Value::Dict& region_dict) {
return JoinListValuesToString(region_dict, kLocalesPath);
}
// Array mapping region keys to their extracting functions.
constexpr std::pair<const char*,
std::optional<std::string> (*)(const base::Value::Dict&)>
kRegionKeysToExtractors[] = {
{kInitialLocaleKey, &GetInitialLocaleFromRegionalData},
{kKeyboardLayoutKey, &GetKeyboardLayoutFromRegionalData},
{kKeyboardMechanicalLayoutKey,
&GetKeyboardMechanicalLayoutFromRegionalData},
{kInitialTimezoneKey, &GetInitialTimezoneFromRegionalData}};
base::FilePath GetFilePathIgnoreFailure(int key) {
base::FilePath file_path;
base::PathService::Get(key, &file_path);
return file_path;
}
bool HasOemPrefix(std::string_view name) {
return name.substr(0, 4) == "oem_";
}
StatisticsProviderImpl::StatisticsSources CreateDefaultSources() {
StatisticsProviderImpl::StatisticsSources sources;
sources.crossystem_tool = base::CommandLine(base::FilePath(kCrosSystemTool));
sources.vpd_tool = base::CommandLine(base::FilePath(kFilteredVpdTool));
sources.machine_info_filepath = GetFilePathIgnoreFailure(FILE_MACHINE_INFO);
sources.oem_manifest_filepath = base::FilePath(kOemManifestFilePath);
sources.cros_regions_filepath = base::FilePath(kCrosRegions);
return sources;
}
// Maps machine statistic name to the MachineStatistic variant in
// tools/metrics/histograms/metadata/chromeos/histograms.xml.
std::string_view StatisticNameToMachineStatisticVariant(
std::string_view statistic_name) {
static constexpr auto kStatisticNameToVariant =
base::MakeFixedFlatMap<std::string_view, std::string_view>({
{kActivateDateKey, "ActivateDate"},
{kBlockDevModeKey, "BlockDevmode"},
{kCheckEnrollmentKey, "CheckEnrollment"},
{kShouldSendRlzPingKey, "ShouldSendRlzPing"},
{kRlzEmbargoEndDateKey, "RlzEmbargoEndDate"},
{kCustomizationIdKey, "CustomizationId"},
{kDevSwitchBootKey, "DevswBoot"},
{kDockMacAddressKey, "DockMac"},
{kEthernetMacAddressKey, "EthernetMac"},
{kFirmwareWriteProtectCurrentKey, "WpswCur"},
{kFirmwareTypeKey, "MainfwType"},
{kHardwareClassKey, "HardwareClass"},
{kIsVmKey, "IsVm"},
{kIsCrosDebugKey, "IsCrosDebug"},
{kMachineModelName, "ModelName"},
{kMachineOemName, "OemName"},
{kManufactureDateKey, "MfgDate"},
{kOffersCouponCodeKey, "UbindAttribute"},
{kOffersGroupCodeKey, "GbindAttribute"},
{kRlzBrandCodeKey, "RlzBrandCode"},
{kRegionKey, "Region"},
{kSerialNumberKey, "SerialNumber"},
{kFlexIdKey, "FlexId"},
{kLegacySerialNumberKey, "LegacySerialNumber"},
{kInitialLocaleKey, "InitialLocale"},
{kInitialTimezoneKey, "InitialTimezone"},
{kKeyboardLayoutKey, "KeyboardLayout"},
{kKeyboardMechanicalLayoutKey, "KeyboardMechanicalLayout"},
{kAttestedDeviceIdKey, "AttestedDeviceId"},
{kDisplayProfilesKey, "DisplayProfiles"},
{kOemCanExitEnterpriseEnrollmentKey, "OemCanExitEnrollment"},
{kOemDeviceRequisitionKey, "OemDeviceRequisition"},
{kOemIsEnterpriseManagedKey, "OemEnterpriseManaged"},
{kOemKeyboardDrivenOobeKey, "OemKeyboardDrivenOobe"},
});
if (const auto it = kStatisticNameToVariant.find(statistic_name);
it != kStatisticNameToVariant.end()) {
return it->second;
}
LOG(WARNING) << "Unhandled statistic is recorded: " << statistic_name;
return statistic_name;
}
void RecordStatisticsRequestLoadingTimeMetric(std::string_view statistic_name,
base::TimeDelta loading_time) {
// Loading time is expected to be 0 (when requested statistic is already
// loaded), or up to short time of `kLoadTimeout`.
const std::string metric_name = base::StrCat(
{kStatisticLoadingTimeMetricNamePrefix,
StatisticNameToMachineStatisticVariant(statistic_name), ".LoadingTime"});
base::UmaHistogramTimes(metric_name, loading_time);
}
} // namespace
StatisticsProviderImpl::StatisticsSources::StatisticsSources() = default;
StatisticsProviderImpl::StatisticsSources::~StatisticsSources() = default;
StatisticsProviderImpl::StatisticsSources::StatisticsSources(
const StatisticsSources& other) = default;
StatisticsProviderImpl::StatisticsSources&
StatisticsProviderImpl::StatisticsSources::operator=(
const StatisticsSources& other) = default;
StatisticsProviderImpl::StatisticsSources::StatisticsSources(
StatisticsSources&& other) = default;
StatisticsProviderImpl::StatisticsSources&
StatisticsProviderImpl::StatisticsSources::operator=(
StatisticsSources&& other) = default;
// static
std::unique_ptr<StatisticsProviderImpl>
StatisticsProviderImpl::CreateProviderForTesting(
StatisticsSources testing_sources) {
// Using `new` to access a non-public constructor.
return base::WrapUnique(
new StatisticsProviderImpl(std::move(testing_sources)));
}
StatisticsProviderImpl::StatisticsProviderImpl()
: StatisticsProviderImpl(CreateDefaultSources()) {}
StatisticsProviderImpl::StatisticsProviderImpl(StatisticsSources sources)
: sources_(std::move(sources)),
load_statistics_started_(false),
oem_manifest_loaded_(false),
statistics_loaded_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
StatisticsProviderImpl::~StatisticsProviderImpl() = default;
void StatisticsProviderImpl::StartLoadingMachineStatistics(
bool load_oem_manifest) {
CHECK(!load_statistics_started_);
load_statistics_started_ = true;
VLOG(1) << "Started loading statistics. Load OEM Manifest: "
<< load_oem_manifest;
// TaskPriority::USER_BLOCKING because this is on the critical path of
// rendering the NTP on startup. https://crbug.com/831835
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&StatisticsProviderImpl::LoadMachineStatistics,
base::Unretained(this), load_oem_manifest));
}
void StatisticsProviderImpl::ScheduleOnMachineStatisticsLoaded(
base::OnceClosure callback) {
{
// It is important to hold `statistics_loaded_lock_` when checking the
// `statistics_loaded_` event to make sure that its state doesn't change
// before `callback` is added to `statistics_loaded_callbacks_`.
base::AutoLock auto_lock(statistics_loaded_lock_);
// Machine statistics are not loaded yet. Add `callback` to a list to be
// scheduled once machine statistics are loaded.
if (!statistics_loaded_.IsSignaled()) {
statistics_loaded_callbacks_.emplace_back(
std::move(callback), base::SequencedTaskRunner::GetCurrentDefault());
return;
}
}
// Machine statistics are loaded. Schedule `callback` immediately.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
std::move(callback));
}
std::optional<std::string_view> StatisticsProviderImpl::GetMachineStatistic(
std::string_view name) {
VLOG(1) << "Machine Statistic requested: " << name;
if (!WaitForStatisticsLoaded(name)) {
LOG(ERROR) << "GetMachineStatistic called before load started: " << name;
return std::nullopt;
}
// Test region should override any other value.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kCrosRegion)) {
if (const std::optional<std::string_view> region_result =
GetRegionalInformation(name)) {
return region_result;
}
}
if (const auto iter = machine_info_.find(name); iter != machine_info_.end()) {
return std::string_view(iter->second);
}
if (const std::optional<std::string_view> region_result =
GetRegionalInformation(name)) {
return region_result;
}
if (base::SysInfo::IsRunningOnChromeOS() &&
(oem_manifest_loaded_ || !HasOemPrefix(name))) {
VLOG(1) << "Requested statistic not found: " << name;
}
return std::nullopt;
}
StatisticsProviderImpl::FlagValue StatisticsProviderImpl::GetMachineFlag(
std::string_view name) {
VLOG(1) << "Machine Flag requested: " << name;
if (!WaitForStatisticsLoaded(name)) {
LOG(ERROR) << "GetMachineFlag called before load started: " << name;
return FlagValue::kUnset;
}
if (const auto iter = machine_flags_.find(name);
iter != machine_flags_.end()) {
return iter->second ? FlagValue::kTrue : FlagValue::kFalse;
}
if (base::SysInfo::IsRunningOnChromeOS() &&
(oem_manifest_loaded_ || !HasOemPrefix(name))) {
VLOG(1) << "Requested machine flag not found: " << name;
}
return FlagValue::kUnset;
}
void StatisticsProviderImpl::Shutdown() {
cancellation_flag_.Set(); // Cancel any pending loads
}
bool StatisticsProviderImpl::IsRunningOnVm() {
if (!base::SysInfo::IsRunningOnChromeOS()) {
return false;
}
return GetMachineStatistic(kIsVmKey) == kIsVmValueTrue;
}
bool StatisticsProviderImpl::IsCrosDebugMode() {
if (!base::SysInfo::IsRunningOnChromeOS()) {
return false;
}
return GetMachineStatistic(kIsCrosDebugKey) == kIsCrosDebugValueTrue;
}
StatisticsProvider::VpdStatus StatisticsProviderImpl::GetVpdStatus() const {
return vpd_status_;
}
void StatisticsProviderImpl::SignalStatisticsLoaded() {
decltype(statistics_loaded_callbacks_) local_statistics_loaded_callbacks;
{
base::AutoLock auto_lock(statistics_loaded_lock_);
// Move all callbacks to a local variable.
local_statistics_loaded_callbacks = std::move(statistics_loaded_callbacks_);
// Prevent new callbacks from being added to `statistics_loaded_callbacks_`
// and unblock pending WaitForStatisticsLoaded() calls.
statistics_loaded_.Signal();
VLOG(1) << "Finished loading statistics.";
}
// Schedule callbacks that were in `statistics_loaded_callbacks_`.
for (auto& callback : local_statistics_loaded_callbacks) {
callback.second->PostTask(FROM_HERE, std::move(callback.first));
}
}
bool StatisticsProviderImpl::WaitForStatisticsLoaded(
std::string_view statistic_name) {
CHECK(load_statistics_started_);
if (statistics_loaded_.IsSignaled()) {
RecordStatisticsRequestLoadingTimeMetric(
statistic_name,
/*loading_time=*/base::TimeDelta());
return true;
}
// Block if the statistics are not loaded yet. Normally this shouldn't
// happen except during OOBE.
const base::Time start_time = base::Time::Now();
base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
statistics_loaded_.TimedWait(kLoadTimeout);
const base::TimeDelta dtime = base::Time::Now() - start_time;
RecordStatisticsRequestLoadingTimeMetric(statistic_name, dtime);
if (statistics_loaded_.IsSignaled()) {
VLOG(1) << "Statistics loaded after waiting " << dtime.InMilliseconds()
<< "ms.";
return true;
}
LOG(ERROR) << "Statistics not loaded after waiting " << dtime.InMilliseconds()
<< "ms.";
return false;
}
void StatisticsProviderImpl::LoadMachineStatistics(bool load_oem_manifest) {
// Run from the file task runner. StatisticsProviderImpl is a Singleton<> and
// will not be destroyed until after threads have been stopped, so this test
// is always safe.
if (cancellation_flag_.IsSet()) {
return;
}
LoadCrossystemTool();
std::string crossystem_wpsw;
if (base::SysInfo::IsRunningOnChromeOS()) {
// If available, the key should be taken from machine info or VPD instead of
// the tool. If not available, the tool's value will be restored.
auto it = machine_info_.find(kFirmwareWriteProtectCurrentKey);
if (it != machine_info_.end()) {
crossystem_wpsw = it->second;
machine_info_.erase(it);
}
}
LoadMachineInfoFile();
LoadVpd();
// Ensure that the hardware class key is present with the expected
// key name, and if it couldn't be retrieved, that the value is "unknown".
std::string hardware_class = machine_info_[kHardwareClassCrosSystemKey];
machine_info_[kHardwareClassKey] =
!hardware_class.empty() ? hardware_class : kHardwareClassValueUnknown;
if (base::SysInfo::IsRunningOnChromeOS()) {
// By default, assume that this is *not* a VM. If crossystem is not present,
// report that we are not in a VM.
machine_info_[kIsVmKey] = kIsVmValueFalse;
const auto is_vm_iter = machine_info_.find(kIsVmCrosSystemKey);
if (is_vm_iter != machine_info_.end() &&
is_vm_iter->second == kIsVmValueTrue) {
machine_info_[kIsVmKey] = kIsVmValueTrue;
}
// By default, assume that this is *not* in debug mode. If crossystem is not
// present, report that we are not in debug mode.
machine_info_[kIsCrosDebugKey] = kIsCrosDebugValueFalse;
const auto is_debug_iter = machine_info_.find(kIsCrosDebugCrosSystemKey);
if (is_debug_iter != machine_info_.end() &&
is_debug_iter->second == kIsCrosDebugValueTrue) {
machine_info_[kIsCrosDebugKey] = kIsCrosDebugValueTrue;
}
// Use the write-protect value from crossystem only if it hasn't been loaded
// from any other source, since the result of crossystem is less reliable
// for this key.
if (!base::Contains(machine_info_, kFirmwareWriteProtectCurrentKey) &&
!crossystem_wpsw.empty()) {
LOG(WARNING) << "wpsw_cur missing from machine_info, using value: "
<< crossystem_wpsw;
machine_info_[kFirmwareWriteProtectCurrentKey] = crossystem_wpsw;
}
// TODO(b/315929204): Remove temporary logging.
if (machine_info_.find(kFirmwareWriteProtectCurrentKey) ==
machine_info_.end()) {
LOG(WARNING) << "Write-protect value unknown.";
} else if (machine_info_[kFirmwareWriteProtectCurrentKey] != "1") {
LOG(WARNING) << "Write-protect disabled.";
}
}
if (load_oem_manifest) {
// If kAppOemManifestFile switch is specified, load OEM Manifest file.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kAppOemManifestFile)) {
LoadOemManifestFromFile(
command_line->GetSwitchValuePath(switches::kAppOemManifestFile));
} else if (base::SysInfo::IsRunningOnChromeOS()) {
LoadOemManifestFromFile(sources_.oem_manifest_filepath);
}
}
// Set region from command line if present.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kCrosRegion)) {
const std::string region =
command_line->GetSwitchValueASCII(switches::kCrosRegion);
machine_info_[kRegionKey] = region;
VLOG(1) << "CrOS region set to '" << region << "'";
}
const auto it = machine_info_.find(kRegionKey);
LoadRegionsFile(sources_.cros_regions_filepath,
it != machine_info_.end() ? it->second : "");
SignalStatisticsLoaded();
}
void StatisticsProviderImpl::LoadCrossystemTool() {
if (!base::SysInfo::IsRunningOnChromeOS()) {
return;
}
NameValuePairsParser parser(&machine_info_);
// Parse all of the key/value pairs from the crossystem tool.
if (!parser.ParseNameValuePairsFromTool(sources_.crossystem_tool,
NameValuePairsFormat::kCrossystem)) {
LOG(ERROR) << "Errors parsing output from: "
<< sources_.crossystem_tool.GetProgram();
}
// Drop useless "(error)" values so they don't displace valid values
// supplied later by other tools: https://crbug.com/844258
parser.DeletePairsWithValue(kCrosSystemValueError);
}
void StatisticsProviderImpl::LoadMachineInfoFile() {
if (!base::PathExists(sources_.machine_info_filepath)) {
if (base::SysInfo::IsRunningOnChromeOS()) {
// This is unexpected, since the file is supposed to always be populated
// by write-machine-info script on ui start.
LOG(ERROR) << "Missing machine info: " << sources_.machine_info_filepath;
return;
}
// Use time value to create an unique stub serial because clashes of the
// same serial for the same domain invalidate earlier enrollments. Persist
// to disk to keep it constant across restarts (required for re-enrollment
// testing).
std::string stub_contents =
"\"serial_number\"=\"stub_" +
base::NumberToString(base::Time::Now().InMillisecondsSinceUnixEpoch()) +
"\"\n";
if (!base::WriteFile(sources_.machine_info_filepath, stub_contents)) {
PLOG(ERROR) << "Error writing machine info stub "
<< sources_.machine_info_filepath;
}
}
// The machine-info file is generated only for OOBE and enterprise enrollment
// and may not be present. See login-manager/init/machine-info.conf.
NameValuePairsParser(&machine_info_)
.ParseNameValuePairsFromFile(sources_.machine_info_filepath,
NameValuePairsFormat::kMachineInfo);
}
void StatisticsProviderImpl::LoadVpd() {
if (!base::SysInfo::IsRunningOnChromeOS()) {
machine_info_[kActivateDateKey] = kDefaultActivateDateStub;
vpd_status_ = VpdStatus::kInvalid;
return;
}
NameValuePairsParser parser(&machine_info_);
std::string output;
int exit_code;
if (!base::GetAppOutputWithExitCode(sources_.vpd_tool, &output, &exit_code)) {
LOG(ERROR) << "Failed to run VPD tool: " << sources_.vpd_tool.GetProgram();
vpd_status_ = VpdStatus::kInvalid;
return;
}
if (!parser.ParseNameValuePairsFromString(output,
NameValuePairsFormat::kVpdDump)) {
LOG(ERROR) << "Errors parsing output from: "
<< sources_.vpd_tool.GetProgram();
vpd_status_ = VpdStatus::kInvalid;
return;
}
switch (exit_code) {
case static_cast<int>(DumpVpdExitCodes::kValid):
vpd_status_ = VpdStatus::kValid;
break;
case static_cast<int>(DumpVpdExitCodes::kRoInvalid):
vpd_status_ = VpdStatus::kRoInvalid;
break;
case static_cast<int>(DumpVpdExitCodes::kRwInvalid):
vpd_status_ = VpdStatus::kRwInvalid;
break;
case static_cast<int>(DumpVpdExitCodes::kBothInvalid):
vpd_status_ = VpdStatus::kInvalid;
break;
default:
vpd_status_ = VpdStatus::kInvalid;
LOG(ERROR) << "Unexpected return code from: "
<< sources_.vpd_tool.GetProgram() << ", " << exit_code;
break;
};
VLOG(1) << "VPD dump exit status: " << exit_code;
}
void StatisticsProviderImpl::LoadOemManifestFromFile(
const base::FilePath& file) {
// Called from LoadMachineStatistics. Check cancellation_flag_ again here.
if (cancellation_flag_.IsSet()) {
return;
}
KioskOemManifestParser::Manifest oem_manifest;
if (!KioskOemManifestParser::Load(file, &oem_manifest)) {
LOG(WARNING) << "Unable to load OEM Manifest file: " << file.value();
return;
}
machine_info_[kOemDeviceRequisitionKey] = oem_manifest.device_requisition;
machine_flags_[kOemIsEnterpriseManagedKey] = oem_manifest.enterprise_managed;
machine_flags_[kOemCanExitEnterpriseEnrollmentKey] =
oem_manifest.can_exit_enrollment;
machine_flags_[kOemKeyboardDrivenOobeKey] = oem_manifest.keyboard_driven_oobe;
oem_manifest_loaded_ = true;
VLOG(1) << "Loaded OEM Manifest statistics from " << file.value();
}
void StatisticsProviderImpl::LoadRegionsFile(const base::FilePath& filename,
std::string_view region) {
JSONFileValueDeserializer regions_file(filename);
int regions_error_code = 0;
std::string regions_error_message;
std::unique_ptr<base::Value> json_value =
regions_file.Deserialize(®ions_error_code, ®ions_error_message);
if (!json_value.get()) {
if (base::SysInfo::IsRunningOnChromeOS()) {
LOG(ERROR) << "Failed to load regions file '" << filename.value()
<< "': error='" << regions_error_message << "'";
}
return;
}
if (!json_value->is_dict()) {
LOG(ERROR) << "Bad regions file '" << filename.value()
<< "': not a dictionary.";
return;
}
base::Value::Dict* region_dict = json_value->GetDict().FindDict(region);
if (region_dict == nullptr) {
LOG(ERROR) << "Bad regional data: '" << region << "' << not found.";
return;
}
// Extract region keys from the dictionary with corresponding extractors.
for (const auto& [key, extractor] : kRegionKeysToExtractors) {
if (auto region_statistic = extractor(*region_dict)) {
region_info_[key] = std::move(region_statistic.value());
}
}
}
std::optional<std::string_view> StatisticsProviderImpl::GetRegionalInformation(
std::string_view name) const {
if (!base::Contains(machine_info_, kRegionKey)) {
return std::nullopt;
}
if (const auto iter = region_info_.find(name); iter != region_info_.end()) {
return std::string_view(iter->second);
}
return std::nullopt;
}
} // namespace ash::system