DO NOT MERGE GetActivePackages() opens "block" apexes with size

"block" apexes require size to open. In GetActivePackages(), size is
retrieved by looking up ApexFileRepository and passed to
ApexFile::Open().

Ignore-AOSP-First: not for S

Bug: 187686900
Test: ApexTestCases
Merged-In: I63779f4f2efb6c741b678a8f2baf727b7da4049a
Change-Id: I63779f4f2efb6c741b678a8f2baf727b7da4049a
(cherry picked from commit a6f7dcfd431c2a0eefc7d5d10f18d9bcf0545bf0)
diff --git a/apexd/apex_file.cpp b/apexd/apex_file.cpp
index 89ff213..577e514 100644
--- a/apexd/apex_file.cpp
+++ b/apexd/apex_file.cpp
@@ -178,7 +178,7 @@
   }
 
   return ApexFile(realpath, image_offset, image_size, std::move(*manifest),
-                  pubkey, fs_type, is_compressed);
+                  pubkey, fs_type, is_compressed, size);
 }
 
 // AVB-related code.
diff --git a/apexd/apex_file.h b/apexd/apex_file.h
index 0983661..de9254a 100644
--- a/apexd/apex_file.h
+++ b/apexd/apex_file.h
@@ -56,6 +56,7 @@
   const std::string& GetPath() const { return apex_path_; }
   const std::optional<int32_t>& GetImageOffset() const { return image_offset_; }
   const std::optional<size_t>& GetImageSize() const { return image_size_; }
+  const std::optional<uint32_t>& GetFileSize() const { return file_size_; }
   const ::apex::proto::ApexManifest& GetManifest() const { return manifest_; }
   const std::string& GetBundledPublicKey() const { return apex_pubkey_; }
   const std::optional<std::string>& GetFsType() const { return fs_type_; }
@@ -69,14 +70,16 @@
            const std::optional<int32_t>& image_offset,
            const std::optional<size_t>& image_size,
            ::apex::proto::ApexManifest manifest, const std::string& apex_pubkey,
-           const std::optional<std::string>& fs_type, bool is_compressed)
+           const std::optional<std::string>& fs_type, bool is_compressed,
+           std::optional<size_t> file_size)
       : apex_path_(apex_path),
         image_offset_(image_offset),
         image_size_(image_size),
         manifest_(std::move(manifest)),
         apex_pubkey_(apex_pubkey),
         fs_type_(fs_type),
-        is_compressed_(is_compressed) {}
+        is_compressed_(is_compressed),
+        file_size_(file_size) {}
 
   std::string apex_path_;
   std::optional<int32_t> image_offset_;
@@ -85,6 +88,7 @@
   std::string apex_pubkey_;
   std::optional<std::string> fs_type_;
   bool is_compressed_;
+  std::optional<uint32_t> file_size_;
 };
 
 }  // namespace apex
diff --git a/apexd/apex_file_repository.cpp b/apexd/apex_file_repository.cpp
index 0d0b457..c863f72 100644
--- a/apexd/apex_file_repository.cpp
+++ b/apexd/apex_file_repository.cpp
@@ -375,5 +375,20 @@
   return std::cref(it->second);
 }
 
+std::optional<ApexFileRef> ApexFileRepository::GetApexFile(
+    const std::string& full_path) const {
+  for (const auto& [_, apex] : pre_installed_store_) {
+    if (apex.GetPath() == full_path) {
+      return std::cref(apex);
+    }
+  }
+  for (const auto& [_, apex] : data_store_) {
+    if (apex.GetPath() == full_path) {
+      return std::cref(apex);
+    }
+  }
+  return std::nullopt;
+}
+
 }  // namespace apex
 }  // namespace android
diff --git a/apexd/apex_file_repository.h b/apexd/apex_file_repository.h
index 8fccc02..ca2dbc8 100644
--- a/apexd/apex_file_repository.h
+++ b/apexd/apex_file_repository.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <functional>
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <vector>
@@ -123,6 +124,9 @@
   // using |HasPreinstalledVersion| function.
   ApexFileRef GetPreInstalledApex(const std::string& name) const;
 
