Add vendor logging code within the CHRE daemon

Bug: 207156504
Test: Verify by device logs
Change-Id: I2cc5f24570f2abf3f656560855bdb2b3b26a5e04
diff --git a/Android.mk b/Android.mk
index 78df3fb..86db98c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,6 +39,7 @@
 
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_CFLAGS += -Wall -Werror -Wextra
+LOCAL_CFLAGS += -DCHRE_DAEMON_METRIC_ENABLED
 
 LOCAL_TIDY_CHECKS := -google-runtime-int
 # bug:205155753, external/pigweed/pw_tokenizer/decode.cc:161 has this warning as error
@@ -87,7 +88,9 @@
     android.hardware.soundtrigger@2.0 \
     libpower \
     libprotobuf-cpp-lite \
-    pixelatoms-cpp
+    pixelatoms-cpp \
+    android.frameworks.stats-V1-ndk \
+    libbinder_ndk
 
 LOCAL_CPPFLAGS += -std=c++20
 LOCAL_CFLAGS += -Wno-sign-compare
diff --git a/host/common/daemon_base.cc b/host/common/daemon_base.cc
index 5925537..402fb9c 100644
--- a/host/common/daemon_base.cc
+++ b/host/common/daemon_base.cc
@@ -21,9 +21,19 @@
 #include "chre_host/log.h"
 #include "chre_host/napp_header.h"
 
-#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
 #include <json/json.h>
 
+#ifdef CHRE_DAEMON_METRIC_ENABLED
+#include <aidl/android/frameworks/stats/IStats.h>
+#include <android/binder_manager.h>
+#include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
+
+using ::aidl::android::frameworks::stats::IStats;
+using ::aidl::android::frameworks::stats::VendorAtom;
+using ::aidl::android::frameworks::stats::VendorAtomValue;
+namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
+#endif  // CHRE_DAEMON_METRIC_ENABLED
+
 // Aliased for consistency with the way these symbols are referenced in
 // CHRE-side code
 namespace fbs = ::chre::fbs;
@@ -114,7 +124,11 @@
   if (!success) {
     LOGE("Failed to send nanoapp filename.");
   } else {
-    mPreloadedNanoappPendingTransactionIds.push(transactionId);
+    Transaction transaction = {
+        .transactionId = transactionId,
+        .nanoappId = appId,
+    };
+    mPreloadedNanoappPendingTransactions.push(transaction);
   }
 
   return success;
@@ -202,10 +216,12 @@
   } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
     configureLpma(false /* enabled */);
   } else if (messageType == fbs::ChreMessage::MetricLog) {
+#ifdef CHRE_DAEMON_METRIC_ENABLED
     std::unique_ptr<fbs::MessageContainerT> container =
         fbs::UnPackMessageContainer(messageBuffer);
     const auto *metricMsg = container->message.AsMetricLog();
     handleMetricLog(metricMsg);
+#endif  // CHRE_DAEMON_METRIC_ENABLED
   } else if (hostClientId == kHostClientIdDaemon) {
     handleDaemonMessage(messageBuffer);
   } else if (hostClientId == ::chre::kHostClientIdUnspecified) {
@@ -245,27 +261,43 @@
     LOGE("Invalid message from CHRE directed to daemon");
   } else {
     const auto *response = container->message.AsLoadNanoappResponse();
-    if (mPreloadedNanoappPendingTransactionIds.empty()) {
+    if (mPreloadedNanoappPendingTransactions.empty()) {
       LOGE("Received nanoapp load response with no pending load");
-    } else if (mPreloadedNanoappPendingTransactionIds.front() !=
+    } else if (mPreloadedNanoappPendingTransactions.front().transactionId !=
                response->transaction_id) {
       LOGE("Received nanoapp load response with ID %" PRIu32
            " expected transaction id %" PRIu32,
            response->transaction_id,
-           mPreloadedNanoappPendingTransactionIds.front());
+           mPreloadedNanoappPendingTransactions.front().transactionId);
     } else {
       if (!response->success) {
         LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
-             mPreloadedNanoappPendingTransactionIds.front());
+             mPreloadedNanoappPendingTransactions.front().transactionId);
+
+#ifdef CHRE_DAEMON_METRIC_ENABLED
+        std::vector<VendorAtomValue> values(3);
+        values[0].set<VendorAtomValue::longValue>(
+            mPreloadedNanoappPendingTransactions.front().nanoappId);
+        values[1].set<VendorAtomValue::intValue>(
+            PixelAtoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED);
+        values[2].set<VendorAtomValue::intValue>(
+            PixelAtoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
+        const VendorAtom atom{
+            .reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+            .atomId = PixelAtoms::Atom::kChreHalNanoappLoadFailed,
+            .values{std::move(values)},
+        };
+        reportMetric(atom);
+#endif  // CHRE_DAEMON_METRIC_ENABLED
       }
-      mPreloadedNanoappPendingTransactionIds.pop();
+      mPreloadedNanoappPendingTransactions.pop();
     }
   }
 }
 
