summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Ioffe <ioffe@google.com>2022-05-15 21:29:40 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2022-05-15 21:29:40 +0000
commita4731bde30d8554a98ab3f1c4b52f6715e0997e2 (patch)
tree4bf7f910bba12d12f4f8fa63e611701b3a9f6512
parent3c9ace65e2dfe701b5f5037e8a4929cca1460d2e (diff)
parent1805ea3c1359e83b159e48ff443d47543ba44009 (diff)
downloadapex-a4731bde30d8554a98ab3f1c4b52f6715e0997e2.tar.gz
Merge "Mark APEXes that changed version during this boot" into tm-dev
-rw-r--r--apexd/aidl/android/apex/ApexInfo.aidl11
-rw-r--r--apexd/apexd.cpp33
-rw-r--r--apexd/apexd.h5
-rw-r--r--apexd/apexd_test.cpp161
-rw-r--r--apexd/apexservice.cpp1
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;
}