Home | History | Annotate | Download | only in uploader
      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