Add CHECK to diagnose the crash.
[chromium-blink-merge.git] / chromeos / system / statistics_provider.cc
blobbac717360345205f2b0fa6e9d399d8ae2c3e1f37
1 // Copyright 2013 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.
5 #include "chromeos/system/statistics_provider.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/memory/singleton.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/synchronization/cancellation_flag.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/sys_info.h"
19 #include "base/task_runner.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/time/time.h"
22 #include "chromeos/app_mode/kiosk_oem_manifest_parser.h"
23 #include "chromeos/chromeos_constants.h"
24 #include "chromeos/chromeos_paths.h"
25 #include "chromeos/chromeos_switches.h"
26 #include "chromeos/system/name_value_pairs_parser.h"
28 namespace chromeos {
29 namespace system {
31 namespace {
33 // Path to the tool used to get system info, and delimiters for the output
34 // format of the tool.
35 const char* kCrosSystemTool[] = { "/usr/bin/crossystem" };
36 const char kCrosSystemEq[] = "=";
37 const char kCrosSystemDelim[] = "\n";
38 const char kCrosSystemCommentDelim[] = "#";
39 const char kCrosSystemUnknownValue[] = "(error)";
41 const char kHardwareClassCrosSystemKey[] = "hwid";
42 const char kUnknownHardwareClass[] = "unknown";
44 // Key/value delimiters of machine hardware info file. machine-info is generated
45 // only for OOBE and enterprise enrollment and may not be present. See
46 // login-manager/init/machine-info.conf.
47 const char kMachineHardwareInfoEq[] = "=";
48 const char kMachineHardwareInfoDelim[] = " \n";
50 // File to get ECHO coupon info from, and key/value delimiters of
51 // the file.
52 const char kEchoCouponFile[] = "/var/cache/echo/vpd_echo.txt";
53 const char kEchoCouponEq[] = "=";
54 const char kEchoCouponDelim[] = "\n";
56 // File to get VPD info from, and key/value delimiters of the file.
57 const char kVpdFile[] = "/var/log/vpd_2.0.txt";
58 const char kVpdEq[] = "=";
59 const char kVpdDelim[] = "\n";
61 // Timeout that we should wait for statistics to get loaded
62 const int kTimeoutSecs = 3;
64 // The location of OEM manifest file used to trigger OOBE flow for kiosk mode.
65 const base::CommandLine::CharType kOemManifestFilePath[] =
66 FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json");
68 } // namespace
70 // Key values for GetMachineStatistic()/GetMachineFlag() calls.
71 const char kActivateDateKey[] = "ActivateDate";
72 const char kCustomizationIdKey[] = "customization_id";
73 const char kDevSwitchBootKey[] = "devsw_boot";
74 const char kDevSwitchBootValueDev[] = "1";
75 const char kDevSwitchBootValueVerified[] = "0";
76 const char kFirmwareTypeKey[] = "mainfw_type";
77 const char kFirmwareTypeValueDeveloper[] = "developer";
78 const char kFirmwareTypeValueNonchrome[] = "nonchrome";
79 const char kFirmwareTypeValueNormal[] = "normal";
80 const char kHardwareClassKey[] = "hardware_class";
81 const char kOffersCouponCodeKey[] = "ubind_attribute";
82 const char kOffersGroupCodeKey[] = "gbind_attribute";
83 const char kRlzBrandCodeKey[] = "rlz_brand_code";
84 const char kWriteProtectSwitchBootKey[] = "wpsw_boot";
85 const char kWriteProtectSwitchBootValueOff[] = "0";
86 const char kWriteProtectSwitchBootValueOn[] = "1";
88 // OEM specific statistics. Must be prefixed with "oem_".
89 const char kOemCanExitEnterpriseEnrollmentKey[] = "oem_can_exit_enrollment";
90 const char kOemDeviceRequisitionKey[] = "oem_device_requisition";
91 const char kOemIsEnterpriseManagedKey[] = "oem_enterprise_managed";
92 const char kOemKeyboardDrivenOobeKey[] = "oem_keyboard_driven_oobe";
94 bool HasOemPrefix(const std::string& name) {
95 return name.substr(0, 4) == "oem_";
98 // The StatisticsProvider implementation used in production.
99 class StatisticsProviderImpl : public StatisticsProvider {
100 public:
101 // StatisticsProvider implementation:
102 void StartLoadingMachineStatistics(
103 const scoped_refptr<base::TaskRunner>& file_task_runner,
104 bool load_oem_manifest) override;
105 bool GetMachineStatistic(const std::string& name,
106 std::string* result) override;
107 bool HasMachineStatistic(const std::string& name) override;
108 bool GetMachineFlag(const std::string& name, bool* result) override;
109 bool HasMachineFlag(const std::string& name) override;
110 void Shutdown() override;
112 static StatisticsProviderImpl* GetInstance();
114 protected:
115 typedef std::map<std::string, bool> MachineFlags;
116 friend struct DefaultSingletonTraits<StatisticsProviderImpl>;
118 StatisticsProviderImpl();
119 ~StatisticsProviderImpl() override;
121 // Waits up to |kTimeoutSecs| for statistics to be loaded. Returns true if
122 // they were loaded successfully.
123 bool WaitForStatisticsLoaded();
125 // Loads the machine statistics off of disk. Runs on the file thread.
126 void LoadMachineStatistics(bool load_oem_manifest);
128 // Loads the OEM statistics off of disk. Runs on the file thread.
129 void LoadOemManifestFromFile(const base::FilePath& file);
131 bool load_statistics_started_;
132 NameValuePairsParser::NameValueMap machine_info_;
133 MachineFlags machine_flags_;
134 base::CancellationFlag cancellation_flag_;
135 // |on_statistics_loaded_| protects |machine_info_| and |machine_flags_|.
136 base::WaitableEvent on_statistics_loaded_;
137 bool oem_manifest_loaded_;
139 private:
140 DISALLOW_COPY_AND_ASSIGN(StatisticsProviderImpl);
143 bool StatisticsProviderImpl::WaitForStatisticsLoaded() {
144 CHECK(load_statistics_started_);
145 if (on_statistics_loaded_.IsSignaled())
146 return true;
148 // Block if the statistics are not loaded yet. Normally this shouldn't
149 // happen except during OOBE.
150 base::Time start_time = base::Time::Now();
151 base::ThreadRestrictions::ScopedAllowWait allow_wait;
152 on_statistics_loaded_.TimedWait(base::TimeDelta::FromSeconds(kTimeoutSecs));
154 base::TimeDelta dtime = base::Time::Now() - start_time;
155 if (on_statistics_loaded_.IsSignaled()) {
156 LOG(ERROR) << "Statistics loaded after waiting "
157 << dtime.InMilliseconds() << "ms. ";
158 return true;
161 LOG(ERROR) << "Statistics not loaded after waiting "
162 << dtime.InMilliseconds() << "ms. ";
163 return false;
166 bool StatisticsProviderImpl::GetMachineStatistic(const std::string& name,
167 std::string* result) {
168 VLOG(1) << "Machine Statistic requested: " << name;
169 if (!WaitForStatisticsLoaded()) {
170 LOG(ERROR) << "GetMachineStatistic called before load started: " << name;
171 return false;
174 NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
175 if (iter == machine_info_.end()) {
176 if (base::SysInfo::IsRunningOnChromeOS() &&
177 (oem_manifest_loaded_ || !HasOemPrefix(name))) {
178 LOG(WARNING) << "Requested statistic not found: " << name;
180 return false;
182 *result = iter->second;
183 return true;
186 bool StatisticsProviderImpl::HasMachineStatistic(const std::string& name) {
187 std::string result;
188 return GetMachineStatistic(name, &result);
191 bool StatisticsProviderImpl::GetMachineFlag(const std::string& name,
192 bool* result) {
193 VLOG(1) << "Machine Flag requested: " << name;
194 if (!WaitForStatisticsLoaded()) {
195 LOG(ERROR) << "GetMachineFlag called before load started: " << name;
196 return false;
199 MachineFlags::const_iterator iter = machine_flags_.find(name);
200 if (iter == machine_flags_.end()) {
201 if (base::SysInfo::IsRunningOnChromeOS() &&
202 (oem_manifest_loaded_ || !HasOemPrefix(name))) {
203 LOG(WARNING) << "Requested machine flag not found: " << name;
205 return false;
207 *result = iter->second;
208 return true;
211 bool StatisticsProviderImpl::HasMachineFlag(const std::string& name) {
212 bool result = false;
213 return GetMachineFlag(name, &result);
216 void StatisticsProviderImpl::Shutdown() {
217 cancellation_flag_.Set(); // Cancel any pending loads
220 StatisticsProviderImpl::StatisticsProviderImpl()
221 : load_statistics_started_(false),
222 on_statistics_loaded_(true /* manual_reset */,
223 false /* initially_signaled */),
224 oem_manifest_loaded_(false) {
227 StatisticsProviderImpl::~StatisticsProviderImpl() {
230 void StatisticsProviderImpl::StartLoadingMachineStatistics(
231 const scoped_refptr<base::TaskRunner>& file_task_runner,
232 bool load_oem_manifest) {
233 CHECK(!load_statistics_started_);
234 load_statistics_started_ = true;
236 VLOG(1) << "Started loading statistics. Load OEM Manifest: "
237 << load_oem_manifest;
239 file_task_runner->PostTask(
240 FROM_HERE,
241 base::Bind(&StatisticsProviderImpl::LoadMachineStatistics,
242 base::Unretained(this),
243 load_oem_manifest));
246 void StatisticsProviderImpl::LoadMachineStatistics(bool load_oem_manifest) {
247 // Run from the file task runner. StatisticsProviderImpl is a Singleton<> and
248 // will not be destroyed until after threads have been stopped, so this test
249 // is always safe.
250 if (cancellation_flag_.IsSet())
251 return;
253 NameValuePairsParser parser(&machine_info_);
254 if (base::SysInfo::IsRunningOnChromeOS()) {
255 // Parse all of the key/value pairs from the crossystem tool.
256 if (!parser.ParseNameValuePairsFromTool(arraysize(kCrosSystemTool),
257 kCrosSystemTool,
258 kCrosSystemEq,
259 kCrosSystemDelim,
260 kCrosSystemCommentDelim)) {
261 LOG(ERROR) << "Errors parsing output from: " << kCrosSystemTool;
265 base::FilePath machine_info_path;
266 PathService::Get(chromeos::FILE_MACHINE_INFO, &machine_info_path);
267 if (!base::SysInfo::IsRunningOnChromeOS() &&
268 !base::PathExists(machine_info_path)) {
269 // Use time value to create an unique stub serial because clashes of the
270 // same serial for the same domain invalidate earlier enrollments. Persist
271 // to disk to keep it constant across restarts (required for re-enrollment
272 // testing).
273 std::string stub_contents =
274 "\"serial_number\"=\"stub_" +
275 base::Int64ToString(base::Time::Now().ToJavaTime()) + "\"\n";
276 int bytes_written = base::WriteFile(machine_info_path,
277 stub_contents.c_str(),
278 stub_contents.size());
279 // static_cast<int> is fine because stub_contents is small.
280 if (bytes_written < static_cast<int>(stub_contents.size())) {
281 LOG(ERROR) << "Error writing machine info stub: "
282 << machine_info_path.value();
286 parser.GetNameValuePairsFromFile(machine_info_path,
287 kMachineHardwareInfoEq,
288 kMachineHardwareInfoDelim);
289 parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile),
290 kEchoCouponEq,
291 kEchoCouponDelim);
292 parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile),
293 kVpdEq,
294 kVpdDelim);
296 // Ensure that the hardware class key is present with the expected
297 // key name, and if it couldn't be retrieved, that the value is "unknown".
298 std::string hardware_class = machine_info_[kHardwareClassCrosSystemKey];
299 if (hardware_class.empty() || hardware_class == kCrosSystemUnknownValue)
300 machine_info_[kHardwareClassKey] = kUnknownHardwareClass;
301 else
302 machine_info_[kHardwareClassKey] = hardware_class;
304 if (load_oem_manifest) {
305 // If kAppOemManifestFile switch is specified, load OEM Manifest file.
306 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
307 if (command_line->HasSwitch(switches::kAppOemManifestFile)) {
308 LoadOemManifestFromFile(
309 command_line->GetSwitchValuePath(switches::kAppOemManifestFile));
310 } else if (base::SysInfo::IsRunningOnChromeOS()) {
311 LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath));
315 // Finished loading the statistics.
316 on_statistics_loaded_.Signal();
317 VLOG(1) << "Finished loading statistics.";
320 void StatisticsProviderImpl::LoadOemManifestFromFile(
321 const base::FilePath& file) {
322 // Called from LoadMachineStatistics. Check cancellation_flag_ again here.
323 if (cancellation_flag_.IsSet())
324 return;
326 KioskOemManifestParser::Manifest oem_manifest;
327 if (!KioskOemManifestParser::Load(file, &oem_manifest)) {
328 LOG(WARNING) << "Unable to load OEM Manifest file: " << file.value();
329 return;
331 machine_info_[kOemDeviceRequisitionKey] =
332 oem_manifest.device_requisition;
333 machine_flags_[kOemIsEnterpriseManagedKey] =
334 oem_manifest.enterprise_managed;
335 machine_flags_[kOemCanExitEnterpriseEnrollmentKey] =
336 oem_manifest.can_exit_enrollment;
337 machine_flags_[kOemKeyboardDrivenOobeKey] =
338 oem_manifest.keyboard_driven_oobe;
340 oem_manifest_loaded_ = true;
341 VLOG(1) << "Loaded OEM Manifest statistics from " << file.value();
344 StatisticsProviderImpl* StatisticsProviderImpl::GetInstance() {
345 return Singleton<StatisticsProviderImpl,
346 DefaultSingletonTraits<StatisticsProviderImpl> >::get();
349 static StatisticsProvider* g_test_statistics_provider = NULL;
351 // static
352 StatisticsProvider* StatisticsProvider::GetInstance() {
353 if (g_test_statistics_provider)
354 return g_test_statistics_provider;
355 return StatisticsProviderImpl::GetInstance();
358 // static
359 void StatisticsProvider::SetTestProvider(StatisticsProvider* test_provider) {
360 g_test_statistics_provider = test_provider;
363 } // namespace system
364 } // namespace chromeos