Fix compiler filter / reason reporting and add the ISA to the metrics

The compiler filter / reason reporting was not accurate for a variety
of reasons. (e.g. reporting was only done at startup, it was relying
on imprecise APIs and had errors in the logic).

In order to keep track of the precise optimization status, this CL
introduces the concept of AppInfo, which encapsulates the data about
the application / system server code paths, their optimization status
and possible other metadata (e.g. profiles).

To populate it, we rely on 2 distinct events:
1) The framework calling VMRuntime#registerAppInfo to inform the
runtime about the applications code paths and their types (e.g. primary,
split, secondary).
2) Class loading, when we can determine the actual optimization status
like filters, reasons, and whether or not we can load the odex files.

These events may happen in any order so we could deal with a partial
state at some point in time, but in the majority of cases they always
happen at Class Loading, followed by RegisterAppInfo.

This CL also deletes the OatFileManager#getPrimaryOatFile which was
a misleading API as it didn't work in most cases. It also adds more
tests to the metrics/reporting infra for previous missing or
unimplemented cases.

Test: gtest
Bug: 170149255
Change-Id: If0a7a25d06ff6fb89fe4861139b7dee61c05814d
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index c9110ee..2a71c2c 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -29,7 +29,6 @@
 
 #include "android-base/logging.h"
 #include "base/bit_utils.h"
-#include "base/compiler_filter.h"
 #include "base/time_utils.h"
 
 #pragma clang diagnostic push
@@ -82,127 +81,96 @@
 #undef METRIC
 };
 
+// Names come from PackageManagerServiceCompilerMapping.java
+#define REASON_NAME_LIST(V) \
+  V(kError, "error") \
+  V(kUnknown, "unknown") \
+  V(kFirstBoot, "first-boot") \
+  V(kBootAfterOTA, "boot-after-ota") \
+  V(kPostBoot, "post-boot") \
+  V(kInstall, "install") \
+  V(kInstallFast, "install-fast") \
+  V(kInstallBulk, "install-bulk") \
+  V(kInstallBulkSecondary, "install-bulk-secondary") \
+  V(kInstallBulkDowngraded, "install-bulk-downgraded") \
+  V(kInstallBulkSecondaryDowngraded, "install-bulk-secondary-downgraded") \
+  V(kBgDexopt, "bg-dexopt") \
+  V(kABOTA, "ab-ota") \
+  V(kInactive, "inactive") \
+  V(kShared, "shared") \
+  V(kInstallWithDexMetadata, "install-with-dex-metadata") \
+  V(kPrebuilt, "prebuilt") \
+  V(kCmdLine, "cmdline")
+
 // We log compilation reasons as part of the metadata we report. Since elsewhere compilation reasons
 // are specified as a string, we define them as an enum here which indicates the reasons that we
 // support.
 enum class CompilationReason {
-  kError,
-  kUnknown,
-  kFirstBoot,
-  kBootAfterOTA,
-  kPostBoot,
-  kInstall,
-  kInstallFast,
-  kInstallBulk,
-  kInstallBulkSecondary,
-  kInstallBulkDowngraded,
-  kInstallBulkSecondaryDowngraded,
-  kBgDexopt,
-  kABOTA,
-  kInactive,
-  kShared,
-  kInstallWithDexMetadata,
-  kPrebuilt,
-  kCmdLine
+#define REASON(kind, name) kind,
+  REASON_NAME_LIST(REASON)
+#undef REASON
 };
 
+#define REASON_NAME(kind, kind_name) \
+    case CompilationReason::kind: return kind_name;
+#define REASON_FROM_NAME(kind, kind_name) \
+    if (name == kind_name) { return CompilationReason::kind; }
+
 constexpr const char* CompilationReasonName(CompilationReason reason) {
   switch (reason) {
-    case CompilationReason::kError:
-      return "error";
-    case CompilationReason::kUnknown:
-      return "unknown";
-    case CompilationReason::kFirstBoot:
-      return "first-boot";
-    case CompilationReason::kBootAfterOTA:
-      return "boot-after-ota";
-    case CompilationReason::kPostBoot:
-      return "post-boot";
-    case CompilationReason::kInstall:
-      return "install";
-    case CompilationReason::kInstallFast:
-      return "install-fast";
-    case CompilationReason::kInstallBulk:
-      return "install-bulk";
-    case CompilationReason::kInstallBulkSecondary:
-      return "install-bulk-secondary";
-    case CompilationReason::kInstallBulkDowngraded:
-      return "install-bulk-downgraded";
-    case CompilationReason::kInstallBulkSecondaryDowngraded:
-      return "install-bulk-secondary-downgraded";
-    case CompilationReason::kBgDexopt:
-      return "bg-dexopt";
-    case CompilationReason::kABOTA:
-      return "ab-ota";
-    case CompilationReason::kInactive:
-      return "inactive";
-    case CompilationReason::kShared:
-      return "shared";
-    case CompilationReason::kInstallWithDexMetadata:
-      return "install-with-dex-metadata";
-    case CompilationReason::kPrebuilt:
-      return "prebuilt";
-    case CompilationReason::kCmdLine:
-      return "cmdline";
+    REASON_NAME_LIST(REASON_NAME)
   }
 }
 
 constexpr CompilationReason CompilationReasonFromName(std::string_view name) {
-  // Names come from PackageManagerServiceCompilerMapping.java
-  if (name == "unknown") {
-    return CompilationReason::kUnknown;
-  }
-  if (name == "first-boot") {
-    return CompilationReason::kFirstBoot;
-  }
-  if (name == "boot-after-ota") {
-    return CompilationReason::kBootAfterOTA;
-  }
-  if (name == "post-boot") {
-    return CompilationReason::kPostBoot;
-  }
-  if (name == "install") {
-    return CompilationReason::kInstall;
-  }
-  if (name == "install-fast") {
-    return CompilationReason::kInstallFast;
-  }
-  if (name == "install-bulk") {
-    return CompilationReason::kInstallBulk;
-  }
-  if (name == "install-bulk-secondary") {
-    return CompilationReason::kInstallBulkSecondary;
-  }
-  if (name == "install-bulk-downgraded") {
-    return CompilationReason::kInstallBulkDowngraded;
-  }
-  if (name == "install-bulk-secondary-downgraded") {
-    return CompilationReason::kInstallBulkSecondaryDowngraded;
-  }
-  if (name == "bg-dexopt") {
-    return CompilationReason::kBgDexopt;
-  }
-  if (name == "ab-ota") {
-    return CompilationReason::kABOTA;
-  }
-  if (name == "inactive") {
-    return CompilationReason::kInactive;
-  }
-  if (name == "shared") {
-    return CompilationReason::kShared;
-  }
-  if (name == "install-with-dex-metadata") {
-    return CompilationReason::kInstallWithDexMetadata;
-  }
-  if (name == "prebuilt") {
-    return CompilationReason::kPrebuilt;
-  }
-  if (name == "cmdline") {
-    return CompilationReason::kCmdLine;
-  }
+  REASON_NAME_LIST(REASON_FROM_NAME)
   return CompilationReason::kError;
 }
 
