a7675bcc896ead5c9008ccd8e9ce14930406fdb5
[platform/framework/web/crosswalk.git] / src / chromeos / system / statistics_provider.cc
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.
4
5 #include "chromeos/system/statistics_provider.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_path.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/singleton.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/synchronization/cancellation_flag.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/sys_info.h"
17 #include "base/task_runner.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/time/time.h"
20 #include "chromeos/app_mode/kiosk_oem_manifest_parser.h"
21 #include "chromeos/chromeos_constants.h"
22 #include "chromeos/chromeos_switches.h"
23 #include "chromeos/system/name_value_pairs_parser.h"
24
25 namespace chromeos {
26 namespace system {
27
28 namespace {
29
30 // Path to the tool used to get system info, and delimiters for the output
31 // format of the tool.
32 const char* kCrosSystemTool[] = { "/usr/bin/crossystem" };
33 const char kCrosSystemEq[] = "=";
34 const char kCrosSystemDelim[] = "\n";
35 const char kCrosSystemCommentDelim[] = "#";
36 const char kCrosSystemUnknownValue[] = "(error)";
37
38 const char kHardwareClassCrosSystemKey[] = "hwid";
39 const char kUnknownHardwareClass[] = "unknown";
40 const char kSerialNumber[] = "sn";
41
42 // File to get machine hardware info from, and key/value delimiters of
43 // the file. machine-info is generated only for OOBE and enterprise enrollment
44 // and may not be present. See login-manager/init/machine-info.conf.
45 const char kMachineHardwareInfoFile[] = "/tmp/machine-info";
46 const char kMachineHardwareInfoEq[] = "=";
47 const char kMachineHardwareInfoDelim[] = " \n";
48
49 // File to get ECHO coupon info from, and key/value delimiters of
50 // the file.
51 const char kEchoCouponFile[] = "/var/cache/echo/vpd_echo.txt";
52 const char kEchoCouponEq[] = "=";
53 const char kEchoCouponDelim[] = "\n";
54
55 // File to get VPD info from, and key/value delimiters of the file.
56 const char kVpdFile[] = "/var/log/vpd_2.0.txt";
57 const char kVpdEq[] = "=";
58 const char kVpdDelim[] = "\n";
59
60 // Timeout that we should wait for statistics to get loaded
61 const int kTimeoutSecs = 3;
62
63 // The location of OEM manifest file used to trigger OOBE flow for kiosk mode.
64 const CommandLine::CharType kOemManifestFilePath[] =
65     FILE_PATH_LITERAL("/usr/share/oem/oobe/manifest.json");
66
67 }  // namespace
68
69 // Key values for GetMachineStatistic()/GetMachineFlag() calls.
70 const char kDevSwitchBootMode[] = "devsw_boot";
71 const char kCustomizationIdKey[] = "customization_id";
72 const char kHardwareClassKey[] = "hardware_class";
73 const char kOffersCouponCodeKey[] = "ubind_attribute";
74 const char kOffersGroupCodeKey[] = "gbind_attribute";
75 const char kRlzBrandCodeKey[] = "rlz_brand_code";
76 const char kActivateDateKey[] = "ActivateDate";
77
78 // OEM specific statistics. Must be prefixed with "oem_".
79 const char kOemCanExitEnterpriseEnrollmentKey[] = "oem_can_exit_enrollment";
80 const char kOemDeviceRequisitionKey[] = "oem_device_requisition";
81 const char kOemIsEnterpriseManagedKey[] = "oem_enterprise_managed";
82 const char kOemKeyboardDrivenOobeKey[] = "oem_keyboard_driven_oobe";
83
84 bool HasOemPrefix(const std::string& name) {
85   return name.substr(0, 4) == "oem_";
86 }
87
88 // The StatisticsProvider implementation used in production.
89 class StatisticsProviderImpl : public StatisticsProvider {
90  public:
91   // StatisticsProvider implementation:
92   virtual void StartLoadingMachineStatistics(
93       const scoped_refptr<base::TaskRunner>& file_task_runner,
94       bool load_oem_manifest) override;
95   virtual bool GetMachineStatistic(const std::string& name,
96                                    std::string* result) override;
97   virtual bool HasMachineStatistic(const std::string& name) override;
98   virtual bool GetMachineFlag(const std::string& name, bool* result) override;
99   virtual bool HasMachineFlag(const std::string& name) override;
100   virtual void Shutdown() override;
101
102   static StatisticsProviderImpl* GetInstance();
103
104  protected:
105   typedef std::map<std::string, bool> MachineFlags;
106   friend struct DefaultSingletonTraits<StatisticsProviderImpl>;
107
108   StatisticsProviderImpl();
109   virtual ~StatisticsProviderImpl();
110
111   // Waits up to |kTimeoutSecs| for statistics to be loaded. Returns true if
112   // they were loaded successfully.
113   bool WaitForStatisticsLoaded();
114
115   // Loads the machine statistics off of disk. Runs on the file thread.
116   void LoadMachineStatistics(bool load_oem_manifest);
117
118   // Loads the OEM statistics off of disk. Runs on the file thread.
119   void LoadOemManifestFromFile(const base::FilePath& file);
120
121   bool load_statistics_started_;
122   NameValuePairsParser::NameValueMap machine_info_;
123   MachineFlags machine_flags_;
124   base::CancellationFlag cancellation_flag_;
125   // |on_statistics_loaded_| protects |machine_info_| and |machine_flags_|.
126   base::WaitableEvent on_statistics_loaded_;
127   bool oem_manifest_loaded_;
128
129  private:
130   DISALLOW_COPY_AND_ASSIGN(StatisticsProviderImpl);
131 };
132
133 bool StatisticsProviderImpl::WaitForStatisticsLoaded() {
134   CHECK(load_statistics_started_);
135   if (on_statistics_loaded_.IsSignaled())
136     return true;
137
138   // Block if the statistics are not loaded yet. Normally this shouldn't
139   // happen except during OOBE.
140   base::Time start_time = base::Time::Now();
141   base::ThreadRestrictions::ScopedAllowWait allow_wait;
142   on_statistics_loaded_.TimedWait(base::TimeDelta::FromSeconds(kTimeoutSecs));
143
144   base::TimeDelta dtime = base::Time::Now() - start_time;
145   if (on_statistics_loaded_.IsSignaled()) {
146     LOG(ERROR) << "Statistics loaded after waiting "
147                << dtime.InMilliseconds() << "ms. ";
148     return true;
149   }
150
151   LOG(ERROR) << "Statistics not loaded after waiting "
152              << dtime.InMilliseconds() << "ms. ";
153   return false;
154 }
155
156 bool StatisticsProviderImpl::GetMachineStatistic(const std::string& name,
157                                                  std::string* result) {
158   VLOG(1) << "Machine Statistic requested: " << name;
159   if (!WaitForStatisticsLoaded()) {
160     LOG(ERROR) << "GetMachineStatistic called before load started: " << name;
161     return false;
162   }
163
164   NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
165   if (iter == machine_info_.end()) {
166     if (base::SysInfo::IsRunningOnChromeOS() &&
167         (oem_manifest_loaded_ || !HasOemPrefix(name))) {
168       LOG(WARNING) << "Requested statistic not found: " << name;
169     }
170     return false;
171   }
172   *result = iter->second;
173   return true;
174 }
175
176 bool StatisticsProviderImpl::HasMachineStatistic(const std::string& name) {
177   std::string result;
178   return GetMachineStatistic(name, &result);
179 }
180
181 bool StatisticsProviderImpl::GetMachineFlag(const std::string& name,
182                                             bool* result) {
183   VLOG(1) << "Machine Flag requested: " << name;
184   if (!WaitForStatisticsLoaded()) {
185     LOG(ERROR) << "GetMachineFlag called before load started: " << name;
186     return false;
187   }
188
189   MachineFlags::const_iterator iter = machine_flags_.find(name);
190   if (iter == machine_flags_.end()) {
191     if (base::SysInfo::IsRunningOnChromeOS() &&
192         (oem_manifest_loaded_ || !HasOemPrefix(name))) {
193       LOG(WARNING) << "Requested machine flag not found: " << name;
194     }
195     return false;
196   }
197   *result = iter->second;
198   return true;
199 }
200
201 bool StatisticsProviderImpl::HasMachineFlag(const std::string& name) {
202   bool result = false;
203   return GetMachineFlag(name, &result);
204 }
205
206 void StatisticsProviderImpl::Shutdown() {
207   cancellation_flag_.Set();  // Cancel any pending loads
208 }
209
210 StatisticsProviderImpl::StatisticsProviderImpl()
211     : load_statistics_started_(false),
212       on_statistics_loaded_(true  /* manual_reset */,
213                             false /* initially_signaled */),
214       oem_manifest_loaded_(false) {
215 }
216
217 StatisticsProviderImpl::~StatisticsProviderImpl() {
218 }
219
220 void StatisticsProviderImpl::StartLoadingMachineStatistics(
221     const scoped_refptr<base::TaskRunner>& file_task_runner,
222     bool load_oem_manifest) {
223   CHECK(!load_statistics_started_);
224   load_statistics_started_ = true;
225
226   VLOG(1) << "Started loading statistics. Load OEM Manifest: "
227           << load_oem_manifest;
228
229   file_task_runner->PostTask(
230       FROM_HERE,
231       base::Bind(&StatisticsProviderImpl::LoadMachineStatistics,
232                  base::Unretained(this),
233                  load_oem_manifest));
234 }
235
236 void StatisticsProviderImpl::LoadMachineStatistics(bool load_oem_manifest) {
237   // Run from the file task runner. StatisticsProviderImpl is a Singleton<> and
238   // will not be destroyed until after threads have been stopped, so this test
239   // is always safe.
240   if (cancellation_flag_.IsSet())
241     return;
242
243   NameValuePairsParser parser(&machine_info_);
244   if (base::SysInfo::IsRunningOnChromeOS()) {
245     // Parse all of the key/value pairs from the crossystem tool.
246     if (!parser.ParseNameValuePairsFromTool(arraysize(kCrosSystemTool),
247                                             kCrosSystemTool,
248                                             kCrosSystemEq,
249                                             kCrosSystemDelim,
250                                             kCrosSystemCommentDelim)) {
251       LOG(ERROR) << "Errors parsing output from: " << kCrosSystemTool;
252     }
253   }
254
255   parser.GetNameValuePairsFromFile(base::FilePath(kMachineHardwareInfoFile),
256                                    kMachineHardwareInfoEq,
257                                    kMachineHardwareInfoDelim);
258   parser.GetNameValuePairsFromFile(base::FilePath(kEchoCouponFile),
259                                    kEchoCouponEq,
260                                    kEchoCouponDelim);
261   parser.GetNameValuePairsFromFile(base::FilePath(kVpdFile),
262                                    kVpdEq,
263                                    kVpdDelim);
264
265   // Ensure that the hardware class key is present with the expected
266   // key name, and if it couldn't be retrieved, that the value is "unknown".
267   std::string hardware_class = machine_info_[kHardwareClassCrosSystemKey];
268   if (hardware_class.empty() || hardware_class == kCrosSystemUnknownValue)
269     machine_info_[kHardwareClassKey] = kUnknownHardwareClass;
270   else
271     machine_info_[kHardwareClassKey] = hardware_class;
272
273   if (load_oem_manifest) {
274     // If kAppOemManifestFile switch is specified, load OEM Manifest file.
275     CommandLine* command_line = CommandLine::ForCurrentProcess();
276     if (command_line->HasSwitch(switches::kAppOemManifestFile)) {
277       LoadOemManifestFromFile(
278           command_line->GetSwitchValuePath(switches::kAppOemManifestFile));
279     } else if (base::SysInfo::IsRunningOnChromeOS()) {
280       LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath));
281     }
282   }
283
284   if (!base::SysInfo::IsRunningOnChromeOS() &&
285       machine_info_.find(kSerialNumber) == machine_info_.end()) {
286     // Set stub value for testing. A time value is appended to avoid clashes of
287     // the same serial for the same domain, which would invalidate earlier
288     // enrollments. A fake /tmp/machine-info file should be used instead if
289     // a stable serial is needed, e.g. to test re-enrollment.
290     base::TimeDelta time = base::Time::Now() - base::Time::UnixEpoch();
291     machine_info_[kSerialNumber] =
292         "stub_serial_number_" + base::Int64ToString(time.InSeconds());
293   }
294
295   // Finished loading the statistics.
296   on_statistics_loaded_.Signal();
297   VLOG(1) << "Finished loading statistics.";
298 }
299
300 void StatisticsProviderImpl::LoadOemManifestFromFile(
301     const base::FilePath& file) {
302   // Called from LoadMachineStatistics. Check cancellation_flag_ again here.
303   if (cancellation_flag_.IsSet())
304     return;
305
306   KioskOemManifestParser::Manifest oem_manifest;
307   if (!KioskOemManifestParser::Load(file, &oem_manifest)) {
308     LOG(WARNING) << "Unable to load OEM Manifest file: " << file.value();
309     return;
310   }
311   machine_info_[kOemDeviceRequisitionKey] =
312       oem_manifest.device_requisition;
313   machine_flags_[kOemIsEnterpriseManagedKey] =
314       oem_manifest.enterprise_managed;
315   machine_flags_[kOemCanExitEnterpriseEnrollmentKey] =
316       oem_manifest.can_exit_enrollment;
317   machine_flags_[kOemKeyboardDrivenOobeKey] =
318       oem_manifest.keyboard_driven_oobe;
319
320   oem_manifest_loaded_ = true;
321   VLOG(1) << "Loaded OEM Manifest statistics from " << file.value();
322 }
323
324 StatisticsProviderImpl* StatisticsProviderImpl::GetInstance() {
325   return Singleton<StatisticsProviderImpl,
326                    DefaultSingletonTraits<StatisticsProviderImpl> >::get();
327 }
328
329 static StatisticsProvider* g_test_statistics_provider = NULL;
330
331 // static
332 StatisticsProvider* StatisticsProvider::GetInstance() {
333   if (g_test_statistics_provider)
334     return g_test_statistics_provider;
335   return StatisticsProviderImpl::GetInstance();
336 }
337
338 // static
339 void StatisticsProvider::SetTestProvider(StatisticsProvider* test_provider) {
340   g_test_statistics_provider = test_provider;
341 }
342
343 }  // namespace system
344 }  // namespace chromeos