Update dexpreopt_test to verify boot images.

This ensures that odrefresh does not unexpectedly override the boot
image on /system on the first boot.

Bug: 203492478
Test: Build a system image, flash it to a device, and run
  `atest art_standalone_dexpreopt_test`.
Change-Id: Id02e60612e6b3c12876cc8707e6d2be3866a5b6f
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 06c8cfc..1f034e6 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -236,13 +236,11 @@
     static: {
         whole_static_libs: [
             "libc++fs",
-            "libprocinfo",
         ],
     },
     shared: {
         static_libs: [
             "libc++fs",
-            "libprocinfo",
         ],
     },
 }
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index 20bc87c..593ea25 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -50,7 +50,6 @@
 #include "dex/primitive.h"
 #include "gtest/gtest.h"
 #include "nativehelper/scoped_local_ref.h"
-#include "procinfo/process.h"
 
 namespace art {
 
@@ -680,12 +679,16 @@
 std::vector<pid_t> GetPidByName(const std::string& process_name) {
   std::vector<pid_t> results;
   for (pid_t pid : android::base::AllPids{}) {
-    android::procinfo::ProcessInfo process_info;
-    std::string error;
-    if (!android::procinfo::GetProcessInfo(pid, &process_info, &error)) {
+    std::string cmdline;
+    if (!android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline)) {
       continue;
     }
-    if (process_info.name == process_name) {
+    // Take the first argument.
+    size_t pos = cmdline.find('\0');
+    if (pos != std::string::npos) {
+      cmdline.resize(pos);
+    }
+    if (cmdline == process_name) {
       results.push_back(pid);
     }
   }
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index a33c632..6124ed9 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -287,8 +287,8 @@
 template <typename Param>
 using CommonArtTestWithParam = CommonArtTestBase<testing::TestWithParam<Param>>;
 
-// Returns a list of PIDs of the processes whose process name (the name of the binary without path)
-// fully matches the given name.
+// Returns a list of PIDs of the processes whose process name (the first commandline argument) fully
+// matches the given name.
 std::vector<pid_t> GetPidByName(const std::string& process_name);
 
 #define TEST_DISABLED_FOR_TARGET() \
diff --git a/test/dexpreopt/dexpreopt_test.cc b/test/dexpreopt/dexpreopt_test.cc
index 2dd5662..5315937 100644
--- a/test/dexpreopt/dexpreopt_test.cc
+++ b/test/dexpreopt/dexpreopt_test.cc
@@ -28,12 +28,16 @@
 #include <iterator>
 #include <string>
 #include <unordered_set>
+#include <utility>
 #include <vector>
 
+#include "android-base/properties.h"
 #include "android-base/result.h"
+#include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "arch/instruction_set.h"
 #include "base/common_art_test.h"
+#include "base/file_utils.h"
 #include "base/os.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -44,6 +48,9 @@
 
 using ::testing::IsSubsetOf;
 
+constexpr const char* kZygote32 = "zygote";
+constexpr const char* kZygote64 = "zygote64";
+
 std::vector<std::string> GetListFromEnv(const std::string& name) {
   const char* env_value = getenv(name.c_str());
   if (env_value == nullptr || strlen(env_value) == 0) {
@@ -52,7 +59,74 @@
   return android::base::Split(env_value, ":");
 }
 
-android::base::Result<std::vector<std::string>> GetSystemServerArtifacts() {
+android::base::Result<std::vector<std::pair<std::string, InstructionSet>>> GetZygoteNamesAndIsas() {
+  std::vector<std::pair<std::string, InstructionSet>> names_and_isas;
+
+  // Possible values are: "zygote32", "zygote64", "zygote32_64", "zygote64_32".
+  std::string zygote_kinds = android::base::GetProperty("ro.zygote", {});
+  if (zygote_kinds.empty()) {
+    return Errorf("Unable to get Zygote kinds");
+  }
+
+  switch (kRuntimeISA) {
+    case InstructionSet::kArm:
+    case InstructionSet::kArm64:
+      if (zygote_kinds.find("32") != std::string::npos) {
+        names_and_isas.push_back(std::make_pair(kZygote32, InstructionSet::kArm));
+      }
+      if (zygote_kinds.find("64") != std::string::npos) {
+        names_and_isas.push_back(std::make_pair(kZygote64, InstructionSet::kArm64));
+      }
+      break;
+    case InstructionSet::kX86:
+    case InstructionSet::kX86_64:
+      if (zygote_kinds.find("32") != std::string::npos) {
+        names_and_isas.push_back(std::make_pair(kZygote32, InstructionSet::kX86));
+      }
+      if (zygote_kinds.find("64") != std::string::npos) {
+        names_and_isas.push_back(std::make_pair(kZygote64, InstructionSet::kX86_64));
+      }
+      break;
+    default:
+      return Errorf("Unknown runtime ISA: {}", GetInstructionSetString(kRuntimeISA));
+  }
+
+  return names_and_isas;
+}
+
+android::base::Result<std::vector<std::string>> GetZygoteExpectedArtifacts(InstructionSet isa) {
+  std::vector<std::string> jars = GetListFromEnv("DEX2OATBOOTCLASSPATH");
+  if (jars.empty()) {
+    return Errorf("Environment variable `DEX2OATBOOTCLASSPATH` is not defined or empty");
+  }
+  std::string art_root = GetArtRoot();
+  std::string android_root = GetAndroidRoot();
+  std::vector<std::string> artifacts;
+  for (size_t i = 0; i < jars.size(); i++) {
+    const std::string& jar = jars[i];
+    std::string basename =
+        i == 0 ? "boot.oat" : "boot-" + ReplaceFileExtension(android::base::Basename(jar), "oat");
+    // TODO(b/211973309): Update this once the primary boot image is moved.
+    std::string dir = android::base::StartsWith(jar, art_root) ? art_root + "/javalib" :
+                                                                 android_root + "/framework";
+    std::string oat_file = android::base::StringPrintf(
+        "%s/%s/%s", dir.c_str(), GetInstructionSetString(isa), basename.c_str());
+
+    if (!OS::FileExists(oat_file.c_str())) {
+      if (errno == EACCES) {
+        return ErrnoErrorf("Failed to stat() {}", oat_file);
+      }
+      // Dexpreopting is probably disabled. No need to report missing artifacts here because
+      // artifact generation is already checked at build time.
+      continue;
+    }
+
+    artifacts.push_back(oat_file);
+  }
+  return artifacts;
+}
+
+android::base::Result<std::vector<std::string>> GetSystemServerExpectedArtifacts() {
   std::vector<std::string> jars = GetListFromEnv("SYSTEMSERVERCLASSPATH");
   if (jars.empty()) {
     return Errorf("Environment variable `SYSTEMSERVERCLASSPATH` is not defined or empty");
@@ -85,31 +159,76 @@
   return artifacts;
 }
 
+android::base::Result<std::vector<std::string>> GetMappedFiles(pid_t pid,
+                                                               const std::string& extension,
+                                                               uint16_t flags) {
+  std::vector<android::procinfo::MapInfo> maps;
+  if (!android::procinfo::ReadProcessMaps(pid, &maps)) {
+    return ErrnoErrorf("Failed to get mapped memory regions of pid {}", pid);
+  }
+  std::vector<std::string> files;
+  for (const android::procinfo::MapInfo& map : maps) {
+    if ((map.flags & flags) && android::base::EndsWith(map.name, extension)) {
+      files.push_back(map.name);
+    }
+  }
+  return files;
+}
+
+android::base::Result<std::vector<std::string>> GetZygoteMappedOatFiles(
+    const std::string& zygote_name) {
+  std::vector<pid_t> pids = art::GetPidByName(zygote_name);
+  if (pids.empty()) {
+    return Errorf("Unable to find Zygote process: {}", zygote_name);
+  }
+  // OAT files in boot images may not be mmaped with PROT_EXEC if they don't contain executable
+  // code. Checking PROT_READ is sufficient because an OAT file will be unmapped if the runtime
+  // rejects it.
+  return GetMappedFiles(pids[0], ".oat", PROT_READ);
+}
+
 android::base::Result<std::vector<std::string>> GetSystemServerArtifactsMappedOdexes() {
   std::vector<pid_t> pids = art::GetPidByName("system_server");
   if (pids.size() != 1) {
     return Errorf("There should be exactly one `system_server` process, found {}", pids.size());
   }
-  pid_t pid = pids[0];
-  std::vector<android::procinfo::MapInfo> maps;
-  if (!android::procinfo::ReadProcessMaps(pid, &maps)) {
-    return ErrnoErrorf("Failed to get mapped memory regions of `system_server`");
-  }
-  std::vector<std::string> odexes;
-  for (const android::procinfo::MapInfo& map : maps) {
-    if ((map.flags & PROT_EXEC) && android::base::EndsWith(map.name, ".odex")) {
-      odexes.push_back(map.name);
+  // Unlike boot images, app images don't get unmapped if the runtime rejects them in some cases
+  // (e.g., CLC mismatch). Therefore, we need to check the PROT_EXEC flag to ensure that they are
+  // valid.
+  // The ODEX files always contain executable code because system server jars are compiled with the
+  // "speed" filter.
+  return GetMappedFiles(pids[0], ".odex", PROT_EXEC);
+}
+
+TEST(DexpreoptTest, ForZygote) {
+  android::base::Result<std::vector<std::pair<std::string, InstructionSet>>> zygote_names_and_isas =
+      GetZygoteNamesAndIsas();
+  ASSERT_RESULT_OK(zygote_names_and_isas);
+
+  for (const auto& [zygote_name, isa] : *zygote_names_and_isas) {
+    android::base::Result<std::vector<std::string>> expected_artifacts =
+        GetZygoteExpectedArtifacts(isa);
+    ASSERT_RESULT_OK(expected_artifacts);
+
+    if (expected_artifacts->empty()) {
+      // Skip the test if dexpreopting is disabled.
+      return;
     }
+
+    android::base::Result<std::vector<std::string>> mapped_oat_files =
+        GetZygoteMappedOatFiles(zygote_name);
+    ASSERT_RESULT_OK(mapped_oat_files);
+
+    EXPECT_THAT(expected_artifacts.value(), IsSubsetOf(mapped_oat_files.value()));
   }
-  return odexes;
 }
 
 TEST(DexpreoptTest, ForSystemServer) {
-  android::base::Result<std::vector<std::string>> system_server_artifacts =
-      GetSystemServerArtifacts();
-  ASSERT_RESULT_OK(system_server_artifacts);
+  android::base::Result<std::vector<std::string>> expected_artifacts =
+      GetSystemServerExpectedArtifacts();
+  ASSERT_RESULT_OK(expected_artifacts);
 
-  if (system_server_artifacts->empty()) {
+  if (expected_artifacts->empty()) {
     // Skip the test if dexpreopting is disabled.
     return;
   }
@@ -118,7 +237,7 @@
       GetSystemServerArtifactsMappedOdexes();
   ASSERT_RESULT_OK(mapped_odexes);
 
-  EXPECT_THAT(system_server_artifacts.value(), IsSubsetOf(mapped_odexes.value()));
+  EXPECT_THAT(expected_artifacts.value(), IsSubsetOf(mapped_odexes.value()));
 }
 
 }  // namespace art