+#undef REASON_NAME
+#undef ReasonFromName
+
+#define COMPILER_FILTER_REPORTING_LIST(V) \
+  V(kError, "error") /* Error (invalid value) condition */ \
+  V(kUnknown, "unknown") /* Unknown (not set) condition */ \
+  V(kAssumeVerified, "assume-verified") /* Standard compiler filters */ \
+  V(kExtract, "extract") \
+  V(kVerify, "verify") \
+  V(kSpaceProfile, "space-profile") \
+  V(kSpace, "space") \
+  V(kSpeedProfile, "speed-profile") \
+  V(kSpeed, "speed") \
+  V(kEverythingProfile, "everything-profile") \
+  V(kEverything, "everything") \
+  V(kRunFromApk, "run-from-apk") /* Augmented compiler filters as produces by OatFileAssistant#GetOptimizationStatus */ \
+  V(kRunFromApkFallback, "run-from-apk-fallback")
+
+// Augmented compiler filter enum, used in the reporting infra.
+enum class CompilerFilterReporting {
+#define FILTER(kind, name) kind,
+  COMPILER_FILTER_REPORTING_LIST(FILTER)
+#undef FILTER
+};
+
+#define FILTER_NAME(kind, kind_name) \
+    case CompilerFilterReporting::kind: return kind_name;
+#define FILTER_FROM_NAME(kind, kind_name) \
+    if (name == kind_name) { return CompilerFilterReporting::kind; }
+
+constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) {
+  switch (filter) {
+    COMPILER_FILTER_REPORTING_LIST(FILTER_NAME)
+  }
+}
+
+constexpr CompilerFilterReporting CompilerFilterReportingFromName(std::string_view name) {
+  COMPILER_FILTER_REPORTING_LIST(FILTER_FROM_NAME)
+  return CompilerFilterReporting::kError;
+}
+
+#undef FILTER_NAME
+#undef FILTER_FROM_NAME
+
 // SessionData contains metadata about a metrics session (basically the lifetime of an ART process).
 // This information should not change for the lifetime of the session.
 struct SessionData {
@@ -214,7 +182,7 @@
   int64_t session_id;
   int32_t uid;
   CompilationReason compilation_reason;
-  std::optional<CompilerFilter::Filter> compiler_filter;
+  CompilerFilterReporting compiler_filter;
 };
 
 // MetricsBackends are used by a metrics reporter to write metrics to some external location. For
@@ -229,7 +197,10 @@
   // includes a session id which is used to correlate any metric reports with the same instance of
   // the ART runtime. Additionally, session_data includes useful metadata such as the package name
   // for this process.
-  virtual void BeginSession(const SessionData& session_data) = 0;
+  //
+  // It may also be called whenever there is an update to the session metadata (e.g. optimization
+  // state).
+  virtual void BeginOrUpdateSession(const SessionData& session_data) = 0;
 
  protected:
   // Called by the metrics reporter to indicate that a new metrics report is starting.
@@ -468,7 +439,7 @@
  public:
   StringBackend();
 
-  void BeginSession(const SessionData& session_data) override;
+  void BeginOrUpdateSession(const SessionData& session_data) override;
 
   void BeginReport(uint64_t timestamp_millis) override;
 
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index c8c8701..f09987b 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -51,7 +51,7 @@
 
   return SessionData{
     .compilation_reason = CompilationReason::kUnknown,
-    .compiler_filter = std::nullopt,
+    .compiler_filter = CompilerFilterReporting::kUnknown,
     .session_id = kInvalidSessionId,
     .uid = uid,
   };
@@ -97,7 +97,7 @@
   return result;
 }
 
-void StringBackend::BeginSession(const SessionData& session_data) {
+void StringBackend::BeginOrUpdateSession(const SessionData& session_data) {
   session_data_ = session_data;
 }
 
@@ -110,10 +110,7 @@
     os_ << "    uid: " << session_data_->uid << "\n";
     os_ << "    compilation_reason: " << CompilationReasonName(session_data_->compilation_reason)
         << "\n";
-    os_ << "    compiler_filter: "
-        << (session_data_->compiler_filter.has_value()
-                ? CompilerFilter::NameOfFilter(session_data_->compiler_filter.value())
-                : "(unspecified)")
+    os_ << "    compiler_filter: " << CompilerFilterReportingName(session_data_->compiler_filter)
         << "\n";
   }
   os_ << "  Metrics:\n";
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index 77f1b97..e7882ec 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -305,6 +305,141 @@
   metrics.ReportAllMetrics(&zero_backend);
 }
 
