metrics: generate app_version from standard lsb-release fields

app_version used to be set to CHROMEOS_RELEASE_DESCRIPTION which has no
structure and should never be used for parsing.

Now that various version numbers have been added to lsb-release, use those
fields to generate the version string.

The version string format is "A.B.C.D (Official Build) $CHANNEL $BOARD" with:
* A = chrome milestone
* B = build number
* C = branch number
* D = patch number
* CHANNEL is one of STABLE, DEV, BETA, CANARY
* BOARD is the board name

BUG=chromium:426889
TEST=trybot run on gizmo-paladin
TEST=`test_that -b gizmo gizmo platform_MetricsUploader`
TEST=`FEATURES=test emerge-gizmo metrics`

Change-Id: I624df14bd859e0c0279dd3de621e651150d30add
Reviewed-on: https://chromium-review.googlesource.com/236949
Tested-by: Bertrand Simonnet <bsimonnet@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Commit-Queue: Bertrand Simonnet <bsimonnet@chromium.org>
Trybot-Ready: Bertrand Simonnet <bsimonnet@chromium.org>
diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc
index 54c2693..28e0f09 100644
--- a/metrics/uploader/system_profile_cache.cc
+++ b/metrics/uploader/system_profile_cache.cc
@@ -12,6 +12,7 @@
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "base/sys_info.h"
 #include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
 #include "metrics/persistent_integer.h"
@@ -26,6 +27,21 @@
 
 }  // 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),
@@ -48,12 +64,17 @@
   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_DESCRIPTION",
-                                         &profile_.app_version) ||
+      !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) ||
+      !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE",
+                                         &build_type) ||
+      !GetChromeOSVersion(&chromeos_version) ||
       !GetHardwareId(&profile_.hardware_class)) {
     DLOG(ERROR) << "failing to initialize profile cache";
     return false;
@@ -63,6 +84,9 @@
   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)) {
@@ -126,6 +150,39 @@
   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 = 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);