+#ifdef CHRE_DAEMON_METRIC_ENABLED
 void ChreDaemonBase::handleMetricLog(const ::chre::fbs::MetricLogT *metricMsg) {
   const std::vector<int8_t> &encodedMetric = metricMsg->encoded_metric;
-  namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
 
   switch (metricMsg->id) {
     case PixelAtoms::Atom::kChrePalOpenFailed: {
@@ -273,7 +305,15 @@
       if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) {
         LOGE("Failed to parse metric data");
       } else {
-        // TODO(b/207156504): Log the metric
+        std::vector<VendorAtomValue> values(2);
+        values[0].set<VendorAtomValue::intValue>(metric.pal());
+        values[1].set<VendorAtomValue::intValue>(metric.type());
+        const VendorAtom atom{
+            .reverseDomainName = PixelAtoms::ReverseDomainNames().pixel(),
+            .atomId = PixelAtoms::Atom::kChrePalOpenFailed,
+            .values{std::move(values)},
+        };
+        reportMetric(atom);
       }
       break;
     }
@@ -282,6 +322,26 @@
     }
   }
 }
+#endif  // CHRE_DAEMON_METRIC_ENABLED
+
+#ifdef CHRE_DAEMON_METRIC_ENABLED
+void ChreDaemonBase::reportMetric(const VendorAtom &atom) {
+  const std::string statsServiceName =
+      std::string(IStats::descriptor).append("/default");
+  if (!AServiceManager_isDeclared(statsServiceName.c_str())) {
+    LOGE("Stats service is not declared.");
+    return;
+  }
+
+  std::shared_ptr<IStats> stats_client = IStats::fromBinder(ndk::SpAIBinder(
+      AServiceManager_waitForService(statsServiceName.c_str())));
+
+  const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom);
+  if (!ret.isOk()) {
+    LOGE("Failed to report vendor atom");
+  }
+}
+#endif  // CHRE_DAEMON_METRIC_ENABLED
 
 }  // namespace chre
 }  // namespace android
diff --git a/host/common/include/chre_host/daemon_base.h b/host/common/include/chre_host/daemon_base.h
index 6b5c6f2..5154f2e 100644
--- a/host/common/include/chre_host/daemon_base.h
+++ b/host/common/include/chre_host/daemon_base.h
@@ -27,6 +27,11 @@
 #include "chre_host/log_message_parser.h"
 #include "chre_host/socket_server.h"
 
+#ifdef CHRE_DAEMON_METRIC_ENABLED
+#include <aidl/android/frameworks/stats/IStats.h>
+#include <android/binder_manager.h>
+#endif
+
 namespace android {
 namespace chre {
 
@@ -86,6 +91,12 @@
   //! with any clients after the server starts.
   static constexpr uint16_t kHostClientIdDaemon = UINT16_MAX;
 
+  //! Contains the transaction ID and app ID used to preload nanoapps.
+  struct Transaction {
+    uint32_t transactionId;
+    uint64_t nanoappId;
+  };
+
   void setShutdownRequested(bool request) {
     mChreShutdownRequested = request;
   }
@@ -194,10 +205,22 @@
    */
   virtual void configureLpma(bool enabled) = 0;
 
+#ifdef CHRE_DAEMON_METRIC_ENABLED
   /**
    * Handles a metric log message sent from CHRE
    */
   virtual void handleMetricLog(const ::chre::fbs::MetricLogT *metric_msg);
+#endif  // CHRE_DAEMON_METRIC_ENABLED
+
+#ifdef CHRE_DAEMON_METRIC_ENABLED
+  /**
+   * Create and report CHRE vendor atom and send it to stats_client
+   *
+   * @param atom the vendor atom to be reported
+   */
+  virtual void reportMetric(
+      const aidl::android::frameworks::stats::VendorAtom &atom);
+#endif  // CHRE_DAEMON_METRIC_ENABLED
 
   /**
    * Returns the CHRE log message parser instance.
@@ -216,9 +239,9 @@
   //! Set to true when we request a graceful shutdown of CHRE
   std::atomic<bool> mChreShutdownRequested;
 
-  //! Contains a set of transaction IDs used to load the preloaded nanoapps.
-  //! The IDs are stored in the order they are sent.
-  std::queue<uint32_t> mPreloadedNanoappPendingTransactionIds;
+  //! Contains a set of transaction IDs and app IDs used to load the preloaded
+  //! nanoapps. The IDs are stored in the order they are sent.
+  std::queue<Transaction> mPreloadedNanoappPendingTransactions;
 
   /**
    * Computes and returns the clock drift between the system clock
diff --git a/host/common/include/chre_host/log.h b/host/common/include/chre_host/log.h
index eb85334..a247e87 100644
--- a/host/common/include/chre_host/log.h
+++ b/host/common/include/chre_host/log.h
@@ -25,7 +25,8 @@
 
 /**
  * Logs a message to both logcat and stdout. Don't use this directly; prefer one
- * of LOGE, LOGW, etc. to populate the level.
+ * of LOGE, LOGW, etc. to populate the level. Use LOG_PRI directly rather than
+ * ALOG to avoid misinterpreting LOG_* macros that may be incorrectly evaluated.
  *
  * @param level log level to pass to ALOG (LOG_ERROR, LOG_WARN, etc.)
  * @param stream output stream to print to (e.g. stdout)
@@ -33,7 +34,7 @@
  */
 #define CHRE_LOG(level, stream, format, ...)                                   \
   do {                                                                         \
-    ALOG(level, LOG_TAG, format, ##__VA_ARGS__);                               \
+    LOG_PRI(ANDROID_##level, LOG_TAG, format, ##__VA_ARGS__);                  \
     fprintf(stream, "%s:%d: " format "\n", __func__, __LINE__, ##__VA_ARGS__); \
   } while (0)