Merge "Update the allow list inside apex_shim.cpp"
diff --git a/apexd/aidl/android/apex/ApexInfo.aidl b/apexd/aidl/android/apex/ApexInfo.aidl
index 47a78d0..fb590f2 100644
--- a/apexd/aidl/android/apex/ApexInfo.aidl
+++ b/apexd/aidl/android/apex/ApexInfo.aidl
@@ -27,4 +27,15 @@
 
     // 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 0f0c729..baad6cc 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -129,6 +129,15 @@
 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.
@@ -1856,6 +1865,14 @@
       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);
 }
 
@@ -2206,6 +2223,7 @@
       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);
@@ -2213,6 +2231,7 @@
         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);
@@ -2228,6 +2247,10 @@
     // 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
@@ -2852,6 +2875,7 @@
     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);
@@ -4005,5 +4029,14 @@
   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 cdcc193..c30df4c 100644
--- a/apexd/apexd.h
+++ b/apexd/apexd.h
@@ -227,6 +227,11 @@
 // 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 6e248a5..cc6d072 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 @@
     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 @@
   void SetUp() final {
     ApexdUnitTest::SetUp();
     GetApexDatabaseForTesting().Reset();
+    GetChangedActiveApexesForTesting().clear();
     ASSERT_THAT(SetUpApexTestEnvironment(), Ok());
   }
 
@@ -4698,5 +4712,152 @@
             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 dfe946c..801f634 100644
--- a/apexd/apexservice.cpp
+++ b/apexd/apexservice.cpp
@@ -371,6 +371,7 @@
                                      ? sys_apex_path
                                      : *preinstalled_path;
   }
+  out.activeApexChanged = ::android::apex::IsActiveApexChanged(package);
   return out;
 }