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