diff options
author | Nikita Ioffe <ioffe@google.com> | 2022-05-15 21:29:40 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2022-05-15 21:29:40 +0000 |
commit | a4731bde30d8554a98ab3f1c4b52f6715e0997e2 (patch) | |
tree | 4bf7f910bba12d12f4f8fa63e611701b3a9f6512 | |
parent | 3c9ace65e2dfe701b5f5037e8a4929cca1460d2e (diff) | |
parent | 1805ea3c1359e83b159e48ff443d47543ba44009 (diff) | |
download | apex-a4731bde30d8554a98ab3f1c4b52f6715e0997e2.tar.gz |
Merge "Mark APEXes that changed version during this boot" into tm-dev
-rw-r--r-- | apexd/aidl/android/apex/ApexInfo.aidl | 11 | ||||
-rw-r--r-- | apexd/apexd.cpp | 33 | ||||
-rw-r--r-- | apexd/apexd.h | 5 | ||||
-rw-r--r-- | apexd/apexd_test.cpp | 161 | ||||
-rw-r--r-- | apexd/apexservice.cpp | 1 |
5 files changed, 211 insertions, 0 deletions
diff --git a/apexd/aidl/android/apex/ApexInfo.aidl b/apexd/aidl/android/apex/ApexInfo.aidl index 47a78d0d..fb590f2a 100644 --- a/apexd/aidl/android/apex/ApexInfo.aidl +++ b/apexd/aidl/android/apex/ApexInfo.aidl @@ -27,4 +27,15 @@ parcelable ApexInfo { // Populated only for getStagedApex() API boolean hasClassPathJars; + + // Will be set to true if during this boot a different APEX package of the APEX was + // activated, than in the previous boot. + // This can happen in the following situations: + // 1. It was part of the staged session that was applied during this boot. + // 2. A compressed system APEX was decompressed during this boot. + // 3. apexd failed to activate an APEX on /data/apex/active (that was successfully + // activated during last boot) and needed to fallback to pre-installed counterpart. + // Note: this field can only be set to true during boot, after boot is completed + // (sys.boot_completed = 1) value of this field will always be false. + boolean activeApexChanged; } diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp index 055c641e..ff60ed72 100644 --- a/apexd/apexd.cpp +++ b/apexd/apexd.cpp @@ -128,6 +128,15 @@ CheckpointInterface* gVoldService; bool gSupportsFsCheckpoints = false; bool gInFsCheckpointMode = false; +// APEXEs for which a different version was activated than in the previous boot. +// This can happen in the following scenarios: +// 1. This APEX is part of the staged session that was applied during this +// boot. +// 2. This is a compressed APEX that was decompressed during this boot. +// 3. We failed to activate APEX from /data/apex/active and fallback to the +// pre-installed APEX. +std::set<std::string> gChangedActiveApexes; + static constexpr size_t kLoopDeviceSetupAttempts = 3u; // Please DO NOT add new modules to this list without contacting mainline-modularization@ first. @@ -1864,6 +1873,14 @@ Result<void> ActivateMissingApexes(const std::vector<ApexFileRef>& apexes, fallback_apexes.emplace_back(std::cref(apex_file)); } } + if (mode == kBootMode) { + // Treat fallback to pre-installed APEXes as a change of the acitve APEX, + // since we are already in a pretty dire situation, so it's better if we + // drop all the caches. + for (const auto& apex : fallback_apexes) { + gChangedActiveApexes.insert(apex.get().GetManifest().name()); + } + } return ActivateApexPackages(fallback_apexes, mode); } @@ -2214,6 +2231,7 @@ void ScanStagedSessionsDirAndStage() { continue; } + std::vector<std::string> staged_apex_names; for (const auto& apex : apexes) { // TODO(b/158470836): Avoid opening ApexFile repeatedly. Result<ApexFile> apex_file = ApexFile::Open(apex); @@ -2221,6 +2239,7 @@ void ScanStagedSessionsDirAndStage() { LOG(ERROR) << "Cannot open apex file during staging: " << apex; continue; } + staged_apex_names.push_back(apex_file->GetManifest().name()); } const Result<void> result = StagePackages(apexes); @@ -2236,6 +2255,10 @@ void ScanStagedSessionsDirAndStage() { // Session was OK, release scopeguard. scope_guard.Disable(); + for (const std::string& apex : staged_apex_names) { + gChangedActiveApexes.insert(apex); + } + auto st = session.UpdateStateAndCommit(SessionState::ACTIVATED); if (!st.ok()) { LOG(ERROR) << "Failed to mark " << session @@ -2860,6 +2883,7 @@ Result<ApexFile> ProcessCompressedApex(const ApexFile& capex, return Error() << "Failed to decompress CAPEX: " << return_apex.error(); } + gChangedActiveApexes.insert(return_apex->GetManifest().name()); /// Release compressed blocks in case decompression_dest is on f2fs-compressed // filesystem. ReleaseF2fsCompressedBlocks(decompression_dest); @@ -4013,5 +4037,14 @@ Result<ApexFile> InstallPackage(const std::string& package_path) { return new_apex; } +bool IsActiveApexChanged(const ApexFile& apex) { + return gChangedActiveApexes.find(apex.GetManifest().name()) != + gChangedActiveApexes.end(); +} + +std::set<std::string>& GetChangedActiveApexesForTesting() { + return gChangedActiveApexes; +} + } // namespace apex } // namespace android diff --git a/apexd/apexd.h b/apexd/apexd.h index cdcc193a..c30df4c3 100644 --- a/apexd/apexd.h +++ b/apexd/apexd.h @@ -227,6 +227,11 @@ android::base::Result<ApexFile> InstallPackage(const std::string& package_path); // Exposed for testing. android::base::Result<int> AddBlockApex(ApexFileRepository& instance); +bool IsActiveApexChanged(const ApexFile& apex); + +// Shouldn't be used outside of apexd_test.cpp +std::set<std::string>& GetChangedActiveApexesForTesting(); + } // namespace apex } // namespace android diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp index 6e248a58..cc6d0721 100644 --- a/apexd/apexd_test.cpp +++ b/apexd/apexd_test.cpp @@ -36,6 +36,7 @@ #include <vector> #include "apex_database.h" +#include "apex_file.h" #include "apex_file_repository.h" #include "apex_manifest.pb.h" #include "apexd_checkpoint.h" @@ -196,6 +197,18 @@ class ApexdUnitTest : public ::testing::Test { return StringPrintf("%s/%s", data_dir_.c_str(), target_name.c_str()); } + std::string AddDecompressedApex(const std::string& apex_name) { + auto apex_file = ApexFile::Open(GetTestFile(apex_name)); + CHECK(apex_file.ok()); + std::string target_name = + apex_file->GetManifest().name() + "@" + + std::to_string(apex_file->GetManifest().version()) + + std::string(kDecompressedApexPackageSuffix); + fs::copy(GetTestFile(apex_name), decompression_dir_ + "/" + target_name); + return StringPrintf("%s/%s", decompression_dir_.c_str(), + target_name.c_str()); + } + std::string AddBlockApex(const std::string& apex_name, const std::string& public_key = "", const std::string& root_digest = "") { @@ -905,6 +918,7 @@ class ApexdMountTest : public ApexdUnitTest { void SetUp() final { ApexdUnitTest::SetUp(); GetApexDatabaseForTesting().Reset(); + GetChangedActiveApexesForTesting().clear(); ASSERT_THAT(SetUpApexTestEnvironment(), Ok()); } @@ -4698,5 +4712,152 @@ TEST_F(ApexdUnitTest, ProcessCompressedApexWrongSELinuxContext) { GetSelinuxContext(decompressed_apex_path)); } +TEST_F(ApexdMountTest, OnStartNoApexUpdated) { + MockCheckpointInterface checkpoint_interface; + // Need to call InitializeVold before calling OnStart + InitializeVold(&checkpoint_interface); + + AddPreInstalledApex("com.android.apex.compressed.v1.capex"); + std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); + std::string apex_path_2 = + AddPreInstalledApex("apex.apexd_test_different_app.apex"); + std::string apex_path_3 = AddDataApex("apex.apexd_test_v2.apex"); + std::string apex_path_4 = + AddDecompressedApex("com.android.apex.compressed.v1_original.apex"); + + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + + OnStart(); + + UnmountOnTearDown(apex_path_2); + UnmountOnTearDown(apex_path_3); + UnmountOnTearDown(apex_path_4); + + auto updated_apexes = GetChangedActiveApexesForTesting(); + ASSERT_EQ(updated_apexes.size(), 0u); + // Quick check that all apexes were mounted + auto apex_mounts = GetApexMounts(); + ASSERT_EQ(apex_mounts.size(), 6u); +} + +TEST_F(ApexdMountTest, OnStartDecompressingConsideredApexUpdate) { + MockCheckpointInterface checkpoint_interface; + // Need to call InitializeVold before calling OnStart + InitializeVold(&checkpoint_interface); + + AddPreInstalledApex("com.android.apex.compressed.v1.capex"); + std::string apex_path_1 = AddPreInstalledApex("apex.apexd_test.apex"); + std::string decompressed_active_apex = StringPrintf( + "%s/com.android.apex.compressed@1%s", GetDecompressionDir().c_str(), + kDecompressedApexPackageSuffix); + UnmountOnTearDown(decompressed_active_apex); + + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + + OnStart(); + + UnmountOnTearDown(apex_path_1); + UnmountOnTearDown(decompressed_active_apex); + + auto updated_apexes = GetChangedActiveApexesForTesting(); + ASSERT_EQ(updated_apexes.size(), 1u); + auto apex_file = ApexFile::Open(decompressed_active_apex); + ASSERT_THAT(apex_file, Ok()); + ASSERT_TRUE(IsActiveApexChanged(*apex_file)); +} + +TEST_F(ApexdMountTest, ActivatesStagedSession) { + MockCheckpointInterface checkpoint_interface; + // Need to call InitializeVold before calling OnStart + InitializeVold(&checkpoint_interface); + + std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex"); + auto apex_session = CreateStagedSession("apex.apexd_test_v2.apex", 37); + apex_session->UpdateStateAndCommit(SessionState::STAGED); + + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + + std::string active_apex = + GetDataDir() + "/" + "com.android.apex.test_package@2.apex"; + + UnmountOnTearDown(preinstalled_apex); + UnmountOnTearDown(active_apex); + OnStart(); + + // Quick check that session was activated + { + auto session = ApexSession::GetSession(37); + ASSERT_THAT(session, Ok()); + ASSERT_EQ(session->GetState(), SessionState::ACTIVATED); + } + + auto updated_apexes = GetChangedActiveApexesForTesting(); + ASSERT_EQ(updated_apexes.size(), 1u); + auto apex_file = ApexFile::Open(active_apex); + ASSERT_THAT(apex_file, Ok()); + ASSERT_TRUE(IsActiveApexChanged(*apex_file)); +} + +TEST_F(ApexdMountTest, FailsToActivateStagedSession) { + MockCheckpointInterface checkpoint_interface; + // Need to call InitializeVold before calling OnStart + InitializeVold(&checkpoint_interface); + + std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex"); + auto apex_session = + CreateStagedSession("apex.apexd_test_manifest_mismatch.apex", 73); + apex_session->UpdateStateAndCommit(SessionState::STAGED); + + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + + UnmountOnTearDown(preinstalled_apex); + OnStart(); + + // Quick check that session was activated + { + auto session = ApexSession::GetSession(73); + ASSERT_THAT(session, Ok()); + ASSERT_NE(session->GetState(), SessionState::ACTIVATED); + } + + auto updated_apexes = GetChangedActiveApexesForTesting(); + ASSERT_EQ(updated_apexes.size(), 1u); + + auto apex_file = ApexFile::Open(preinstalled_apex); + ASSERT_THAT(apex_file, Ok()); + ASSERT_TRUE(IsActiveApexChanged(*apex_file)); +} + +TEST_F(ApexdMountTest, FailsToActivateApexFallbacksToSystemOne) { + MockCheckpointInterface checkpoint_interface; + // Need to call InitializeVold before calling OnStart + InitializeVold(&checkpoint_interface); + + std::string preinstalled_apex = AddPreInstalledApex("apex.apexd_test.apex"); + AddDataApex("apex.apexd_test_manifest_mismatch.apex"); + + ASSERT_THAT( + ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()}), + Ok()); + + UnmountOnTearDown(preinstalled_apex); + OnStart(); + + auto updated_apexes = GetChangedActiveApexesForTesting(); + ASSERT_EQ(updated_apexes.size(), 1u); + + auto apex_file = ApexFile::Open(preinstalled_apex); + ASSERT_THAT(apex_file, Ok()); + ASSERT_TRUE(IsActiveApexChanged(*apex_file)); +} + } // namespace apex } // namespace android diff --git a/apexd/apexservice.cpp b/apexd/apexservice.cpp index dfe946c0..801f6344 100644 --- a/apexd/apexservice.cpp +++ b/apexd/apexservice.cpp @@ -371,6 +371,7 @@ static ApexInfo GetApexInfo(const ApexFile& package) { ? sys_apex_path : *preinstalled_path; } + out.activeApexChanged = ::android::apex::IsActiveApexChanged(package); return out; } |