+  // Returns an instance matching with |full_path|
+  std::optional<ApexFileRef> GetApexFile(const std::string& full_path) const;
+
   // Clears ApexFileRepostiry.
   // Only use in tests.
   void Reset(const std::string& decompression_dir = kApexDecompressedDir) {
diff --git a/apexd/apex_file_repository_test.cpp b/apexd/apex_file_repository_test.cpp
index 76ae8de..2fbbc1a 100644
--- a/apexd/apex_file_repository_test.cpp
+++ b/apexd/apex_file_repository_test.cpp
@@ -44,6 +44,8 @@
 using android::base::GetExecutableDirectory;
 using android::base::StringPrintf;
 using ::testing::ByRef;
+using ::testing::Eq;
+using ::testing::Optional;
 using ::testing::UnorderedElementsAre;
 
 static std::string GetTestDataDir() { return GetExecutableDirectory(); }
@@ -561,6 +563,38 @@
   ASSERT_THAT(ret, ApexFileEq(ByRef(*apex)));
 }
 
+TEST(ApexFileRepositoryTest, GetApexFileWithPath) {
+  // Prepare test data.
+  TemporaryDir built_in_dir;
+  fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
+
+  ApexFileRepository instance;
+  ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path})));
+
+  auto apex_path = StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path);
+  auto apex = ApexFile::Open(apex_path);
+  ASSERT_RESULT_OK(apex);
+
+  auto ret = instance.GetApexFile(apex_path);
+  ASSERT_THAT(ret, Optional(ApexFileEq(ByRef(*apex))));
+}
+
+TEST(ApexFileRepositoryTest, GetApexFileReturnsNulloptWithUnknownPath) {
+  // Prepare test data.
+  TemporaryDir built_in_dir;
+  fs::copy(GetTestFile("apex.apexd_test.apex"), built_in_dir.path);
+
+  ApexFileRepository instance;
+  ASSERT_TRUE(IsOk(instance.AddPreInstalledApex({built_in_dir.path})));
+
+  auto apex_path = StringPrintf("%s/apex.apexd_test.apex", built_in_dir.path);
+  auto apex = ApexFile::Open(apex_path);
+  ASSERT_RESULT_OK(apex);
+
+  auto ret = instance.GetApexFile(apex_path + ".wrong");
+  ASSERT_THAT(ret, Eq(std::nullopt));
+}
+
 TEST(ApexFileRepositoryTest, GetPreInstalledApexNoSuchApexAborts) {
   ASSERT_DEATH(
       {
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index f0646b0..0fef46e 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -1304,13 +1304,21 @@
 
 std::vector<ApexFile> GetActivePackages() {
   std::vector<ApexFile> ret;
+  const auto& instance = ApexFileRepository::GetInstance();
   gMountedApexes.ForallMountedApexes(
       [&](const std::string&, const MountedApexData& data, bool latest) {
         if (!latest) {
           return;
         }
-
-        Result<ApexFile> apex_file = ApexFile::Open(data.full_path);
+        // A block apex requires its size to open it.
+        // Retrieves file_size from ApexFileRepository.
+        // TODO(b/187783952): we can remove this when the block device
+        // can be open without size.
+        std::optional<uint32_t> file_size = std::nullopt;
+        if (auto apex = instance.GetApexFile(data.full_path); apex) {
+          file_size = apex->get().GetFileSize();
+        }
+        Result<ApexFile> apex_file = ApexFile::Open(data.full_path, file_size);
         if (!apex_file.ok()) {
           return;
         }
diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp
index 3395202..3c80c1b 100644
--- a/apexd/apexd_test.cpp
+++ b/apexd/apexd_test.cpp
@@ -126,11 +126,18 @@
     static constexpr const int kFirstApexPartition = 2;
     auto partition_num = kFirstApexPartition + signature->apexes_size();
     auto apex_path = StringPrintf("%s/vm-payload-%d", td_.path, partition_num);
-    fs::copy(GetTestFile(apex_name), apex_path);
+    auto apex_size = *GetFileSize(GetTestFile(apex_name));
+    {
+      // Create a temp file with padding at the end
+      std::ofstream out(apex_path);
+      std::ifstream in(GetTestFile(apex_name), std::ios::binary);
+      out << in.rdbuf();
+      out << std::string(10, 0);  // ten zeros.
+    }
 
     auto apex = signature->add_apexes();
     apex->set_name("apex" + std::to_string(partition_num));
-    apex->set_size(*GetFileSize(apex_path));
+    apex->set_size(apex_size);
     if (pubkey.has_value()) {
       apex->set_publickey(*pubkey);
     }
@@ -2604,12 +2611,14 @@
   // Need to call InitializeVold before calling OnStart
   InitializeVold(&checkpoint_interface);
 
-  AddPreInstalledApex("apex.apexd_test.apex");
-  AddPreInstalledApex("apex.apexd_test_different_app.apex");
+  auto path1 = AddPreInstalledApex("apex.apexd_test.apex");
+  auto path2 = AddPreInstalledApex("apex.apexd_test_different_app.apex");
   // In VM mode, we don't scan /data/apex
   AddDataApex("apex.apexd_test_v2.apex");
 
   ASSERT_EQ(0, OnStartInVmMode());
+  UnmountOnTearDown(path1);
+  UnmountOnTearDown(path2);
 
   auto apex_mounts = GetApexMounts();
   ASSERT_THAT(apex_mounts,
@@ -2638,9 +2647,10 @@
   // Need to call InitializeVold before calling OnStart
   InitializeVold(&checkpoint_interface);
 
-  AddBlockApex("apex.apexd_test.apex");
+  auto path1 = AddBlockApex("apex.apexd_test.apex");
 
   ASSERT_EQ(0, OnStartInVmMode());
+  UnmountOnTearDown(path1);
 
   auto apex_mounts = GetApexMounts();
   ASSERT_THAT(apex_mounts,
@@ -2648,6 +2658,19 @@
                                    "/apex/com.android.apex.test_package@1",
                                    // Emits apex-info-list as well
                                    "/apex/apex-info-list.xml"));
+
+  ASSERT_EQ(access("/apex/apex-info-list.xml", F_OK), 0);
+  auto info_list =
+      com::android::apex::readApexInfoList("/apex/apex-info-list.xml");
+  ASSERT_TRUE(info_list.has_value());
+  auto apex_info_xml_1 = com::android::apex::ApexInfo(
+      /* moduleName= */ "com.android.apex.test_package",
+      /* modulePath= */ path1,
+      /* preinstalledModulePath= */ path1,
+      /* versionCode= */ 1, /* versionName= */ "1",
+      /* isFactory= */ true, /* isActive= */ true);
+  ASSERT_THAT(info_list->getApexInfo(),
+              UnorderedElementsAre(ApexInfoXmlEq(apex_info_xml_1)));
 }
 
 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithDuplicateNames) {
@@ -2671,5 +2694,20 @@
   ASSERT_EQ(1, OnStartInVmMode());
 }
 
+TEST_F(ApexdMountTest, GetActivePackagesReturningBlockApexesAsWell) {
+  MockCheckpointInterface checkpoint_interface;
+  // Need to call InitializeVold before calling OnStart
+  InitializeVold(&checkpoint_interface);
+
+  auto path1 = AddBlockApex("apex.apexd_test.apex");
+
+  ASSERT_EQ(0, OnStartInVmMode());
+  UnmountOnTearDown(path1);
+
+  auto active_apexes = GetActivePackages();
+  ASSERT_EQ(1u, active_apexes.size());
+  ASSERT_EQ(path1, active_apexes[0].GetPath());
+}
+
 }  // namespace apex
 }  // namespace android