Add a metrics notice and launch metrics

This prints a notice if there is no user
configuration in place, or if the user has
previously accepted metrics capture. This
can be selected via launcher flags. The
metrics binary is started, if yes.

The metrics binary is a skeleton for now.

Bug: 149776902
Test: local build and run
Change-Id: I4b5d6ffbae38c2b9e472ddde42e6d42d25777174
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 6d0a15c..e5d5dc9 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -170,6 +170,8 @@
                           "will be reset to the state it was initially launched "
                           "in. This flag is ignored if the underlying partition "
                           "images have been updated since the first launch.");
+DEFINE_string(report_anonymous_usage_stats, "", "Report anonymous usage "
+            "statistics for metrics collection and analysis.");
 
 namespace {
 
@@ -363,6 +365,10 @@
   tmp_config_obj.set_use_bootloader(FLAGS_use_bootloader);
   tmp_config_obj.set_bootloader(FLAGS_bootloader);
 
+  tmp_config_obj.set_enable_metrics(FLAGS_report_anonymous_usage_stats);
+  tmp_config_obj.set_metrics_binary(
+      vsoc::DefaultHostArtifactsPath("bin/metrics"));
+
   if (!FLAGS_boot_slot.empty()) {
       tmp_config_obj.set_boot_slot(FLAGS_boot_slot);
   }
diff --git a/host/commands/launch/launch_cvd.cc b/host/commands/launch/launch_cvd.cc
index 6cc7c93..b13c424 100644
--- a/host/commands/launch/launch_cvd.cc
+++ b/host/commands/launch/launch_cvd.cc
@@ -13,7 +13,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <iostream>
 #include <sstream>
+#include <fstream>
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
@@ -40,6 +42,8 @@
 DEFINE_bool(run_file_discovery, true,
             "Whether to run file discovery or get input files from stdin.");
 DEFINE_int32(num_instances, 1, "Number of Android guests to launch");
+DEFINE_string(report_anonymous_usage_stats, "", "Report anonymous usage "
+            "statistics for metrics collection and analysis.");
 
 namespace {
 
@@ -83,6 +87,31 @@
   }
 }
 
+std::string ValidateMetricsConfirmation(std::string use_metrics) {
+  if (use_metrics == "") {
+    if (vsoc::CuttlefishConfig::ConfigExists()) {
+      auto config = vsoc::CuttlefishConfig::Get();
+      if (config) {
+        if (config->enable_metrics() == vsoc::CuttlefishConfig::kYes) {
+          use_metrics = "y";
+        } else if (config->enable_metrics() == vsoc::CuttlefishConfig::kNo) {
+          use_metrics = "n";
+        }
+      }
+    }
+  }
+  if (use_metrics == "y") {
+    std::cout << "===================================================================\n";
+    std::cout << "NOTICE:\n\n";
+    std::cout << "We collect usage statistics in accordance with our\n"
+                 "Content Licenses (https://source.android.com/setup/start/licenses),\n"
+                 "Contributor License Agreement (https://cla.developers.google.com/),\n"
+                 "Privacy Policy (https://policies.google.com/privacy) and\n"
+                 "Terms of Service (https://policies.google.com/terms).\n";
+    std::cout << "===================================================================\n\n";
+  }
+  return use_metrics;
+}
 } // namespace
 
 int main(int argc, char** argv) {
@@ -96,6 +125,9 @@
 
   gflags::HandleCommandLineHelpFlags();
 
+  auto use_metrics = FLAGS_report_anonymous_usage_stats;
+  FLAGS_report_anonymous_usage_stats = ValidateMetricsConfirmation(use_metrics);
+
   cvd::SharedFD assembler_stdout, assembler_stdout_capture;
   cvd::SharedFD::Pipe(&assembler_stdout_capture, &assembler_stdout);
 
diff --git a/host/commands/metrics/Android.bp b/host/commands/metrics/Android.bp
new file mode 100644
index 0000000..1b35d5d
--- /dev/null
+++ b/host/commands/metrics/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2020 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.
+
+cc_binary_host {
+    name: "metrics",
+    srcs: [
+        "metrics.cc",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libcuttlefish_fs",
+        "libcuttlefish_utils",
+        "libbase",
+    ],
+    static_libs: [
+        "libcuttlefish_host_config",
+        "libgflags",
+        "libjsoncpp",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/metrics/metrics.cc b/host/commands/metrics/metrics.cc
new file mode 100644
index 0000000..4e26585
--- /dev/null
+++ b/host/commands/metrics/metrics.cc
@@ -0,0 +1,46 @@
+//
+// Copyright (C) 2020 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 <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/tee.h"
+#include "host/commands/metrics/metrics_defs.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+using cvd::MetricsExitCodes;
+
+int main(int argc, char** argv) {
+  ::android::base::InitLogging(argv, android::base::StderrLogger);
+  google::ParseCommandLineFlags(&argc, &argv, false);
+
+  cvd::TeeStderrToFile stderr_tee;
+  auto config = vsoc::CuttlefishConfig::Get();
+  auto instance = config->ForDefaultInstance();
+  auto metrics_log_path = instance.PerInstancePath("metrics.log");
+  stderr_tee.SetFile(cvd::SharedFD::Creat(metrics_log_path.c_str(), 0755));
+
+  if (config->enable_metrics() != vsoc::CuttlefishConfig::kYes) {
+    LOG(ERROR) << "metrics not enabled, but metrics were launched.";
+    return cvd::MetricsExitCodes::kInvalidHostConfiguration;
+  }
+
+  while (true) {
+    // do nothing
+    sleep(std::numeric_limits<unsigned int>::max());
+  }
+  return cvd::MetricsExitCodes::kMetricsError;
+}
diff --git a/host/commands/metrics/metrics_defs.h b/host/commands/metrics/metrics_defs.h
new file mode 100644
index 0000000..189d13b
--- /dev/null
+++ b/host/commands/metrics/metrics_defs.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+#pragma once
+
+namespace cvd {
+
+enum MetricsExitCodes : int {
+  kSuccess=0,
+  kMetricsError=1,
+  kInvalidHostConfiguration=2,
+};
+
+}  // namespace cvd
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index 5eb082b..ccd1b78 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -361,3 +361,11 @@
                                    GetOnSubprocessExitCallback(config));
   return TpmPorts{port};
 }
+
+void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
+                                  const vsoc::CuttlefishConfig& config) {
+  cvd::Command metrics(config.metrics_binary());
+
+  process_monitor->StartSubprocess(std::move(metrics),
+                                   GetOnSubprocessExitCallback(config));
+}
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index dc7a95b..53c97cd 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -57,3 +57,6 @@
 
 TpmPorts LaunchTpm(cvd::ProcessMonitor* process_monitor,
                    const vsoc::CuttlefishConfig& config);