+TEST(CompilerFilterReportingTest, FromName) {
+  ASSERT_EQ(CompilerFilterReportingFromName("error"),
+            CompilerFilterReporting::kError);
+  ASSERT_EQ(CompilerFilterReportingFromName("unknown"),
+            CompilerFilterReporting::kUnknown);
+  ASSERT_EQ(CompilerFilterReportingFromName("assume-verified"),
+            CompilerFilterReporting::kAssumeVerified);
+  ASSERT_EQ(CompilerFilterReportingFromName("extract"),
+            CompilerFilterReporting::kExtract);
+  ASSERT_EQ(CompilerFilterReportingFromName("verify"),
+            CompilerFilterReporting::kVerify);
+  ASSERT_EQ(CompilerFilterReportingFromName("space-profile"),
+            CompilerFilterReporting::kSpaceProfile);
+  ASSERT_EQ(CompilerFilterReportingFromName("space"),
+            CompilerFilterReporting::kSpace);
+  ASSERT_EQ(CompilerFilterReportingFromName("speed-profile"),
+            CompilerFilterReporting::kSpeedProfile);
+  ASSERT_EQ(CompilerFilterReportingFromName("speed"),
+            CompilerFilterReporting::kSpeed);
+  ASSERT_EQ(CompilerFilterReportingFromName("everything-profile"),
+            CompilerFilterReporting::kEverythingProfile);
+  ASSERT_EQ(CompilerFilterReportingFromName("everything"),
+            CompilerFilterReporting::kEverything);
+  ASSERT_EQ(CompilerFilterReportingFromName("run-from-apk"),
+            CompilerFilterReporting::kRunFromApk);
+  ASSERT_EQ(CompilerFilterReportingFromName("run-from-apk-fallback"),
+            CompilerFilterReporting::kRunFromApkFallback);
+}
+
+TEST(CompilerFilterReportingTest, Name) {
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kError),
+            "error");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kUnknown),
+            "unknown");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kAssumeVerified),
+            "assume-verified");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kExtract),
+            "extract");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kVerify),
+            "verify");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpaceProfile),
+            "space-profile");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpace),
+            "space");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpeedProfile),
+            "speed-profile");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kSpeed),
+            "speed");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kEverythingProfile),
+            "everything-profile");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kEverything),
+            "everything");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kRunFromApk),
+            "run-from-apk");
+  ASSERT_EQ(CompilerFilterReportingName(CompilerFilterReporting::kRunFromApkFallback),
+            "run-from-apk-fallback");
+}
+
+TEST(CompilerReason, FromName) {
+  ASSERT_EQ(CompilationReasonFromName("unknown"),
+            CompilationReason::kUnknown);
+  ASSERT_EQ(CompilationReasonFromName("first-boot"),
+            CompilationReason::kFirstBoot);
+  ASSERT_EQ(CompilationReasonFromName("boot-after-ota"),
+            CompilationReason::kBootAfterOTA);
+  ASSERT_EQ(CompilationReasonFromName("post-boot"),
+            CompilationReason::kPostBoot);
+  ASSERT_EQ(CompilationReasonFromName("install"),
+            CompilationReason::kInstall);
+  ASSERT_EQ(CompilationReasonFromName("install-fast"),
+            CompilationReason::kInstallFast);
+  ASSERT_EQ(CompilationReasonFromName("install-bulk"),
+            CompilationReason::kInstallBulk);
+  ASSERT_EQ(CompilationReasonFromName("install-bulk-secondary"),
+            CompilationReason::kInstallBulkSecondary);
+  ASSERT_EQ(CompilationReasonFromName("install-bulk-downgraded"),
+            CompilationReason::kInstallBulkDowngraded);
+  ASSERT_EQ(CompilationReasonFromName("install-bulk-secondary-downgraded"),
+            CompilationReason::kInstallBulkSecondaryDowngraded);
+  ASSERT_EQ(CompilationReasonFromName("bg-dexopt"),
+            CompilationReason::kBgDexopt);
+  ASSERT_EQ(CompilationReasonFromName("ab-ota"),
+            CompilationReason::kABOTA);
+  ASSERT_EQ(CompilationReasonFromName("inactive"),
+            CompilationReason::kInactive);
+  ASSERT_EQ(CompilationReasonFromName("shared"),
+            CompilationReason::kShared);
+  ASSERT_EQ(CompilationReasonFromName("install-with-dex-metadata"),
+            CompilationReason::kInstallWithDexMetadata);
+  ASSERT_EQ(CompilationReasonFromName("prebuilt"),
+            CompilationReason::kPrebuilt);
+  ASSERT_EQ(CompilationReasonFromName("cmdline"),
+            CompilationReason::kCmdLine);
+  ASSERT_EQ(CompilationReasonFromName("error"),
+            CompilationReason::kError);
+}
+
+TEST(CompilerReason, Name) {
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kUnknown),
+            "unknown");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kFirstBoot),
+            "first-boot");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kBootAfterOTA),
+            "boot-after-ota");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kPostBoot),
+            "post-boot");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstall),
+            "install");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallFast),
+            "install-fast");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulk),
+            "install-bulk");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkSecondary),
+            "install-bulk-secondary");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkDowngraded),
+            "install-bulk-downgraded");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallBulkSecondaryDowngraded),
+            "install-bulk-secondary-downgraded");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kBgDexopt),
+            "bg-dexopt");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kABOTA),
+            "ab-ota");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInactive),
+            "inactive");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kShared),
+            "shared");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kInstallWithDexMetadata),
+            "install-with-dex-metadata");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kPrebuilt),
+            "prebuilt");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kCmdLine),
+            "cmdline");
+  ASSERT_EQ(CompilationReasonName(CompilationReason::kError),
+            "error");
+}
 }  // namespace metrics
 }  // namespace art
 
