/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "uploader/system_profile_cache.h" #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 <brillo/osrelease_reader.h> #include <string> #include <update_engine/client.h> #include <vector> #include "constants.h" #include "persistent_integer.h" #include "uploader/metrics_log_base.h" #include "uploader/proto/chrome_user_metrics_extension.pb.h" namespace { const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId"; } // 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() : initialized_(false), testing_(false), metrics_directory_(metrics::kMetricsdDirectory), session_id_(new chromeos_metrics::PersistentInteger( kPersistentSessionIdFilename, metrics_directory_)) {} SystemProfileCache::SystemProfileCache(bool testing, const base::FilePath& metrics_directory) : initialized_(false), testing_(testing), metrics_directory_(metrics_directory), session_id_(new chromeos_metrics::PersistentInteger( kPersistentSessionIdFilename, metrics_directory)) {} bool SystemProfileCache::Initialize() { CHECK(!initialized_) << "this should be called only once in the metrics_daemon lifetime."; brillo::OsReleaseReader reader; std::string channel; if (testing_) { reader.LoadTestingOnly(metrics_directory_); channel = "unknown"; } else { reader.Load(); auto client = update_engine::UpdateEngineClient::CreateInstance(); if (!client) { LOG(ERROR) << "failed to create the update engine client"; return false; } if (!client->GetChannel(&channel)) { LOG(ERROR) << "failed to read the current channel from update engine."; return false; } } if (!reader.GetString(metrics::kProductId, &profile_.product_id) || profile_.product_id.empty()) { LOG(ERROR) << "product_id is not set."; return false; } if (!reader.GetString(metrics::kProductVersion, &profile_.version)) { LOG(ERROR) << "failed to read the product version"; } if (channel.empty() || profile_.version.empty()) { // If the channel or version is missing, the image is not official. // In this case, set the channel to unknown and the version to 0.0.0.0 to // avoid polluting the production data. channel = ""; profile_.version = metrics::kDefaultVersion; } std::string guid_path = metrics_directory_.Append( metrics::kMetricsGUIDFileName).value(); profile_.client_id = testing_ ? "client_id_test" : GetPersistentGUID(guid_path); profile_.model_manifest_id = "unknown"; if (!testing_) { brillo::KeyValueStore weave_config; if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) { LOG(ERROR) << "Failed to load the weave configuration file."; } else if (!weave_config.GetString(metrics::kModelManifestId, &profile_.model_manifest_id)) { LOG(ERROR) << "The model manifest id (model_id) is undefined in " << metrics::kWeaveConfigurationFile; } } profile_.channel = ProtoChannelFromString(channel); // 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. session_id_->Add(1); profile_.session_id = static_cast<int32_t>(session_id_->Get()); initialized_ = true; return initialized_; } bool SystemProfileCache::InitializeOrCheck() { return initialized_ || Initialize(); } bool SystemProfileCache::Populate( metrics::ChromeUserMetricsExtension* metrics_proto) { CHECK(metrics_proto); if (not InitializeOrCheck()) { return false; } // 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(9); metrics::SystemProfileProto* profile_proto = metrics_proto->mutable_system_profile(); profile_proto->mutable_hardware()->set_hardware_class( profile_.model_manifest_id); profile_proto->set_app_version(profile_.version); profile_proto->set_channel(profile_.channel); metrics::SystemProfileProto_BrilloDeviceData* device_data = profile_proto->mutable_brillo(); device_data->set_product_id(profile_.product_id); return true; } 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; } 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; }