blob: 00def44af24e5aa4087e9344af8afe619fe093fc [file] [log] [blame]
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -07001// Copyright 2014 The Chromium OS 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
Bertrand SIMONNET4b915ae2015-07-28 15:38:14 -07005#include "uploader/system_profile_cache.h"
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -07006
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -07007#include <string>
8#include <vector>
9
Ben Chan51bf92a2014-09-05 08:21:06 -070010#include "base/files/file_util.h"
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070011#include "base/guid.h"
12#include "base/logging.h"
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070013#include "base/strings/string_number_conversions.h"
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -080014#include "base/strings/string_util.h"
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070015#include "base/sys_info.h"
Bertrand SIMONNET4b915ae2015-07-28 15:38:14 -070016#include "persistent_integer.h"
17#include "uploader/metrics_log_base.h"
18#include "uploader/proto/chrome_user_metrics_extension.pb.h"
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070019#include "vboot/crossystem.h"
20
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070021namespace {
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070022
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070023const char kPersistentGUIDFile[] = "/var/lib/metrics/Sysinfo.GUID";
24const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
25const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID";
26
27} // namespace
28
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -080029std::string ChannelToString(
30 const metrics::SystemProfileProto_Channel& channel) {
31 switch (channel) {
32 case metrics::SystemProfileProto::CHANNEL_STABLE:
33 return "STABLE";
34 case metrics::SystemProfileProto::CHANNEL_DEV:
35 return "DEV";
36 case metrics::SystemProfileProto::CHANNEL_BETA:
37 return "BETA";
38 case metrics::SystemProfileProto::CHANNEL_CANARY:
39 return "CANARY";
40 default:
41 return "UNKNOWN";
42 }
43}
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070044
45SystemProfileCache::SystemProfileCache()
46 : initialized_(false),
47 testing_(false),
48 config_root_("/"),
49 session_id_(new chromeos_metrics::PersistentInteger(
50 kPersistentSessionIdFilename)) {
51}
52
53SystemProfileCache::SystemProfileCache(bool testing,
54 const std::string& config_root)
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070055 : initialized_(false),
Bertrand SIMONNETeb815be2014-09-11 07:38:17 -070056 testing_(testing),
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070057 config_root_(config_root),
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070058 session_id_(new chromeos_metrics::PersistentInteger(
59 kPersistentSessionIdFilename)) {
60}
61
62bool SystemProfileCache::Initialize() {
63 CHECK(!initialized_)
64 << "this should be called only once in the metrics_daemon lifetime.";
65
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -080066 std::string chromeos_version;
67 std::string board;
68 std::string build_type;
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070069 if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME",
70 &profile_.os_name) ||
71 !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION",
72 &profile_.os_version) ||
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -080073 !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) ||
74 !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
75 &build_type) ||
76 !GetChromeOSVersion(&chromeos_version) ||
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070077 !GetHardwareId(&profile_.hardware_class)) {
78 DLOG(ERROR) << "failing to initialize profile cache";
79 return false;
80 }
81
82 std::string channel_string;
83 base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string);
84 profile_.channel = ProtoChannelFromString(channel_string);
85
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -080086 profile_.app_version = chromeos_version + " (" + build_type + ")" +
87 ChannelToString(profile_.channel) + " " + board;
88
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -070089 // If the product id is not defined, use the default one from the protobuf.
90 profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME;
91 if (GetProductId(&profile_.product_id)) {
92 DLOG(INFO) << "Set the product id to " << profile_.product_id;
93 }
94
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070095 profile_.client_id =
Bertrand SIMONNETeb815be2014-09-11 07:38:17 -070096 testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile);
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -070097
98 // Increment the session_id everytime we initialize this. If metrics_daemon
99 // does not crash, this should correspond to the number of reboots of the
100 // system.
101 // TODO(bsimonnet): Change this to map to the number of time system-services
102 // is started.
103 session_id_->Add(1);
Ben Chanf05ab402014-08-07 00:54:59 -0700104 profile_.session_id = static_cast<int32_t>(session_id_->Get());
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700105
106 initialized_ = true;
107 return initialized_;
108}
109
110bool SystemProfileCache::InitializeOrCheck() {
111 return initialized_ || Initialize();
112}
113
114void SystemProfileCache::Populate(
115 metrics::ChromeUserMetricsExtension* metrics_proto) {
116 CHECK(metrics_proto);
117 CHECK(InitializeOrCheck())
118 << "failed to initialize system information.";
119
120 // The client id is hashed before being sent.
121 metrics_proto->set_client_id(
122 metrics::MetricsLogBase::Hash(profile_.client_id));
123 metrics_proto->set_session_id(profile_.session_id);
124
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700125 // Sets the product id.
126 metrics_proto->set_product(profile_.product_id);
127
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700128 metrics::SystemProfileProto* profile_proto =
129 metrics_proto->mutable_system_profile();
130 profile_proto->mutable_hardware()->set_hardware_class(
131 profile_.hardware_class);
132 profile_proto->set_app_version(profile_.app_version);
133 profile_proto->set_channel(profile_.channel);
134
135 metrics::SystemProfileProto_OS* os = profile_proto->mutable_os();
136 os->set_name(profile_.os_name);
137 os->set_version(profile_.os_version);
138}
139
140std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) {
141 std::string guid;
142 base::FilePath filepath(filename);
143 if (!base::ReadFileToString(filepath, &guid)) {
144 guid = base::GenerateGUID();
145 // If we can't read or write the file, the guid will not be preserved during
146 // the next reboot. Crash.
147 CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
148 }
149 return guid;
150}
151
Bertrand SIMONNETa8bcc182014-12-19 09:35:15 -0800152bool SystemProfileCache::GetChromeOSVersion(std::string* version) {
153 if (testing_) {
154 *version = "0.0.0.0";
155 return true;
156 }
157
158 std::string milestone, build, branch, patch;
159 unsigned tmp;
160 if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_CHROME_MILESTONE",
161 &milestone) &&
162 base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_NUMBER",
163 &build) &&
164 base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BRANCH_NUMBER",
165 &branch) &&
166 base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_PATCH_NUMBER",
167 &patch)) {
168 // Convert to uint to ensure those fields are positive numbers.
169 if (base::StringToUint(milestone, &tmp) &&
170 base::StringToUint(build, &tmp) &&
171 base::StringToUint(branch, &tmp) &&
172 base::StringToUint(patch, &tmp)) {
173 std::vector<std::string> parts = {milestone, build, branch, patch};
174 *version = JoinString(parts, '.');
175 return true;
176 }
177 DLOG(INFO) << "The milestone, build, branch or patch is not a positive "
178 << "number.";
179 return false;
180 }
181 DLOG(INFO) << "Field missing from /etc/lsb-release";
182 return false;
183}
184
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700185bool SystemProfileCache::GetHardwareId(std::string* hwid) {
186 CHECK(hwid);
187
Bertrand SIMONNETeb815be2014-09-11 07:38:17 -0700188 if (testing_) {
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700189 // if we are in test mode, we do not call crossystem directly.
190 DLOG(INFO) << "skipping hardware id";
191 *hwid = "";
192 return true;
193 }
194
195 char buffer[128];
196 if (buffer != VbGetSystemPropertyString("hwid", buffer, sizeof(buffer))) {
197 LOG(ERROR) << "error getting hwid";
198 return false;
199 }
200
201 *hwid = std::string(buffer);
202 return true;
203}
204
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700205bool SystemProfileCache::GetProductId(int* product_id) const {
206 chromeos::OsReleaseReader reader;
207 if (testing_) {
208 base::FilePath root(config_root_);
Nathan Bullockd78f3df2014-10-17 10:03:52 -0400209 reader.LoadTestingOnly(root);
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700210 } else {
Nathan Bullockd78f3df2014-10-17 10:03:52 -0400211 reader.Load();
Bertrand SIMONNET71a62ef2014-10-07 11:26:25 -0700212 }
213
214 std::string id;
215 if (reader.GetString(kProductIdFieldName, &id)) {
216 CHECK(base::StringToInt(id, product_id)) << "Failed to convert product_id "
217 << id << " to int.";
218 return true;
219 }
220 return false;
221}
222
Bertrand SIMONNET46b49da2014-06-25 14:38:07 -0700223metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
224 const std::string& channel) {
225
226 if (channel == "stable-channel") {
227 return metrics::SystemProfileProto::CHANNEL_STABLE;
228 } else if (channel == "dev-channel") {
229 return metrics::SystemProfileProto::CHANNEL_DEV;
230 } else if (channel == "beta-channel") {
231 return metrics::SystemProfileProto::CHANNEL_BETA;
232 } else if (channel == "canary-channel") {
233 return metrics::SystemProfileProto::CHANNEL_CANARY;
234 }
235
236 DLOG(INFO) << "unknown channel: " << channel;
237 return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
238}