diff --git a/libartbase/base/metrics/metrics_test.h b/libartbase/base/metrics/metrics_test.h
index 48fd517..3e8b42a 100644
--- a/libartbase/base/metrics/metrics_test.h
+++ b/libartbase/base/metrics/metrics_test.h
@@ -33,7 +33,7 @@
 // test cases to test specific behaviors.
 class TestBackendBase : public MetricsBackend {
  public:
-  void BeginSession([[maybe_unused]] const SessionData& session_data) override {}
+  void BeginOrUpdateSession([[maybe_unused]] const SessionData& session_data) override {}
 
   void BeginReport([[maybe_unused]] uint64_t timestamp_since_start_ms) override {}
 
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 1cad5ab..fed89b8 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -71,6 +71,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "app_info.cc",
         "aot_class_linker.cc",
         "art_field.cc",
         "sdk_checker.cc",
@@ -691,6 +692,7 @@
         ":art-gtest-jars-XandY",
     ],
     srcs: [
+        "app_info_test.cc",
         "arch/arch_test.cc",
         "arch/instruction_set_features_test.cc",
         "arch/memcmp16_test.cc",
diff --git a/runtime/app_info.cc b/runtime/app_info.cc
new file mode 100644
index 0000000..c72951e
--- /dev/null
+++ b/runtime/app_info.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 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 <app_info.h>
+
+#include "base/logging.h"
+#include "base/mutex.h"
+#include "base/safe_map.h"
+#include "thread-inl.h"
+
+namespace art {
+
+static constexpr const char* kUnknownValue = "unknown";
+
+AppInfo::AppInfo()
+    : update_mutex_("app_info_update_mutex", LockLevel::kGenericBottomLock) {}
+
+// Converts VMRuntime.java constansts to a CodeType.
+AppInfo::CodeType AppInfo::FromVMRuntimeConstants(uint32_t code_type) {
+  switch (code_type) {
+    case kVMRuntimePrimaryApk : return CodeType::kPrimaryApk;
+    case kVMRuntimeSplitApk : return CodeType::kPrimaryApk;
+    case kVMRuntimeSecondaryDex : return CodeType::kSecondaryDex;
+    default:
+      LOG(WARNING) << "Unknown code type: " << code_type;
+      return CodeType::kUnknown;
+  }
+}
+
+static const char* CodeTypeName(AppInfo::CodeType code_type) {
+  switch (code_type) {
+    case AppInfo::CodeType::kPrimaryApk : return "primary-apk";
+    case AppInfo::CodeType::kSplitApk : return "split-apk";
+    case AppInfo::CodeType::kSecondaryDex : return "secondary-dex";
+    case AppInfo::CodeType::kUnknown : return "unknown";
+  }
+}
+
+void AppInfo::RegisterAppInfo(const std::string& package_name,
+                              const std::vector<std::string>& code_paths,
+                              const std::string& cur_profile_path,
+                              const std::string& ref_profile_path,
+                              AppInfo::CodeType code_type) {
+  MutexLock mu(Thread::Current(), update_mutex_);
+
+  package_name_ = package_name;
+
+  for (const std::string& code_path : code_paths) {
+    CodeLocationInfo& cli = registered_code_locations_.GetOrCreate(
+        code_path, []() { return CodeLocationInfo(); });
+    cli.cur_profile_path = cur_profile_path;
+    cli.ref_profile_path = ref_profile_path;
+    cli.code_type = code_type;
+
+    VLOG(startup) << "Registering code path. "
+        << "\npackage_name=" << package_name
+        << "\ncode_path=" << code_path
+        << "\ncode_type=" << CodeTypeName(code_type)
+        << "\ncur_profile=" << cur_profile_path
+        << "\nref_profile=" << ref_profile_path;
+  }
+}
+
+void AppInfo::RegisterOdexStatus(const std::string& code_path,
+                                 const std::string& compiler_filter,
+                                 const std::string& compilation_reason,
+                                 const std::string& odex_status) {
+  MutexLock mu(Thread::Current(), update_mutex_);
+
+  CodeLocationInfo& cli = registered_code_locations_.GetOrCreate(
+      code_path, []() { return CodeLocationInfo(); });
+  cli.compiler_filter = compiler_filter;
+  cli.compilation_reason = compilation_reason;
+  cli.odex_status = odex_status;
+
+  VLOG(startup) << "Registering odex status. "
+        << "\ncode_path=" << code_path
+        << "\ncompiler_filter=" << compiler_filter
+        << "\ncompilation_reason=" << compilation_reason
+        << "\nodex_status=" << odex_status;
+}
+
+void AppInfo::GetPrimaryApkOptimizationStatus(
+    std::string* out_compiler_filter,
+    std::string* out_compilation_reason) {
+  MutexLock mu(Thread::Current(), update_mutex_);
+
+  for (const auto& it : registered_code_locations_) {
+    const CodeLocationInfo& cli = it.second;
+    if (cli.code_type == CodeType::kPrimaryApk) {
+      *out_compiler_filter = cli.compiler_filter.value_or(kUnknownValue);
+      *out_compilation_reason = cli.compilation_reason.value_or(kUnknownValue);
+      return;
+    }
+  }
+  *out_compiler_filter = kUnknownValue;
+  *out_compilation_reason = kUnknownValue;
+}
+
+std::ostream& operator<<(std::ostream& os, AppInfo& rhs) {
+  MutexLock mu(Thread::Current(), rhs.update_mutex_);
+
+  os << "AppInfo for package_name=" << rhs.package_name_.value_or(kUnknownValue) << "\n";
+  for (const auto& it : rhs.registered_code_locations_) {
+    const std::string code_path = it.first;
+    const AppInfo::CodeLocationInfo& cli = it.second;
+
+    os << "\ncode_path=" << code_path
+        << "\ncode_type=" << CodeTypeName(cli.code_type)
+        << "\ncompiler_filter=" << cli.compiler_filter.value_or(kUnknownValue)
+        << "\ncompilation_reason=" << cli.compilation_reason.value_or(kUnknownValue)
+        << "\nodex_status=" << cli.odex_status.value_or(kUnknownValue)
+        << "\ncur_profile=" << cli.cur_profile_path.value_or(kUnknownValue)
+        << "\nref_profile=" << cli.ref_profile_path.value_or(kUnknownValue)
+        << "\n";
+  }
+  return os;
+}
+
+}  // namespace art
diff --git a/runtime/app_info.h b/runtime/app_info.h
new file mode 100644
index 0000000..68f2c58
--- /dev/null
+++ b/runtime/app_info.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ART_RUNTIME_APP_INFO_H_
+#define ART_RUNTIME_APP_INFO_H_
+
+#include <vector>
+
+#include "base/mutex.h"
+#include <base/safe_map.h>
+
+namespace art {
+
+// Constants used by VMRuntime.java to interface with the runtime.
+// We could get them from the well known class but it's simpler to
+// redefine them here.
+
+// VMRuntime.CODE_PATH_TYPE_PRIMARY_APK
+static constexpr int32_t kVMRuntimePrimaryApk = 1 << 0;
+// VMRuntime.CODE_PATH_TYPE_SPLIT_APK
+static constexpr int32_t kVMRuntimeSplitApk = 1 << 1;
+// VMRuntime.CODE_PATH_TYPE_SECONDARY_DEX
+static constexpr int32_t kVMRuntimeSecondaryDex = 1 << 2;
+
+// Encapsulates the information the runtime has about the application.
+//
+// The app's info comes from 2 channels:
+//   1) during class loading, when we load oat files.
+//   2) during app startup, when the framework calls VMRuntime#registerAppInfo.
+// In general the class loading event happens before VMRuntime#registerAppInfo.
+class AppInfo {
+ public:
+  enum class CodeType {
+    kUnknown,
+    kPrimaryApk,
+    kSplitApk,
+    kSecondaryDex,
+  };
+
+  // Converts VMRuntime.java constansts to a CodeType.
+  static CodeType FromVMRuntimeConstants(uint32_t code_type);
+
+  AppInfo();
+
+  // Registers the application code paths, types, and associated profiles.
+  void RegisterAppInfo(const std::string& package_name,
+                       const std::vector<std::string>& code_paths,
+                       const std::string& profile_output_filename,
+                       const std::string& ref_profile_filename,
+                       CodeType code_type);
+
+  // Registers the optimization status for single code path.
+  void RegisterOdexStatus(const std::string& code_path,
+                          const std::string& compiler_filter,
+                          const std::string& compilation_reason,
+                          const std::string& odex_status);
+
+  // Extracts the optimization status of the primary apk into the given arguments.
+  // If there are multiple primary APKs registed via RegisterAppInfo, the method
+  // will assign the status of the first APK, sorted by the location name.
+  //
+  // Assigns "unknown" if there is no primary apk or the optimization status was
+  // not set via RegisterOdexStatus,
+  void GetPrimaryApkOptimizationStatus(std::string* out_compiler_filter,
+                                       std::string* out_compilation_reason);
+
+ private:
+  // Encapsulates optimization information about a particular code location.
+  struct CodeLocationInfo {
+    // The type of the code location (primary, split, secondary, unknown).
+    CodeType code_type{CodeType::kUnknown};
+
+    // The compiler filter of the oat file. Note that this contains
+    // the output of OatFileAssistant#GetOptimizationStatus() which may
+    // contain values outside the scope of the CompilerFilter enum.
+    std::optional<std::string> compiler_filter;
+
+    // The compiler reason of the oat file. Note that this contains
+    // the output of OatFileAssistant#GetOptimizationStatus().
+    std::optional<std::string> compilation_reason;
+
+    // The odes status as produced by OatFileAssistant#GetOptimizationStatus().
+    std::optional<std::string> odex_status;
+
+    // The path to the primary profile if given.
+    std::optional<std::string> cur_profile_path;
+
+    // The path to the reference profile if given.
+    std::optional<std::string> ref_profile_path;
+  };
+
+  // The name of the package if set.
+  std::optional<std::string> package_name_ GUARDED_BY(update_mutex_);
+
+  // The registered code locations.
+  SafeMap<std::string, CodeLocationInfo> registered_code_locations_ GUARDED_BY(update_mutex_);
+
+  // Lock to touch the state ot the AppInfo object.
+  art::Mutex update_mutex_ BOTTOM_MUTEX_ACQUIRED_AFTER;
+
+  friend std::ostream& operator<<(std::ostream& os, AppInfo& rhs);
+};
+
+std::ostream& operator<<(std::ostream& os, AppInfo& rhs);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_APP_INFO_H_
diff --git a/runtime/app_info_test.cc b/runtime/app_info_test.cc
new file mode 100644
index 0000000..4a365de
--- /dev/null
+++ b/runtime/app_info_test.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2021 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 "app_info.h"
+
+#include <vector>
+
+#include "gtest/gtest.h"
+
+namespace art {
+
+TEST(AppInfoTest, RegisterAppInfo) {
+  AppInfo app_info;
+  app_info.RegisterAppInfo(
+      "package_name",
+      std::vector<std::string>({"code_location"}),
+      "",
+      "",
+      AppInfo::CodeType::kPrimaryApk);
+
+  std::string filter;
+  std::string reason;
+  app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
+
+  // Odex status was not registered.
+  ASSERT_EQ(filter, "unknown");
+  ASSERT_EQ(reason, "unknown");
+}
+
+TEST(AppInfoTest, RegisterAppInfoWithOdexStatus) {
+  AppInfo app_info;
+  app_info.RegisterAppInfo(
+      "package_name",
+      std::vector<std::string>({"code_location"}),
+      "",
+      "",
+      AppInfo::CodeType::kPrimaryApk);
+  app_info.RegisterOdexStatus(
+      "code_location",
+      "filter",
+      "reason",
+      "odex_status");
+
+  std::string filter;
+  std::string reason;
+  app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
+
+  ASSERT_EQ(filter, "filter");
+  ASSERT_EQ(reason, "reason");
+}
+
+TEST(AppInfoTest, RegisterAppInfoWithOdexStatusMultiplePrimary) {
+  AppInfo app_info;
+  app_info.RegisterOdexStatus(
+      "code_location",
+      "filter",
+      "reason",
+      "odex_status");
+  app_info.RegisterOdexStatus(
+      "code_location2",
+      "filter2",
+      "reason2",
+      "odex_status");
+  app_info.RegisterAppInfo(
+      "package_name",
+      std::vector<std::string>({"code_location"}),
+      "",
+      "",
+      AppInfo::CodeType::kPrimaryApk);
+
+  std::string filter;
+  std::string reason;
+  app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
+
+  // The optimization status should be the one of the first apk.
+  ASSERT_EQ(filter, "filter");
+  ASSERT_EQ(reason, "reason");
+}
+
+TEST(AppInfoTest, RegisterAppInfoWithOdexStatusNoPrimary) {
+  AppInfo app_info;
+
+  // Check that the status is not known in an empty app_info.
+  std::string filter;
+  std::string reason;
+  app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
+
+  // Register a split.s
+  app_info.RegisterAppInfo(
+      "package_name",
+      std::vector<std::string>({"code_location"}),
+      "",
+      "",
+      AppInfo::CodeType::kSplitApk);
+  app_info.RegisterOdexStatus(
+      "code_location",
+      "filter",
+      "reason",
+      "odex_status");
+
+
+  // The optimization status is unknown since we don't have primary apks.
+  app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
+  ASSERT_EQ(filter, "unknown");
+  ASSERT_EQ(reason, "unknown");
+}
+
+}  // namespace art
diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc
index 2398ea9..5104bd3 100644
--- a/runtime/metrics/reporter.cc
+++ b/runtime/metrics/reporter.cc
@@ -21,6 +21,7 @@
 #include <android-base/parseint.h>
 
 #include "base/flags.h"
+#include "oat_file_manager.h"
 #include "runtime.h"
 #include "runtime_options.h"
 #include "statsd.h"
@@ -84,6 +85,18 @@
   }
 }
 
