blob: 4bed7b56ae6905bd1e097bdbfd6920771d3d1432 [file] [log] [blame]
// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "metrics/uploader/system_profile_cache.h"
#include <string>
#include <vector>
#include "base/files/file_util.h"
#include "base/guid.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "metrics/persistent_integer.h"
#include "metrics/uploader/metrics_log_base.h"
#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h"
#include "vboot/crossystem.h"
namespace {
const char kPersistentGUIDFile[] = "/var/lib/metrics/Sysinfo.GUID";
const char kPersistentSessionIdFilename[] =
"/var/lib/metrics/Sysinfo.SessionId";
const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID";
} // namespace
std::string ChannelToString(
const metrics::SystemProfileProto_Channel& channel) {
switch (channel) {
case metrics::SystemProfileProto::CHANNEL_STABLE:
return "STABLE";
case metrics::SystemProfileProto::CHANNEL_DEV:
return "DEV";
case metrics::SystemProfileProto::CHANNEL_BETA:
return "BETA";
case metrics::SystemProfileProto::CHANNEL_CANARY:
return "CANARY";
default:
return "UNKNOWN";
}
}
SystemProfileCache::SystemProfileCache() : SystemProfileCache(false, "/") {}
SystemProfileCache::SystemProfileCache(bool testing,
const std::string& config_root)
: initialized_(false), testing_(testing), config_root_(config_root) {
base::FilePath backing_file(kPersistentSessionIdFilename);
session_id_.reset(new chromeos_metrics::PersistentInteger(backing_file));
}
bool SystemProfileCache::Initialize() {
CHECK(!initialized_)
<< "this should be called only once in the metrics_daemon lifetime.";
std::string chromeos_version;
std::string board;
std::string build_type;
if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME",
&profile_.os_name) ||
!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION",
&profile_.os_version) ||
!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) ||
!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
&build_type) ||
!GetChromeOSVersion(&chromeos_version)) {
DLOG(ERROR) << "failing to initialize profile cache";
return false;
}
if (!GetHardwareId(&profile_.hardware_class)) {
profile_.hardware_class.clear();
}
std::string channel_string;
base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string);
profile_.channel = ProtoChannelFromString(channel_string);
profile_.app_version = chromeos_version + " (" + build_type + ")" +
ChannelToString(profile_.channel) + " " + board;
// If the product id is not defined, use the default one from the protobuf.
profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME;
if (GetProductId(&profile_.product_id)) {
DLOG(INFO) << "Set the product id to " << profile_.product_id;
}
profile_.client_id =
testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile);
// Increment the session_id everytime we initialize this. If metrics_daemon
// does not crash, this should correspond to the number of reboots of the
// system.
// TODO(bsimonnet): Change this to map to the number of time system-services
// is started.
session_id_->Add(1);
profile_.session_id = static_cast<int32_t>(session_id_->Get());
initialized_ = true;
return initialized_;
}
bool SystemProfileCache::InitializeOrCheck() {
return initialized_ || Initialize();
}
void SystemProfileCache::Populate(
metrics::ChromeUserMetricsExtension* metrics_proto) {
CHECK(metrics_proto);
CHECK(InitializeOrCheck()) << "failed to initialize system information.";
// The client id is hashed before being sent.
metrics_proto->set_client_id(
metrics::MetricsLogBase::Hash(profile_.client_id));
metrics_proto->set_session_id(profile_.session_id);
// Sets the product id.
metrics_proto->set_product(profile_.product_id);
metrics::SystemProfileProto* profile_proto =
metrics_proto->mutable_system_profile();
profile_proto->mutable_hardware()->set_hardware_class(
profile_.hardware_class);
profile_proto->set_app_version(profile_.app_version);
profile_proto->set_channel(profile_.channel);
metrics::SystemProfileProto_OS* os = profile_proto->mutable_os();
os->set_name(profile_.os_name);
os->set_version(profile_.os_version);
}
std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) {
std::string guid;
base::FilePath filepath(filename);
if (!base::ReadFileToString(filepath, &guid)) {
guid = base::GenerateGUID();
// If we can't read or write the file, the guid will not be preserved during
// the next reboot. Crash.
CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
}
return guid;
}
bool SystemProfileCache::GetChromeOSVersion(std::string* version) {
if (testing_) {
*version = "0.0.0.0";
return true;
}
std::string milestone, build, branch, patch;
unsigned tmp;
if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_CHROME_MILESTONE",
&milestone) &&
base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_NUMBER",
&build) &&
base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BRANCH_NUMBER",
&branch) &&
base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_PATCH_NUMBER",
&patch)) {
// Convert to uint to ensure those fields are positive numbers.
if (base::StringToUint(milestone, &tmp) &&
base::StringToUint(build, &tmp) && base::StringToUint(branch, &tmp) &&
base::StringToUint(patch, &tmp)) {
std::vector<std::string> parts = {milestone, build, branch, patch};
*version = base::JoinString(parts, ".");
return true;
}
DLOG(INFO) << "The milestone, build, branch or patch is not a positive "
<< "number.";
return false;
}
DLOG(INFO) << "Field missing from /etc/lsb-release";
return false;
}
bool SystemProfileCache::GetHardwareId(std::string* hwid) {
CHECK(hwid);
if (testing_) {
// if we are in test mode, we do not call crossystem directly.
DLOG(INFO) << "skipping hardware id";
*hwid = "";
return true;
}
char buffer[128];
if (buffer != VbGetSystemPropertyString("hwid", buffer, sizeof(buffer))) {
LOG(ERROR) << "error getting hwid";
return false;
}
*hwid = std::string(buffer);
return true;
}
bool SystemProfileCache::GetProductId(int* product_id) const {
brillo::OsReleaseReader reader;
if (testing_) {
base::FilePath root(config_root_);
reader.LoadTestingOnly(root);
} else {
reader.Load();
}
std::string id;
if (reader.GetString(kProductIdFieldName, &id)) {
CHECK(base::StringToInt(id, product_id))
<< "Failed to convert product_id " << id << " to int.";
return true;
}
return false;
}
metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
const std::string& channel) {
if (channel == "stable-channel") {
return metrics::SystemProfileProto::CHANNEL_STABLE;
} else if (channel == "dev-channel") {
return metrics::SystemProfileProto::CHANNEL_DEV;
} else if (channel == "beta-channel") {
return metrics::SystemProfileProto::CHANNEL_BETA;
} else if (channel == "canary-channel") {
return metrics::SystemProfileProto::CHANNEL_CANARY;
}
DLOG(INFO) << "unknown channel: " << channel;
return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
}