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