+void MetricsReporter::NotifyAppInfoUpdated(AppInfo* app_info) {
+  std::string compilation_reason;
+  std::string compiler_filter;
+
+  app_info->GetPrimaryApkOptimizationStatus(
+      &compiler_filter, &compilation_reason);
+
+  SetCompilationInfo(
+      CompilationReasonFromName(compilation_reason),
+      CompilerFilterReportingFromName(compiler_filter));
+}
+
 void MetricsReporter::RequestMetricsReport(bool synchronous) {
   if (thread_.has_value()) {
     messages_.SendMessage(RequestMetricsReportMessage{synchronous});
@@ -94,7 +107,7 @@
 }
 
 void MetricsReporter::SetCompilationInfo(CompilationReason compilation_reason,
-                                         CompilerFilter::Filter compiler_filter) {
+                                         CompilerFilterReporting compiler_filter) {
   if (thread_.has_value()) {
     messages_.SendMessage(CompilationInfoMessage{compilation_reason, compiler_filter});
   }
@@ -159,6 +172,8 @@
           LOG_STREAM(DEBUG) << "Compilation info received " << session_data_.session_id;
           session_data_.compilation_reason = message.compilation_reason;
           session_data_.compiler_filter = message.compiler_filter;
+
+          UpdateSessionInBackends();
         });
   }
 
