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 
ChannelToString(const metrics::SystemProfileProto_Channel & channel)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 
SystemProfileCache()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 
SystemProfileCache(bool testing,const base::FilePath & metrics_directory)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 
Initialize()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 
InitializeOrCheck()139 bool SystemProfileCache::InitializeOrCheck() {
140   return initialized_ || Initialize();
141 }
142 
Populate(metrics::ChromeUserMetricsExtension * metrics_proto)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 
GetPersistentGUID(const std::string & filename)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 
ProtoChannelFromString(const std::string & channel)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