1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "uploader/system_profile_cache.h" 18 19 #include <base/files/file_util.h> 20 #include <base/guid.h> 21 #include <base/logging.h> 22 #include <base/strings/string_number_conversions.h> 23 #include <base/strings/string_util.h> 24 #include <brillo/osrelease_reader.h> 25 #include <string> 26 #include <update_engine/client.h> 27 #include <vector> 28 29 #include "constants.h" 30 #include "persistent_integer.h" 31 #include "uploader/metrics_log_base.h" 32 #include "uploader/proto/chrome_user_metrics_extension.pb.h" 33 34 namespace { 35 36 const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId"; 37 38 } // namespace 39 40 std::string ChannelToString( 41 const metrics::SystemProfileProto_Channel& channel) { 42 switch (channel) { 43 case metrics::SystemProfileProto::CHANNEL_STABLE: 44 return "STABLE"; 45 case metrics::SystemProfileProto::CHANNEL_DEV: 46 return "DEV"; 47 case metrics::SystemProfileProto::CHANNEL_BETA: 48 return "BETA"; 49 case metrics::SystemProfileProto::CHANNEL_CANARY: 50 return "CANARY"; 51 default: 52 return "UNKNOWN"; 53 } 54 } 55 56 SystemProfileCache::SystemProfileCache() 57 : initialized_(false), 58 testing_(false), 59 metrics_directory_(metrics::kMetricsdDirectory), 60 session_id_(new chromeos_metrics::PersistentInteger( 61 kPersistentSessionIdFilename, metrics_directory_)) {} 62 63 SystemProfileCache::SystemProfileCache(bool testing, 64 const base::FilePath& metrics_directory) 65 : initialized_(false), 66 testing_(testing), 67 metrics_directory_(metrics_directory), 68 session_id_(new chromeos_metrics::PersistentInteger( 69 kPersistentSessionIdFilename, metrics_directory)) {} 70 71 bool SystemProfileCache::Initialize() { 72 CHECK(!initialized_) 73 << "this should be called only once in the metrics_daemon lifetime."; 74 75 brillo::OsReleaseReader reader; 76 std::string channel; 77 if (testing_) { 78 reader.LoadTestingOnly(metrics_directory_); 79 channel = "unknown"; 80 } else { 81 reader.Load(); 82 auto client = update_engine::UpdateEngineClient::CreateInstance(); 83 if (!client) { 84 LOG(ERROR) << "failed to create the update engine client"; 85 return false; 86 } 87 if (!client->GetChannel(&channel)) { 88 LOG(ERROR) << "failed to read the current channel from update engine."; 89 return false; 90 } 91 } 92 93 if (!reader.GetString(metrics::kProductId, &profile_.product_id) 94 || profile_.product_id.empty()) { 95 LOG(ERROR) << "product_id is not set."; 96 return false; 97 } 98 99 if (!reader.GetString(metrics::kProductVersion, &profile_.version)) { 100 LOG(ERROR) << "failed to read the product version"; 101 } 102 103 if (channel.empty() || profile_.version.empty()) { 104 // If the channel or version is missing, the image is not official. 105 // In this case, set the channel to unknown and the version to 0.0.0.0 to 106 // avoid polluting the production data. 107 channel = ""; 108 profile_.version = metrics::kDefaultVersion; 109 } 110 std::string guid_path = metrics_directory_.Append( 111 metrics::kMetricsGUIDFileName).value(); 112 profile_.client_id = testing_ ? 113 "client_id_test" : 114 GetPersistentGUID(guid_path); 115 profile_.model_manifest_id = "unknown"; 116 if (!testing_) { 117 brillo::KeyValueStore weave_config; 118 if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) { 119 LOG(ERROR) << "Failed to load the weave configuration file."; 120 } else if (!weave_config.GetString(metrics::kModelManifestId, 121 &profile_.model_manifest_id)) { 122 LOG(ERROR) << "The model manifest id (model_id) is undefined in " 123 << metrics::kWeaveConfigurationFile; 124 } 125 } 126 127 profile_.channel = ProtoChannelFromString(channel); 128 129 // Increment the session_id everytime we initialize this. If metrics_daemon 130 // does not crash, this should correspond to the number of reboots of the 131 // system. 132 session_id_->Add(1); 133 profile_.session_id = static_cast<int32_t>(session_id_->Get()); 134 135 initialized_ = true; 136 return initialized_; 137 } 138 139 bool SystemProfileCache::InitializeOrCheck() { 140 return initialized_ || Initialize(); 141 } 142 143 bool SystemProfileCache::Populate( 144 metrics::ChromeUserMetricsExtension* metrics_proto) { 145 CHECK(metrics_proto); 146 if (not InitializeOrCheck()) { 147 return false; 148 } 149 150 // The client id is hashed before being sent. 151 metrics_proto->set_client_id( 152 metrics::MetricsLogBase::Hash(profile_.client_id)); 153 metrics_proto->set_session_id(profile_.session_id); 154 155 // Sets the product id. 156 metrics_proto->set_product(9); 157 158 metrics::SystemProfileProto* profile_proto = 159 metrics_proto->mutable_system_profile(); 160 profile_proto->mutable_hardware()->set_hardware_class( 161 profile_.model_manifest_id); 162 profile_proto->set_app_version(profile_.version); 163 profile_proto->set_channel(profile_.channel); 164 metrics::SystemProfileProto_BrilloDeviceData* device_data = 165 profile_proto->mutable_brillo(); 166 device_data->set_product_id(profile_.product_id); 167 168 return true; 169 } 170 171 std::string SystemProfileCache::GetPersistentGUID( 172 const std::string& filename) { 173 std::string guid; 174 base::FilePath filepath(filename); 175 if (!base::ReadFileToString(filepath, &guid)) { 176 guid = base::GenerateGUID(); 177 // If we can't read or write the file, the guid will not be preserved during 178 // the next reboot. Crash. 179 CHECK(base::WriteFile(filepath, guid.c_str(), guid.size())); 180 } 181 return guid; 182 } 183 184 metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString( 185 const std::string& channel) { 186 if (channel == "stable-channel") { 187 return metrics::SystemProfileProto::CHANNEL_STABLE; 188 } else if (channel == "dev-channel") { 189 return metrics::SystemProfileProto::CHANNEL_DEV; 190 } else if (channel == "beta-channel") { 191 return metrics::SystemProfileProto::CHANNEL_BETA; 192 } else if (channel == "canary-channel") { 193 return metrics::SystemProfileProto::CHANNEL_CANARY; 194 } 195 196 DLOG(INFO) << "unknown channel: " << channel; 197 return metrics::SystemProfileProto::CHANNEL_UNKNOWN; 198 } 199