@@ -183,7 +198,7 @@
 
   if (!session_started_) {
     for (auto& backend : backends_) {
-      backend->BeginSession(session_data_);
+      backend->BeginOrUpdateSession(session_data_);
     }
     session_started_ = true;
   }
@@ -193,6 +208,14 @@
   }
 }
 
+void MetricsReporter::UpdateSessionInBackends() {
+  if (session_started_) {
+    for (auto& backend : backends_) {
+      backend->BeginOrUpdateSession(session_data_);
+    }
+  }
+}
+
 bool MetricsReporter::ShouldReportAtStartup() const {
   return IsMetricsReportingEnabled(session_data_) &&
       config_.period_spec.has_value() &&
diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h
index 4db5df1..ed7022d 100644
--- a/runtime/metrics/reporter.h
+++ b/runtime/metrics/reporter.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_METRICS_REPORTER_H_
 #define ART_RUNTIME_METRICS_REPORTER_H_
 
+#include "app_info.h"
 #include "base/message_queue.h"
 #include "base/metrics/metrics.h"
 
@@ -110,6 +111,10 @@
   // completes.
   void NotifyStartupCompleted();
 
+  // Notifies the reporter that the app info was updated. This is used to detect / infer
+  // the compiler filter / reason of primary apks.
+  void NotifyAppInfoUpdated(AppInfo* app_info);
+
   // Requests a metrics report
   //
   // If synchronous is set to true, this function will block until the report has completed.
@@ -120,7 +125,7 @@
   void ReloadConfig(const ReportingConfig& config);
 
   void SetCompilationInfo(CompilationReason compilation_reason,
-                          CompilerFilter::Filter compiler_filter);
+                          CompilerFilterReporting compiler_filter);
 
   static constexpr const char* kBackgroundThreadName = "Metrics Background Reporting Thread";
 
@@ -145,6 +150,9 @@
   // Outputs the current state of the metrics to the destination set by config_.
   void ReportMetrics();
 
+  // Updates the session data in all the backends.
+  void UpdateSessionInBackends();
+
   // Whether or not we should wait for startup before reporting for the first time.
   bool ShouldReportAtStartup() const;
 
@@ -182,7 +190,7 @@
 
   struct CompilationInfoMessage {
     CompilationReason compilation_reason;
-    CompilerFilter::Filter compiler_filter;
+    CompilerFilterReporting compiler_filter;
   };
 
   MessageQueue<ShutdownRequestedMessage,
diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc
index 8c2a581..3807c77 100644
--- a/runtime/metrics/reporter_test.cc
+++ b/runtime/metrics/reporter_test.cc
@@ -55,7 +55,7 @@
     SafeMap<DatumId, uint64_t> data;
   };
 
-  void BeginSession(const SessionData& session_data) override {
+  void BeginOrUpdateSession(const SessionData& session_data) override {
     session_data_ = session_data;
   }
 
@@ -84,6 +84,10 @@
     return reports_;
   }
 
+  const SessionData& GetSessionData() {
+    return session_data_;
+  }
+
  private:
   SessionData session_data_;
   std::vector<Report> reports_;
@@ -163,7 +167,11 @@
   //   1) don't add metrics (with_metrics = false)
   //   2) or always add the same metrics (see MaybeStartBackgroundThread)
   // So we can write a global verify method.
-  void VerifyReports(uint32_t size, bool with_metrics) {
+  void VerifyReports(
+        uint32_t size,
+        bool with_metrics,
+        CompilerFilterReporting filter = CompilerFilterReporting::kUnknown,
+        CompilationReason reason = CompilationReason::kUnknown) {
     // TODO: we should iterate through all the other metrics to make sure they were not
     // reported. However, we don't have an easy to use iteration mechanism over metrics yet.
     // We should ads one
@@ -172,6 +180,9 @@
       ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u);
       ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u);
     }
+
+    ASSERT_EQ(backend_->GetSessionData().compiler_filter, filter);
+    ASSERT_EQ(backend_->GetSessionData().compilation_reason, reason);
   }
 
   // Sleeps until the backend received the give number of reports.
@@ -184,6 +195,10 @@
     }
   }
 
+  void NotifyAppInfoUpdated(AppInfo* app_info) {
+    reporter_->NotifyAppInfoUpdated(app_info);
+  }
+
  private:
   std::unique_ptr<MockMetricsReporter> reporter_;
   TestBackend* backend_;
@@ -352,13 +367,38 @@
 TEST_F(MetricsReporterTest, SampleRateEnableAll) {
   SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
 
-  // The background thread should not start.
+  // The background thread should start.
   ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
 
   ASSERT_FALSE(ShouldReportAtStartup());
   ASSERT_TRUE(ShouldContinueReporting());
 }
 
+TEST_F(MetricsReporterTest, CompilerFilter) {
+  SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
+  ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ true));
+
+  AppInfo app_info;
+  app_info.RegisterOdexStatus(
+      "code_location",
+      "verify",
+      "install",
+      "odex_status");
+  app_info.RegisterAppInfo(
+      "package_name",
+      std::vector<std::string>({"code_location"}),
+      "",
+      "",
+      AppInfo::CodeType::kPrimaryApk);
+  NotifyAppInfoUpdated(&app_info);
+
+  WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
+  VerifyReports(
+      /*size=*/ 1,
+      /*with_metrics*/ true,
+      CompilerFilterReporting::kVerify,
+      CompilationReason::kInstall);
+}
 
 // Test class for period spec parsing
 class ReportingPeriodSpecTest : public testing::Test {
diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc
index 725a965..b92fd83 100644
--- a/runtime/metrics/statsd.cc
+++ b/runtime/metrics/statsd.cc
@@ -16,6 +16,7 @@
 
 #include "statsd.h"
 
+#include "arch/instruction_set.h"
 #include "base/compiler_filter.h"
 #include "base/metrics/metrics.h"
 #include "statslog_art.h"
@@ -108,31 +109,35 @@
   }
 }
 
