1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/system/statistics_provider.h" 6 7 #include "base/bind.h" 8 #include "base/chromeos/chromeos_version.h" 9 #include "base/command_line.h" 10 #include "base/files/file_path.h" 11 #include "base/logging.h" 12 #include "base/memory/singleton.h" 13 #include "base/path_service.h" 14 #include "base/synchronization/waitable_event.h" 15 #include "base/threading/thread_restrictions.h" 16 #include "base/time/time.h" 17 #include "chromeos/app_mode/kiosk_oem_manifest_parser.h" 18 #include "chromeos/chromeos_constants.h" 19 #include "chromeos/chromeos_switches.h" 20 #include "chromeos/system/name_value_pairs_parser.h" 21 #include "content/public/browser/browser_thread.h" 22 23 #if defined(GOOGLE_CHROME_BUILD) 24 // TODO(phajdan.jr): Drop that dependency, http://crbug.com/180711 . 25 #include "chrome/common/chrome_version_info.h" 26 #endif 27 28 using content::BrowserThread; 29 30 namespace chromeos { 31 namespace system { 32 namespace { 33 34 // Path to the tool used to get system info, and delimiters for the output 35 // format of the tool. 36 const char* kCrosSystemTool[] = { "/usr/bin/crossystem" }; 37 const char kCrosSystemEq[] = "="; 38 const char kCrosSystemDelim[] = "\n"; 39 const char kCrosSystemCommentDelim[] = "#"; 40 const char kCrosSystemUnknownValue[] = "(error)"; 41 42 const char kHardwareClassCrosSystemKey[] = "hwid"; 43 const char kHardwareClassKey[] = "hardware_class"; 44 const char kUnknownHardwareClass[] = "unknown"; 45 46 // File to get machine hardware info from, and key/value delimiters of 47 // the file. 48 // /tmp/machine-info is generated by platform/init/chromeos_startup. 49 const char kMachineHardwareInfoFile[] = "/tmp/machine-info"; 50 const char kMachineHardwareInfoEq[] = "="; 51 const char kMachineHardwareInfoDelim[] = " \n"; 52 53 // File to get ECHO coupon info from, and key/value delimiters of 54 // the file. 55 const char kEchoCouponFile[] = "/var/cache/echo/vpd_echo.txt"; 56 const char kEchoCouponEq[] = "="; 57 const char kEchoCouponDelim[] = "\n"; 58 59 // File to get machine OS info from, and key/value delimiters of the file. 60 const char kMachineOSInfoFile[] = "/etc/lsb-release"; 61 const char kMachineOSInfoEq[] = "="; 62 const char kMachineOSInfoDelim[] = "\n"; 63 64 // File to get VPD info from, and key/value delimiters of the file. 65 const char kVpdFile[] = "/var/log/vpd_2.0.txt"; 66 const char kVpdEq[] = "="; 67 const char kVpdDelim[] = "\n"; 68 69 // Timeout that we should wait for statistics to get loaded 70 const int kTimeoutSecs = 3; 71 72 // The location of OEM manifest file used to trigger OOBE flow for kiosk mode. 73 const CommandLine::CharType kOemManifestFilePath[] = 74 FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json"); 75 76 } // namespace 77 78 // Key values for GetMachineStatistic()/GetMachineFlag() calls. 79 const char kDevSwitchBootMode[] = "devsw_boot"; 80 const char kHardwareClass[] = "hardware_class"; 81 const char kMachineInfoBoard[] = 82 "CHROMEOS_RELEASE_BOARD"; 83 const char kOffersCouponCodeKey[] = "ubind_attribute"; 84 const char kOffersGroupCodeKey[] = "gbind_attribute"; 85 const char kOemCanExitEnterpriseEnrollmentKey[] = 86 "oem_can_exit_enrollment"; 87 const char kOemDeviceRequisitionKey[] = 88 "oem_device_requisition"; 89 const char kOemIsEnterpriseManagedKey[] = 90 "oem_enterprise_managed"; 91 const char kOemKeyboardDrivenOobeKey[] = 92 "oem_keyboard_driven_oobe"; 93 94 // The StatisticsProvider implementation used in production. 95 class StatisticsProviderImpl : public StatisticsProvider { 96 public: 97 // StatisticsProvider implementation: 98 virtual void Init() OVERRIDE; 99 virtual void StartLoadingMachineStatistics() OVERRIDE; 100 virtual bool GetMachineStatistic(const std::string& name, 101 std::string* result) OVERRIDE; 102 virtual bool GetMachineFlag(const std::string& name, 103 bool* result) OVERRIDE; 104 virtual void LoadOemManifest() OVERRIDE; 105 106 static StatisticsProviderImpl* GetInstance(); 107 108 protected: 109 StatisticsProviderImpl(); 110 void LoadOemManifestFromFile(const base::FilePath& file); 111 112 private: 113 typedef std::map<std::string, bool> MachineFlags; 114 friend struct DefaultSingletonTraits<StatisticsProviderImpl>; 115 116 // Loads the machine info file, which is necessary to get the Chrome channel. 117 // Treat MachineOSInfoFile specially, as distribution channel information 118 // (stable, beta, dev, canary) is required at earlier stage than everything 119 // else. Rather than posting a delayed task, read and parse the machine OS 120 // info file immediately. 121 void LoadMachineOSInfoFile(); 122 123 // Loads the machine statistcs by examining the system. 124 void LoadMachineStatistics(); 125 126 bool initialized_; 127 bool load_statistics_started_; 128 NameValuePairsParser::NameValueMap machine_info_; 129 MachineFlags machine_flags_; 130 base::WaitableEvent on_statistics_loaded_; 131 132 DISALLOW_COPY_AND_ASSIGN(StatisticsProviderImpl); 133 }; 134 135 void StatisticsProviderImpl::Init() { 136 DCHECK(!initialized_); 137 initialized_ = true; 138 139 // Load the machine info file immediately to get the channel info. 140 LoadMachineOSInfoFile(); 141 } 142 143 bool StatisticsProviderImpl::GetMachineStatistic( 144 const std::string& name, std::string* result) { 145 DCHECK(initialized_); 146 DCHECK(load_statistics_started_); 147 148 VLOG(1) << "Statistic is requested for " << name; 149 // Block if the statistics are not loaded yet. Per LOG(WARNING) below, 150 // the statistics are loaded before requested as of now. For regular 151 // sessions (i.e. not OOBE), statistics are first requested when the 152 // user is logging in so we have plenty of time to load the data 153 // beforehand. 154 // 155 // If you see the warning appeared for regular sessions, it probably 156 // means that there is new client code that uses the statistics in the 157 // very early stage of the browser startup. The statistic name should be 158 // helpful to identify the caller. 159 if (!on_statistics_loaded_.IsSignaled()) { 160 LOG(WARNING) << "Waiting to load statistics. Requested statistic: " 161 << name; 162 // http://crbug.com/125385 163 base::ThreadRestrictions::ScopedAllowWait allow_wait; 164 on_statistics_loaded_.TimedWait(base::TimeDelta::FromSeconds(kTimeoutSecs)); 165 166 if (!on_statistics_loaded_.IsSignaled()) { 167 LOG(ERROR) << "Statistics weren't loaded after waiting! " 168 << "Requested statistic: " << name; 169 return false; 170 } 171 } 172 173 NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name); 174 if (iter != machine_info_.end()) { 175 *result = iter->second; 176 return true; 177 } 178 return false; 179 } 180 181 bool StatisticsProviderImpl::GetMachineFlag( 182 const std::string& name, bool* result) { 183 MachineFlags::const_iterator iter = machine_flags_.find(name); 184 if (iter != machine_flags_.end()) { 185 *result = iter->second; 186 return true; 187 } 188 189 return false; 190 } 191 192 void StatisticsProviderImpl::LoadOemManifest() { 193 LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath)); 194 } 195 196 // manual_reset needs to be true, as we want to keep the signaled state. 197 StatisticsProviderImpl::StatisticsProviderImpl() 198 : initialized_(false), 199 load_statistics_started_(false), 200 on_statistics_loaded_(true /* manual_reset */, 201 false /* initially_signaled */) { 202 } 203 204 void StatisticsProviderImpl::LoadMachineOSInfoFile() { 205 NameValuePairsParser parser(&machine_info_); 206 if (parser.GetNameValuePairsFromFile(base::FilePath(kMachineOSInfoFile), 207 kMachineOSInfoEq, 208 kMachineOSInfoDelim)) { 209 #if defined(GOOGLE_CHROME_BUILD) 210 const char kChromeOSReleaseTrack[] = "CHROMEOS_RELEASE_TRACK"; 211 NameValuePairsParser::NameValueMap::iterator iter = 212 machine_info_.find(kChromeOSReleaseTrack); 213 if (iter != machine_info_.end()) 214 chrome::VersionInfo::SetChannel(iter->second); 215 #endif 216 } 217 } 218 219 void StatisticsProviderImpl::StartLoadingMachineStatistics() { 220 DCHECK(initialized_); 221 DCHECK(!load_statistics_started_); 222 load_statistics_started_ = true; 223 224 VLOG(1) << "Started loading statistics"; 225 BrowserThread::PostBlockingPoolTask( 226 FROM_HERE, 227 base::Bind(&StatisticsProviderImpl::LoadMachineStatistics, 228 base::Unretained(this))); 229 } 230 231 void StatisticsProviderImpl::LoadMachineStatistics() { 232 NameValuePairsParser parser(&machine_info_); 233 234 // Parse all of the key/value pairs from the crossystem tool. 235 if (!parser.ParseNameValuePairsFromTool( 236 arraysize(kCrosSystemTool), kCrosSystemTool, kCrosSystemEq, 237 kCrosSystemDelim, kCrosSystemCommentDelim)) { 238 LOG(WARNING) << "There were errors parsing the output of " 239 << kCrosSystemTool << "."; 240 } 241 242 // Ensure that the hardware class key is present with the expected 243 // key name, and if it couldn't be retrieved, that the value is "unknown". 244 std::string hardware_class = machine_info_[kHardwareClassCrosSystemKey]; 245 if (hardware_class.empty() || hardware_class == kCrosSystemUnknownValue) 246 machine_info_[kHardwareClassKey] = kUnknownHardwareClass; 247 else 248 machine_info_[kHardwareClassKey] = hardware_class; 249 250 parser.GetNameValuePairsFromFile(base::FilePath(kMachineHardwareInfoFile), 251 kMachineHardwareInfoEq, 252 kMachineHardwareInfoDelim); 253 parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile), 254 kEchoCouponEq, 255 kEchoCouponDelim); 256 parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile), kVpdEq, kVpdDelim); 257 258 // Finished loading the statistics. 259 on_statistics_loaded_.Signal(); 260 VLOG(1) << "Finished loading statistics"; 261 } 262 263 void StatisticsProviderImpl::LoadOemManifestFromFile( 264 const base::FilePath& file) { 265 KioskOemManifestParser::Manifest oem_manifest; 266 if (!KioskOemManifestParser::Load(file, &oem_manifest)) 267 return; 268 269 machine_info_[kOemDeviceRequisitionKey] = 270 oem_manifest.device_requisition; 271 machine_flags_[kOemIsEnterpriseManagedKey] = 272 oem_manifest.enterprise_managed; 273 machine_flags_[kOemCanExitEnterpriseEnrollmentKey] = 274 oem_manifest.can_exit_enrollment; 275 machine_flags_[kOemKeyboardDrivenOobeKey] = 276 oem_manifest.keyboard_driven_oobe; 277 } 278 279 StatisticsProviderImpl* StatisticsProviderImpl::GetInstance() { 280 return Singleton<StatisticsProviderImpl, 281 DefaultSingletonTraits<StatisticsProviderImpl> >::get(); 282 } 283 284 // The stub StatisticsProvider implementation used on Linux desktop. 285 class StatisticsProviderStubImpl : public StatisticsProviderImpl { 286 public: 287 // StatisticsProvider implementation: 288 virtual void Init() OVERRIDE {} 289 290 virtual void StartLoadingMachineStatistics() OVERRIDE {} 291 292 virtual bool GetMachineStatistic(const std::string& name, 293 std::string* result) OVERRIDE { 294 if (name == "CHROMEOS_RELEASE_BOARD") { 295 // Note: syncer::GetSessionNameSynchronously() also uses the mechanism 296 // below to determine the CrOs release board. However, it cannot include 297 // statistics_provider.h and use this method because of the mutual 298 // dependency that creates between sync.gyp:sync and chrome.gyp:browser. 299 // TODO(rsimha): Update syncer::GetSessionNameSynchronously() if this code 300 // is ever moved into base/. See http://crbug.com/126732. 301 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 302 if (command_line->HasSwitch(chromeos::switches::kChromeOSReleaseBoard)) { 303 *result = command_line-> 304 GetSwitchValueASCII(chromeos::switches::kChromeOSReleaseBoard); 305 return true; 306 } 307 } 308 return false; 309 } 310 311 virtual void LoadOemManifest() OVERRIDE { 312 CommandLine* command_line = CommandLine::ForCurrentProcess(); 313 if (!command_line->HasSwitch(switches::kAppOemManifestFile)) 314 return; 315 316 LoadOemManifestFromFile( 317 command_line->GetSwitchValuePath(switches::kAppOemManifestFile)); 318 } 319 320 static StatisticsProviderStubImpl* GetInstance() { 321 return Singleton<StatisticsProviderStubImpl, 322 DefaultSingletonTraits<StatisticsProviderStubImpl> >::get(); 323 } 324 325 private: 326 friend struct DefaultSingletonTraits<StatisticsProviderStubImpl>; 327 328 StatisticsProviderStubImpl() { 329 } 330 331 DISALLOW_COPY_AND_ASSIGN(StatisticsProviderStubImpl); 332 }; 333 334 StatisticsProvider* StatisticsProvider::GetInstance() { 335 if (base::chromeos::IsRunningOnChromeOS()) { 336 return StatisticsProviderImpl::GetInstance(); 337 } else { 338 return StatisticsProviderStubImpl::GetInstance(); 339 } 340 } 341 342 } // namespace system 343 } // namespace chromeos 344