+
+void LaunchMetrics(cvd::ProcessMonitor* process_monitor,
+                                  const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index 24d2bb3..cb23c23 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -407,6 +407,10 @@
   // Monitor and restart host processes supporting the CVD
   cvd::ProcessMonitor process_monitor;
 
+  if (config->enable_metrics() == vsoc::CuttlefishConfig::kYes) {
+    LaunchMetrics(&process_monitor, *config);
+  }
+
   auto event_pipes =
       LaunchKernelLogMonitor(*config, &process_monitor, 2);
   cvd::SharedFD boot_events_pipe = event_pipes[0];
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 5b10a18..4e4f82e 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -152,6 +152,9 @@
 
 const char* kBootSlot = "boot_slot";
 
+const char* kEnableMetrics = "enable_metrics";
+const char* kMetricsBinary = "metrics_binary";
+
 const char* kLoopMaxPart = "loop_max_part";
 const char* kGuestEnforceSecurity = "guest_enforce_security";
 const char* kGuestAuditSecurity = "guest_audit_security";
@@ -807,6 +810,34 @@
   return (*dictionary_)[kGuestForceNormalBoot].asBool();
 }
 
+void CuttlefishConfig::set_enable_metrics(std::string enable_metrics) {
+  (*dictionary_)[kEnableMetrics] = kUnknown;
+  if (!enable_metrics.empty()) {
+    switch (enable_metrics.at(0)) {
+      case 'y':
+      case 'Y':
+        (*dictionary_)[kEnableMetrics] = kYes;
+        break;
+      case 'n':
+      case 'N':
+        (*dictionary_)[kEnableMetrics] = kNo;
+        break;
+    }
+  }
+}
+
+CuttlefishConfig::Answer CuttlefishConfig::enable_metrics() const {
+  return (CuttlefishConfig::Answer)(*dictionary_)[kEnableMetrics].asInt();
+}
+
+void CuttlefishConfig::set_metrics_binary(const std::string& metrics_binary) {
+  (*dictionary_)[kMetricsBinary] = metrics_binary;
+}
+
+std::string CuttlefishConfig::metrics_binary() const {
+  return (*dictionary_)[kMetricsBinary].asString();
+}
+
 void CuttlefishConfig::set_boot_image_kernel_cmdline(std::string boot_image_kernel_cmdline) {
   Json::Value args_json_obj(Json::arrayValue);
   for (const auto& arg : android::base::Split(boot_image_kernel_cmdline, " ")) {
@@ -859,6 +890,13 @@
   return config.get();
 }
 
+/*static*/ bool CuttlefishConfig::ConfigExists() {
+  auto config_file_path = cvd::StringFromEnv(kCuttlefishConfigEnvVarName,
+                                             vsoc::GetGlobalConfigFileLink());
+  auto real_file_path = cvd::AbsolutePath(config_file_path.c_str());
+  return cvd::FileExists(real_file_path);
+}
+
 CuttlefishConfig::CuttlefishConfig() : dictionary_(new Json::Value()) {}
 // Can't use '= default' on the header because the compiler complains of
 // Json::Value being an incomplete type
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index fc7d6d9..526bfa6 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -50,6 +50,7 @@
 class CuttlefishConfig {
  public:
   static const CuttlefishConfig* Get();
+  static bool ConfigExists();
 
   CuttlefishConfig();
   CuttlefishConfig(CuttlefishConfig&&);
@@ -249,6 +250,18 @@
   void set_guest_force_normal_boot(bool guest_force_normal_boot);
   bool guest_force_normal_boot() const;
 
+  enum Answer {
+    kUnknown = 0,
+    kYes,
+    kNo,
+  };
+
+  void set_enable_metrics(std::string enable_metrics);
+  CuttlefishConfig::Answer enable_metrics() const;
+
+  void set_metrics_binary(const std::string& metrics_binary);
+  std::string metrics_binary() const;
+
   void set_boot_image_kernel_cmdline(std::string boot_image_kernel_cmdline);
   std::vector<std::string> boot_image_kernel_cmdline() const;
 
diff --git a/host_package.mk b/host_package.mk
index 9b59868..2d441dc 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -49,6 +49,7 @@
     run_cvd \
     cvd_status \
     webRTC \
+    metrics \
     fsck.f2fs \
     resize.f2fs \
     make_f2fs \