-constexpr int32_t EncodeCompileFilter(std::optional<CompilerFilter::Filter> filter) {
-  if (filter.has_value()) {
-    switch (filter.value()) {
-      case CompilerFilter::kAssumeVerified:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED;
-      case CompilerFilter::kExtract:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT;
-      case CompilerFilter::kVerify:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY;
-      case CompilerFilter::kSpaceProfile:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE;
-      case CompilerFilter::kSpace:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE;
-      case CompilerFilter::kSpeedProfile:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE;
-      case CompilerFilter::kSpeed:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED;
-      case CompilerFilter::kEverythingProfile:
-        return statsd::
-            ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE;
-      case CompilerFilter::kEverything:
-        return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING;
-    }
-  } else {
-    return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN;
+constexpr int32_t EncodeCompileFilter(CompilerFilterReporting filter) {
+  switch (filter) {
+    case CompilerFilterReporting::kAssumeVerified:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ASSUMED_VERIFIED;
+    case CompilerFilterReporting::kExtract:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EXTRACT;
+    case CompilerFilterReporting::kVerify:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_VERIFY;
+    case CompilerFilterReporting::kSpaceProfile:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE_PROFILE;
+    case CompilerFilterReporting::kSpace:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPACE;
+    case CompilerFilterReporting::kSpeedProfile:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED_PROFILE;
+    case CompilerFilterReporting::kSpeed:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_SPEED;
+    case CompilerFilterReporting::kEverythingProfile:
+      return statsd::
+          ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING_PROFILE;
+    case CompilerFilterReporting::kEverything:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_EVERYTHING;
+    case CompilerFilterReporting::kError:
+      return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_ERROR;
+    case CompilerFilterReporting::kUnknown:
+       return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN;
+    case CompilerFilterReporting::kRunFromApk:
+       return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK;
+    case CompilerFilterReporting::kRunFromApkFallback:
+       return statsd::ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
   }
 }
 
@@ -181,9 +186,28 @@
   }
 }
 
+constexpr int32_t EncodeInstructionSet(InstructionSet isa) {
+  switch (isa) {
+    case InstructionSet::kArm:
+      // Fall-through.
+    case InstructionSet::kThumb2:
+      return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM;
+    case InstructionSet::kArm64:
+      return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM64;
+    case InstructionSet::kX86:
+      return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86;
+    case InstructionSet::kX86_64:
+      return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86_64;
+    case InstructionSet::kNone:
+      return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN;
+  }
+}
+
 class StatsdBackend : public MetricsBackend {
  public:
-  void BeginSession(const SessionData& session_data) override { session_data_ = session_data; }
+  void BeginOrUpdateSession(const SessionData& session_data) override {
+    session_data_ = session_data;
+  }
 
  protected:
   void BeginReport(uint64_t timestamp_since_start_ms) override {
@@ -206,7 +230,7 @@
           static_cast<int64_t>(value),
           statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN,
           statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN,
-          statsd::ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN);
+          EncodeInstructionSet(kRuntimeISA));
     }
   }
 
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index d46a36f..8425904 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -370,11 +370,11 @@
  */
 static void VMRuntime_registerAppInfo(JNIEnv* env,
                                       jclass clazz ATTRIBUTE_UNUSED,
-                                      jstring package_name ATTRIBUTE_UNUSED,
+                                      jstring package_name,
                                       jstring cur_profile_file,
                                       jstring ref_profile_file,
                                       jobjectArray code_paths,
-                                      jint code_path_type ATTRIBUTE_UNUSED) {
+                                      jint code_path_type) {
   std::vector<std::string> code_paths_vec;
   int code_paths_length = env->GetArrayLength(code_paths);
   for (int i = 0; i < code_paths_length; i++) {
@@ -392,7 +392,16 @@
   std::string ref_profile_file_str(raw_ref_profile_file);
   env->ReleaseStringUTFChars(ref_profile_file, raw_ref_profile_file);
 
-  Runtime::Current()->RegisterAppInfo(code_paths_vec, cur_profile_file_str, ref_profile_file_str);
+  const char* raw_package_name = env->GetStringUTFChars(package_name, nullptr);
+  std::string package_name_str(raw_package_name);
+  env->ReleaseStringUTFChars(package_name, raw_package_name);
+
+  Runtime::Current()->RegisterAppInfo(
+      package_name_str,
+      code_paths_vec,
+      cur_profile_file_str,
+      ref_profile_file_str,
+      static_cast<int32_t>(code_path_type));
 }
 
 static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 391d597..a891aa3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -141,26 +141,6 @@
   return oat_files;
 }
 
-bool OatFileManager::GetPrimaryOatFileInfo(std::string* compilation_reason,
-                                           CompilerFilter::Filter* compiler_filter) const {
-  ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
-  std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
-  if (!boot_oat_files.empty()) {
-    for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
-      if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) ==
-          boot_oat_files.end()) {
-        const char* reason = oat_file->GetCompilationReason();
-        if (reason != nullptr) {
-          *compilation_reason = reason;
-        }
-        *compiler_filter = oat_file->GetCompilerFilter();
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 OatFileManager::OatFileManager()
     : only_use_system_oat_files_(false) {}
 
@@ -231,6 +211,12 @@
         &compilation_reason,
         &odex_status);
 
+    Runtime::Current()->GetAppInfo()->RegisterOdexStatus(
+        dex_location,
+        compilation_filter,
+        compilation_reason,
+        odex_status);
+
     ScopedTrace odex_loading(StringPrintf(
         "location=%s status=%s filter=%s reason=%s",
         odex_location.c_str(),
@@ -420,6 +406,10 @@
     Runtime::Current()->GetJit()->RegisterDexFiles(dex_files, class_loader);
   }
 
+  // Now that we loaded the dex/odex files, notify the runtime.
+  // Note that we do this everytime we load dex files.
+  Runtime::Current()->NotifyDexFileLoaded();
+
   return dex_files;
 }
 
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index f8a7341..abf4ea0 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -71,11 +71,6 @@
   // Returns the boot image oat files.
   std::vector<const OatFile*> GetBootOatFiles() const;
 
