| // Copyright (c) 2012 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/chromeos/system/statistics_provider.h" |
| |
| #include "base/bind.h" |
| #include "base/chromeos/chromeos_version.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/path_service.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| #include "chromeos/app_mode/kiosk_oem_manifest_parser.h" |
| #include "chromeos/chromeos_constants.h" |
| #include "chromeos/chromeos_switches.h" |
| #include "chromeos/system/name_value_pairs_parser.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| #if defined(GOOGLE_CHROME_BUILD) |
| // TODO(phajdan.jr): Drop that dependency, http://crbug.com/180711 . |
| #include "chrome/common/chrome_version_info.h" |
| #endif |
| |
| using content::BrowserThread; |
| |
| namespace chromeos { |
| namespace system { |
| namespace { |
| |
| // Path to the tool used to get system info, and delimiters for the output |
| // format of the tool. |
| const char* kCrosSystemTool[] = { "/usr/bin/crossystem" }; |
| const char kCrosSystemEq[] = "="; |
| const char kCrosSystemDelim[] = "\n"; |
| const char kCrosSystemCommentDelim[] = "#"; |
| const char kCrosSystemUnknownValue[] = "(error)"; |
| |
| const char kHardwareClassCrosSystemKey[] = "hwid"; |
| const char kHardwareClassKey[] = "hardware_class"; |
| const char kUnknownHardwareClass[] = "unknown"; |
| |
| // File to get machine hardware info from, and key/value delimiters of |
| // the file. |
| // /tmp/machine-info is generated by platform/init/chromeos_startup. |
| const char kMachineHardwareInfoFile[] = "/tmp/machine-info"; |
| const char kMachineHardwareInfoEq[] = "="; |
| const char kMachineHardwareInfoDelim[] = " \n"; |
| |
| // File to get ECHO coupon info from, and key/value delimiters of |
| // the file. |
| const char kEchoCouponFile[] = "/var/cache/echo/vpd_echo.txt"; |
| const char kEchoCouponEq[] = "="; |
| const char kEchoCouponDelim[] = "\n"; |
| |
| // File to get machine OS info from, and key/value delimiters of the file. |
| const char kMachineOSInfoFile[] = "/etc/lsb-release"; |
| const char kMachineOSInfoEq[] = "="; |
| const char kMachineOSInfoDelim[] = "\n"; |
| |
| // File to get VPD info from, and key/value delimiters of the file. |
| const char kVpdFile[] = "/var/log/vpd_2.0.txt"; |
| const char kVpdEq[] = "="; |
| const char kVpdDelim[] = "\n"; |
| |
| // Timeout that we should wait for statistics to get loaded |
| const int kTimeoutSecs = 3; |
| |
| // The location of OEM manifest file used to trigger OOBE flow for kiosk mode. |
| const CommandLine::CharType kOemManifestFilePath[] = |
| FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json"); |
| |
| } // namespace |
| |
| // Key values for GetMachineStatistic()/GetMachineFlag() calls. |
| const char kDevSwitchBootMode[] = "devsw_boot"; |
| const char kHardwareClass[] = "hardware_class"; |
| const char kMachineInfoBoard[] = |
| "CHROMEOS_RELEASE_BOARD"; |
| const char kOffersCouponCodeKey[] = "ubind_attribute"; |
| const char kOffersGroupCodeKey[] = "gbind_attribute"; |
| const char kOemCanExitEnterpriseEnrollmentKey[] = |
| "oem_can_exit_enrollment"; |
| const char kOemDeviceRequisitionKey[] = |
| "oem_device_requisition"; |
| const char kOemIsEnterpriseManagedKey[] = |
| "oem_enterprise_managed"; |
| const char kOemKeyboardDrivenOobeKey[] = |
| "oem_keyboard_driven_oobe"; |
| |
| // The StatisticsProvider implementation used in production. |
| class StatisticsProviderImpl : public StatisticsProvider { |
| public: |
| // StatisticsProvider implementation: |
| virtual void Init() OVERRIDE; |
| virtual void StartLoadingMachineStatistics() OVERRIDE; |
| virtual bool GetMachineStatistic(const std::string& name, |
| std::string* result) OVERRIDE; |
| virtual bool GetMachineFlag(const std::string& name, |
| bool* result) OVERRIDE; |
| virtual void LoadOemManifest() OVERRIDE; |
| |
| static StatisticsProviderImpl* GetInstance(); |
| |
| protected: |
| StatisticsProviderImpl(); |
| void LoadOemManifestFromFile(const base::FilePath& file); |
| |
| private: |
| typedef std::map<std::string, bool> MachineFlags; |
| friend struct DefaultSingletonTraits<StatisticsProviderImpl>; |
| |
| // Loads the machine info file, which is necessary to get the Chrome channel. |
| // Treat MachineOSInfoFile specially, as distribution channel information |
| // (stable, beta, dev, canary) is required at earlier stage than everything |
| // else. Rather than posting a delayed task, read and parse the machine OS |
| // info file immediately. |
| void LoadMachineOSInfoFile(); |
| |
| // Loads the machine statistcs by examining the system. |
| void LoadMachineStatistics(); |
| |
| bool initialized_; |
| bool load_statistics_started_; |
| NameValuePairsParser::NameValueMap machine_info_; |
| MachineFlags machine_flags_; |
| base::WaitableEvent on_statistics_loaded_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StatisticsProviderImpl); |
| }; |
| |
| void StatisticsProviderImpl::Init() { |
| DCHECK(!initialized_); |
| initialized_ = true; |
| |
| // Load the machine info file immediately to get the channel info. |
| LoadMachineOSInfoFile(); |
| } |
| |
| bool StatisticsProviderImpl::GetMachineStatistic( |
| const std::string& name, std::string* result) { |
| DCHECK(initialized_); |
| DCHECK(load_statistics_started_); |
| |
| VLOG(1) << "Statistic is requested for " << name; |
| // Block if the statistics are not loaded yet. Per LOG(WARNING) below, |
| // the statistics are loaded before requested as of now. For regular |
| // sessions (i.e. not OOBE), statistics are first requested when the |
| // user is logging in so we have plenty of time to load the data |
| // beforehand. |
| // |
| // If you see the warning appeared for regular sessions, it probably |
| // means that there is new client code that uses the statistics in the |
| // very early stage of the browser startup. The statistic name should be |
| // helpful to identify the caller. |
| if (!on_statistics_loaded_.IsSignaled()) { |
| LOG(WARNING) << "Waiting to load statistics. Requested statistic: " |
| << name; |
| // http://crbug.com/125385 |
| base::ThreadRestrictions::ScopedAllowWait allow_wait; |
| on_statistics_loaded_.TimedWait(base::TimeDelta::FromSeconds(kTimeoutSecs)); |
| |
| if (!on_statistics_loaded_.IsSignaled()) { |
| LOG(ERROR) << "Statistics weren't loaded after waiting! " |
| << "Requested statistic: " << name; |
| return false; |
| } |
| } |
| |
| NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name); |
| if (iter != machine_info_.end()) { |
| *result = iter->second; |
| return true; |
| } |
| return false; |
| } |
| |
| bool StatisticsProviderImpl::GetMachineFlag( |
| const std::string& name, bool* result) { |
| MachineFlags::const_iterator iter = machine_flags_.find(name); |
| if (iter != machine_flags_.end()) { |
| *result = iter->second; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void StatisticsProviderImpl::LoadOemManifest() { |
| LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath)); |
| } |
| |
| // manual_reset needs to be true, as we want to keep the signaled state. |
| StatisticsProviderImpl::StatisticsProviderImpl() |
| : initialized_(false), |
| load_statistics_started_(false), |
| on_statistics_loaded_(true /* manual_reset */, |
| false /* initially_signaled */) { |
| } |
| |
| void StatisticsProviderImpl::LoadMachineOSInfoFile() { |
| NameValuePairsParser parser(&machine_info_); |
| if (parser.GetNameValuePairsFromFile(base::FilePath(kMachineOSInfoFile), |
| kMachineOSInfoEq, |
| kMachineOSInfoDelim)) { |
| #if defined(GOOGLE_CHROME_BUILD) |
| const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK"; |
| NameValuePairsParser::NameValueMap::iterator iter = |
| machine_info_.find(kChromeOSReleaseTrack); |
| if (iter != machine_info_.end()) |
| chrome::VersionInfo::SetChannel(iter->second); |
| #endif |
| } |
| } |
| |
| void StatisticsProviderImpl::StartLoadingMachineStatistics() { |
| DCHECK(initialized_); |
| DCHECK(!load_statistics_started_); |
| load_statistics_started_ = true; |
| |
| VLOG(1) << "Started loading statistics"; |
| BrowserThread::PostBlockingPoolTask( |
| FROM_HERE, |
| base::Bind(&StatisticsProviderImpl::LoadMachineStatistics, |
| base::Unretained(this))); |
| } |
| |
| void StatisticsProviderImpl::LoadMachineStatistics() { |
| NameValuePairsParser parser(&machine_info_); |
| |
| // Parse all of the key/value pairs from the crossystem tool. |
| if (!parser.ParseNameValuePairsFromTool( |
| arraysize(kCrosSystemTool), kCrosSystemTool, kCrosSystemEq, |
| kCrosSystemDelim, kCrosSystemCommentDelim)) { |
| LOG(WARNING) << "There were errors parsing the output of " |
| << kCrosSystemTool << "."; |
| } |
| |
| // 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]; |
| if (hardware_class.empty() || hardware_class == kCrosSystemUnknownValue) |
| machine_info_[kHardwareClassKey] = kUnknownHardwareClass; |
| else |
| machine_info_[kHardwareClassKey] = hardware_class; |
| |
| parser.GetNameValuePairsFromFile(base::FilePath(kMachineHardwareInfoFile), |
| kMachineHardwareInfoEq, |
| kMachineHardwareInfoDelim); |
| parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile), |
| kEchoCouponEq, |
| kEchoCouponDelim); |
| parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile), kVpdEq, kVpdDelim); |
| |
| // Finished loading the statistics. |
| on_statistics_loaded_.Signal(); |
| VLOG(1) << "Finished loading statistics"; |
| } |
| |
| void StatisticsProviderImpl::LoadOemManifestFromFile( |
| const base::FilePath& file) { |
| KioskOemManifestParser::Manifest oem_manifest; |
| if (!KioskOemManifestParser::Load(file, &oem_manifest)) |
| 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; |
| } |
| |
| StatisticsProviderImpl* StatisticsProviderImpl::GetInstance() { |
| return Singleton<StatisticsProviderImpl, |
| DefaultSingletonTraits<StatisticsProviderImpl> >::get(); |
| } |
| |
| // The stub StatisticsProvider implementation used on Linux desktop. |
| class StatisticsProviderStubImpl : public StatisticsProviderImpl { |
| public: |
| // StatisticsProvider implementation: |
| virtual void Init() OVERRIDE {} |
| |
| virtual void StartLoadingMachineStatistics() OVERRIDE {} |
| |
| virtual bool GetMachineStatistic(const std::string& name, |
| std::string* result) OVERRIDE { |
| if (name == "CHROMEOS_RELEASE_BOARD") { |
| // Note: syncer::GetSessionNameSynchronously() also uses the mechanism |
| // below to determine the CrOs release board. However, it cannot include |
| // statistics_provider.h and use this method because of the mutual |
| // dependency that creates between sync.gyp:sync and chrome.gyp:browser. |
| // TODO(rsimha): Update syncer::GetSessionNameSynchronously() if this code |
| // is ever moved into base/. See http://crbug.com/126732. |
| const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (command_line->HasSwitch(chromeos::switches::kChromeOSReleaseBoard)) { |
| *result = command_line-> |
| GetSwitchValueASCII(chromeos::switches::kChromeOSReleaseBoard); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| virtual void LoadOemManifest() OVERRIDE { |
| CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| if (!command_line->HasSwitch(switches::kAppOemManifestFile)) |
| return; |
| |
| LoadOemManifestFromFile( |
| command_line->GetSwitchValuePath(switches::kAppOemManifestFile)); |
| } |
| |
| static StatisticsProviderStubImpl* GetInstance() { |
| return Singleton<StatisticsProviderStubImpl, |
| DefaultSingletonTraits<StatisticsProviderStubImpl> >::get(); |
| } |
| |
| private: |
| friend struct DefaultSingletonTraits<StatisticsProviderStubImpl>; |
| |
| StatisticsProviderStubImpl() { |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(StatisticsProviderStubImpl); |
| }; |
| |
| StatisticsProvider* StatisticsProvider::GetInstance() { |
| if (base::chromeos::IsRunningOnChromeOS()) { |
| return StatisticsProviderImpl::GetInstance(); |
| } else { |
| return StatisticsProviderStubImpl::GetInstance(); |
| } |
| } |
| |
| } // namespace system |
| } // namespace chromeos |