-  // Fetches information from the primary oat file.
-  bool GetPrimaryOatFileInfo(std::string* compilation_reason,
-                             CompilerFilter::Filter* compiler_filter)
-      const REQUIRES(!Locks::oat_file_manager_lock_);
-
   // Returns the oat files for the images, registers the oat files.
   // Takes ownership of the imagespace's underlying oat files.
   std::vector<const OatFile*> RegisterImageOatFiles(
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 25bfdc7..8af3558 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -34,6 +34,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <limits>
+#include <string.h>
 #include <thread>
 #include <unordered_set>
 #include <vector>
@@ -1004,12 +1005,19 @@
       !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) {
     std::vector<std::string> dex_filenames;
     Split(class_path_string_, ':', &dex_filenames);
-    // It's ok to pass "" to the ref profile filename. It indicates we don't have
+
+    // We pass "" as the package name because at this point we don't know it. It could be the
+    // Zygote or it could be a dalvikvm cmd line execution. The package name will be re-set during
+    // post-fork or during RegisterAppInfo.
+    //
+    // Also, it's ok to pass "" to the ref profile filename. It indicates we don't have
     // a reference profile.
     RegisterAppInfo(
+        /*package_name=*/ "",
         dex_filenames,
         jit_options_->GetProfileSaverOptions().GetProfilePath(),
-        /*ref_profile_filename=*/ "");
+        /*ref_profile_filename=*/ "",
+        kVMRuntimePrimaryApk);
   }
 
   return true;
@@ -1056,6 +1064,22 @@
   DCHECK(!IsZygote());
 
   if (is_system_server) {
+    // Register the system server code paths.
+    // TODO: Ideally this should be done by the VMRuntime#RegisterAppInfo. However, right now
+    // the method is only called when we set up the profile. It should be called all the time
+    // (simillar to the apps). Once that's done this manual registration can be removed.
+    const char* system_server_classpath = getenv("SYSTEMSERVERCLASSPATH");
+    if (system_server_classpath == nullptr || (strlen(system_server_classpath) == 0)) {
+      LOG(WARNING) << "System server class path not set";
+    } else {
+      std::vector<std::string> jars = android::base::Split(system_server_classpath, ":");
+      app_info_.RegisterAppInfo("android",
+                                jars,
+                                /*cur_profile_path=*/ "",
+                                /*ref_profile_path=*/ "",
+                                AppInfo::CodeType::kPrimaryApk);
+    }
+
     // Set the system server package name to "android".
     // This is used to tell the difference between samples provided by system server
     // and samples generated by other apps when processing boot image profiles.
@@ -1101,6 +1125,8 @@
     session_data.session_id = GetRandomNumber<int64_t>(1, std::numeric_limits<int64_t>::max());
     // TODO: set session_data.compilation_reason and session_data.compiler_filter
     metrics_reporter_->MaybeStartBackgroundThread(session_data);
+    // Also notify about any updates to the app info.
+    metrics_reporter_->NotifyAppInfoUpdated(&app_info_);
   }
 
   StartSignalCatcher();
@@ -2566,9 +2592,22 @@
   }
 }
 
-void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
+void Runtime::RegisterAppInfo(const std::string& package_name,
+                              const std::vector<std::string>& code_paths,
                               const std::string& profile_output_filename,
-                              const std::string& ref_profile_filename) {
+                              const std::string& ref_profile_filename,
+                              int32_t code_type) {
+  app_info_.RegisterAppInfo(
+      package_name,
+      code_paths,
+      profile_output_filename,
+      ref_profile_filename,
+      AppInfo::FromVMRuntimeConstants(code_type));
+
+  if (metrics_reporter_ != nullptr) {
+    metrics_reporter_->NotifyAppInfoUpdated(&app_info_);
+  }
+
   if (jit_.get() == nullptr) {
     // We are not JITing. Nothing to do.
     return;
@@ -3147,6 +3186,8 @@
     return;
   }
 
+  VLOG(startup) << app_info_;
+
   VLOG(startup) << "Adding NotifyStartupCompleted task";
   // Use the heap task processor since we want to be exclusive with the GC and we don't want to
   // block the caller if the GC is running.
@@ -3158,18 +3199,16 @@
   ProfileSaver::NotifyStartupCompleted();
 
   if (metrics_reporter_ != nullptr) {
-    std::string compilation_reason;
-    CompilerFilter::Filter compiler_filter;
-    if (oat_file_manager_->GetPrimaryOatFileInfo(&compilation_reason, &compiler_filter)) {
-      metrics_reporter_->SetCompilationInfo(
-          compilation_reason.empty() ? metrics::CompilationReasonFromName(compilation_reason)
-                                     : metrics::CompilationReason::kUnknown,
-          compiler_filter);
-    }
     metrics_reporter_->NotifyStartupCompleted();
   }
 }
 
+void Runtime::NotifyDexFileLoaded() {
+  if (metrics_reporter_ != nullptr) {
+    metrics_reporter_->NotifyAppInfoUpdated(&app_info_);
+  }
+}
+
 bool Runtime::GetStartupCompleted() const {
   return startup_completed_.load(std::memory_order_seq_cst);
 }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 59f4d19..180eeda 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -27,6 +27,7 @@
 #include <utility>
 #include <vector>
 
+#include "app_info.h"
 #include "base/locks.h"
 #include "base/macros.h"
 #include "base/mem_map.h"
@@ -528,9 +529,11 @@
     return &instrumentation_;
   }
 
-  void RegisterAppInfo(const std::vector<std::string>& code_paths,
+  void RegisterAppInfo(const std::string& package_name,
+                       const std::vector<std::string>& code_paths,
                        const std::string& profile_output_filename,
-                       const std::string& ref_profile_filename);
+                       const std::string& ref_profile_filename,
+                       int32_t code_type);
 
   // Transaction support.
   bool IsActiveTransaction() const;
@@ -973,6 +976,10 @@
   // first call.
   void NotifyStartupCompleted();
 
+  // Notify the runtime that the application finished loading some dex/odex files. This is
+  // called everytime we load a set of dex files in a class loader.
+  void NotifyDexFileLoaded();
+
   // Return true if startup is already completed.
   bool GetStartupCompleted() const;
 
@@ -1000,6 +1007,8 @@
 
   metrics::ArtMetrics* GetMetrics() { return &metrics_; }
 
+  AppInfo* GetAppInfo() { return &app_info_; }
+
   void RequestMetricsReport(bool synchronous = true);
 
   static void MadviseFileForRange(size_t madvise_size_limit_bytes,
@@ -1411,6 +1420,9 @@
   // the third entry in the example above).
   std::string apex_versions_;
 
+  // The info about the application code paths.
+  AppInfo app_info_;
+
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class Dex2oatImageTest;