Merge tag 'android-13.0.0_r32' into int/13/fp3

Android 13.0.0 release 32

* tag 'android-13.0.0_r32':
  Use block apex path as key for rootdigest overrides
  AddBlockApex checks uniqueness for "factory" apexes
  Don't configure queue depth & IO scheduler for auto products

Change-Id: Ia0b8a2c934eb744cfcf3314d2cf097024513f495
diff --git a/apexd/Android.bp b/apexd/Android.bp
index ce5d170..bf4bce3 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -73,8 +73,19 @@
   ],
 }
 
+soong_config_module_type {
+  name: "apexd_cc_defaults",
+  module_type: "cc_defaults",
+  config_namespace: "ANDROID",
+  bool_variables: [
+    "target_board_auto",
+  ],
+  properties: [
+    "cppflags",
+  ],
+}
 
-cc_defaults {
+apexd_cc_defaults {
   name: "libapexd-deps",
   defaults: ["libapex-deps"],
   shared_libs: [
@@ -93,6 +104,11 @@
     "libxml2",
   ],
   whole_static_libs: ["com.android.sysprop.apex"],
+  soong_config_variables: {
+    target_board_auto: {
+      cppflags: ["-DDISABLE_LOOP_IO_CONFIG"],
+    },
+  },
 }
 
 aidl_interface {
diff --git a/apexd/apex_file_repository.cpp b/apexd/apex_file_repository.cpp
index e82feb4..834eef5 100644
--- a/apexd/apex_file_repository.cpp
+++ b/apexd/apex_file_repository.cpp
@@ -279,20 +279,17 @@
 
     if (overrides.last_update_seconds.has_value() ||
         overrides.block_apex_root_digest.has_value()) {
-      block_apex_overrides_.emplace(name, std::move(overrides));
+      block_apex_overrides_.emplace(apex_path, std::move(overrides));
     }
 
-    // APEX should be unique.
-    for (const auto* store : {&pre_installed_store_, &data_store_}) {
-      auto it = store->find(name);
-      if (it != store->end()) {
-        return Error() << "duplicate of " << name << " found in "
-                       << it->second.GetPath();
-      }
-    }
     // Depending on whether the APEX was a factory version in the host or not,
     // put it to different stores.
     auto& store = apex_config.is_factory() ? pre_installed_store_ : data_store_;
+    // We want "uniqueness" in each store.
+    if (auto it = store.find(name); it != store.end()) {
+      return Error() << "duplicate of " << name << " found in "
+                     << it->second.GetPath();
+    }
     store.emplace(name, std::move(*apex_file));
 
     ret++;
@@ -415,8 +412,8 @@
 }
 
 std::optional<std::string> ApexFileRepository::GetBlockApexRootDigest(
-    const std::string& name) const {
-  auto it = block_apex_overrides_.find(name);
+    const std::string& path) const {
+  auto it = block_apex_overrides_.find(path);
   if (it == block_apex_overrides_.end()) {
     return std::nullopt;
   }
@@ -424,8 +421,8 @@
 }
 
 std::optional<int64_t> ApexFileRepository::GetBlockApexLastUpdateSeconds(
-    const std::string& name) const {
-  auto it = block_apex_overrides_.find(name);
+    const std::string& path) const {
+  auto it = block_apex_overrides_.find(path);
   if (it == block_apex_overrides_.end()) {
     return std::nullopt;
   }
diff --git a/apexd/apex_file_repository.h b/apexd/apex_file_repository.h
index 1a1cf94..f90dcbc 100644
--- a/apexd/apex_file_repository.h
+++ b/apexd/apex_file_repository.h
@@ -104,13 +104,13 @@
   android::base::Result<const std::string> GetDataPath(
       const std::string& name) const;
 
-  // Returns root digest of an apex with the given |name| for block apexes.
+  // Returns root digest of an apex with the given |path| for block apexes.
   std::optional<std::string> GetBlockApexRootDigest(
-      const std::string& name) const;
+      const std::string& path) const;
 
-  // Returns timestamp to be used for the block apex of the given |name|.
+  // Returns timestamp to be used for the block apex of the given |path|.
   std::optional<int64_t> GetBlockApexLastUpdateSeconds(
-      const std::string& name) const;
+      const std::string& path) const;
 
   // Checks whether there is a pre-installed version of an apex with the given
   // |name|.
@@ -203,6 +203,8 @@
     std::optional<int64_t> last_update_seconds;
   };
 
+  // Use "path" as key instead of APEX name because there can be multiple
+  // versions of sharedlibs APEXes.
   std::unordered_map<std::string, BlockApexOverride> block_apex_overrides_;
 };
 
diff --git a/apexd/apex_file_repository_test.cpp b/apexd/apex_file_repository_test.cpp
index bf73092..272979f 100644
--- a/apexd/apex_file_repository_test.cpp
+++ b/apexd/apex_file_repository_test.cpp
@@ -740,8 +740,7 @@
   auto status = instance.AddBlockApex(metadata_partition_path);
   ASSERT_TRUE(IsOk(status));
 
-  ASSERT_EQ(hex_root_digest,
-            instance.GetBlockApexRootDigest("com.android.apex.test_package"));
+  ASSERT_EQ(hex_root_digest, instance.GetBlockApexRootDigest(apex_foo_path));
 }
 
 TEST_F(ApexFileRepositoryTestAddBlockApex, GetBlockApexLastUpdateSeconds) {
@@ -767,8 +766,8 @@
   auto status = instance.AddBlockApex(metadata_partition_path);
   ASSERT_TRUE(IsOk(status));
 
-  ASSERT_EQ(last_update_seconds, instance.GetBlockApexLastUpdateSeconds(
-                                     "com.android.apex.test_package"));
+  ASSERT_EQ(last_update_seconds,
+            instance.GetBlockApexLastUpdateSeconds(apex_foo_path));
 }
 
 TEST_F(ApexFileRepositoryTestAddBlockApex, VerifyPublicKeyWhenAddingBlockApex) {
diff --git a/apexd/apexd.cpp b/apexd/apexd.cpp
index b777f1f..5cf19ad 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -538,8 +538,7 @@
                    << ": " << verity_data.error();
   }
   if (instance.IsBlockApex(apex)) {
-    auto root_digest =
-        instance.GetBlockApexRootDigest(apex.GetManifest().name());
+    auto root_digest = instance.GetBlockApexRootDigest(apex.GetPath());
     if (root_digest.has_value() &&
         root_digest.value() != verity_data->root_digest) {
       return Error() << "Failed to verify Apex Verity data for " << full_path
@@ -3438,7 +3437,7 @@
     }
 
     std::optional<int64_t> mtime =
-        instance.GetBlockApexLastUpdateSeconds(apex.GetManifest().name());
+        instance.GetBlockApexLastUpdateSeconds(apex.GetPath());
     if (!mtime.has_value()) {
       struct stat stat_buf;
       if (stat(apex.GetPath().c_str(), &stat_buf) == 0) {
diff --git a/apexd/apexd_loop.cpp b/apexd/apexd_loop.cpp
index 36521b0..2f8d8f2 100644
--- a/apexd/apexd_loop.cpp
+++ b/apexd/apexd_loop.cpp
@@ -158,7 +158,7 @@
 // /dev/block/dm-9 (system-verity; dm-verity)
 // -> /dev/block/dm-1 (system_b; dm-linear)
 // -> /dev/sda26
-static Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
+Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {
   struct stat statbuf;
   int res = stat(file_path.c_str(), &statbuf);
   if (res < 0) {
@@ -511,6 +511,9 @@
     return loop_device.error();
   }
 
+  // We skip confiruing scheduler and queue depth for automotive products.
+  // See: b/241473698.
+#ifndef DISABLE_LOOP_IO_CONFIG
   Result<void> sched_status = ConfigureScheduler(loop_device->name);
   if (!sched_status.ok()) {
     LOG(WARNING) << "Configuring I/O scheduler failed: "
@@ -521,6 +524,7 @@
   if (!qd_status.ok()) {
     LOG(WARNING) << qd_status.error();
   }
+#endif
 
   Result<void> read_ahead_status = ConfigureReadAhead(loop_device->name);
   if (!read_ahead_status.ok()) {
diff --git a/apexd/apexd_loop.h b/apexd/apexd_loop.h
index 3c356d9..330eff3 100644
--- a/apexd/apexd_loop.h
+++ b/apexd/apexd_loop.h
@@ -55,6 +55,10 @@
   int Get() { return device_fd.get(); }
 };
 
+// Exposed only for testing
+android::base::Result<uint32_t> BlockDeviceQueueDepth(
+    const std::string& file_path);
+
 android::base::Result<LoopbackDeviceUniqueFd> WaitForDevice(int num);
 
 android::base::Result<void> ConfigureQueueDepth(
diff --git a/apexd/apexd_test.cpp b/apexd/apexd_test.cpp
index cc6d072..732e23f 100644
--- a/apexd/apexd_test.cpp
+++ b/apexd/apexd_test.cpp
@@ -17,6 +17,7 @@
 #include "apexd.h"
 
 #include <android-base/file.h>
+#include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/result-gmock.h>
 #include <android-base/scopeguard.h>
@@ -54,10 +55,12 @@
 
 using MountedApexData = MountedApexDatabase::MountedApexData;
 using android::apex::testing::ApexFileEq;
+using android::base::Basename;
 using android::base::GetExecutableDirectory;
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::make_scope_guard;
+using android::base::ParseUint;
 using android::base::ReadFileToString;
 using android::base::ReadFully;
 using android::base::RemoveFileIfExists;
@@ -211,10 +214,11 @@
 
   std::string AddBlockApex(const std::string& apex_name,
                            const std::string& public_key = "",
-                           const std::string& root_digest = "") {
-    auto apex_path = vm_payload_disk_ + "2";  // second partition
+                           const std::string& root_digest = "",
+                           bool is_factory = true) {
+    auto apex_path = vm_payload_disk_ + std::to_string(block_device_index_++);
     auto apex_file = GetTestFile(apex_name);
-    WriteMetadata(apex_file, public_key, root_digest);
+    AddToMetadata(apex_name, public_key, root_digest, is_factory);
     // loop_devices_ will be disposed after each test
     loop_devices_.push_back(*WriteBlockApex(apex_file, apex_path));
     return apex_path;
@@ -267,24 +271,23 @@
 
     DeleteDirContent(ApexSession::GetSessionsDir());
   }
-  void WriteMetadata(const std::string& apex_file,
+  void AddToMetadata(const std::string& apex_name,
                      const std::string& public_key,
-                     const std::string& root_digest) {
+                     const std::string& root_digest, bool is_factory) {
     android::microdroid::Metadata metadata;
-
-    auto apex = metadata.add_apexes();
-    apex->set_name("apex");
-    apex->set_public_key(public_key);
-    apex->set_root_digest(root_digest);
-    // In this test, block apeses are assumed as "factory".
-    // ApexFileRepositoryTestAddBlockApex tests non-factory cases.
-    apex->set_is_factory(true);
-
     // The first partition is metadata partition
     auto metadata_partition = vm_payload_disk_ + "1";
-    LOG(INFO) << "Writing metadata to " << metadata_partition;
-    std::ofstream out(metadata_partition);
+    if (access(metadata_partition.c_str(), F_OK) == 0) {
+      metadata = *android::microdroid::ReadMetadata(metadata_partition);
+    }
 
+    auto apex = metadata.add_apexes();
+    apex->set_name(apex_name);
+    apex->set_public_key(public_key);
+    apex->set_root_digest(root_digest);
+    apex->set_is_factory(is_factory);
+
+    std::ofstream out(metadata_partition);
     android::microdroid::WriteMetadata(metadata, out);
   }
 
@@ -306,6 +309,7 @@
   std::string metadata_sepolicy_staged_dir_;
   ApexdConfig config_;
   std::vector<loop::LoopbackDeviceUniqueFd> loop_devices_;  // to be cleaned up
+  int block_device_index_ = 2;  // "1" is reserved for metadata;
 };
 
 // Apex that does not have pre-installed version, does not get selected
@@ -4058,6 +4062,57 @@
   ASSERT_EQ(1, OnStartInVmMode());
 }
 
+TEST_F(ApexdMountTest, OnStartInVmSupportsMultipleSharedLibsApexes) {
+  MockCheckpointInterface checkpoint_interface;
+  InitializeVold(&checkpoint_interface);
+  SetBlockApexEnabled(true);
+
+  auto path1 =
+      AddBlockApex("apex.apexd_test.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
+  auto path2 =
+      AddBlockApex("apex.apexd_test_v2.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
+
+  ASSERT_EQ(0, OnStartInVmMode());
+  UnmountOnTearDown(path1);
+  UnmountOnTearDown(path2);
+}
+
+TEST_F(ApexdMountTest, OnStartInVmShouldRejectInDuplicateFactoryApexes) {
+  MockCheckpointInterface checkpoint_interface;
+  InitializeVold(&checkpoint_interface);
+  SetBlockApexEnabled(true);
+
+  auto path1 =
+      AddBlockApex("apex.apexd_test.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
+  auto path2 =
+      AddBlockApex("apex.apexd_test_v2.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/true);
+
+  ASSERT_EQ(1, OnStartInVmMode());
+  UnmountOnTearDown(path1);
+  UnmountOnTearDown(path2);
+}
+
+TEST_F(ApexdMountTest, OnStartInVmShouldRejectInDuplicateNonFactoryApexes) {
+  MockCheckpointInterface checkpoint_interface;
+  InitializeVold(&checkpoint_interface);
+  SetBlockApexEnabled(true);
+
+  auto path1 =
+      AddBlockApex("apex.apexd_test.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
+  auto path2 =
+      AddBlockApex("apex.apexd_test_v2.apex",
+                   /*public_key=*/"", /*root_digest=*/"", /*is_factory=*/false);
+
+  ASSERT_EQ(1, OnStartInVmMode());
+  UnmountOnTearDown(path1);
+  UnmountOnTearDown(path2);
+}
+
 TEST_F(ApexdMountTest, OnStartInVmModeFailsWithWrongPubkey) {
   MockCheckpointInterface checkpoint_interface;
   // Need to call InitializeVold before calling OnStart
@@ -4859,5 +4914,36 @@
   ASSERT_TRUE(IsActiveApexChanged(*apex_file));
 }
 
+TEST_F(ApexdMountTest, LoopIoConfig) {
+  std::string file_path = AddPreInstalledApex("apex.apexd_test_nocode.apex");
+  ApexFileRepository::GetInstance().AddPreInstalledApex({GetBuiltInDir()});
+
+  ASSERT_THAT(ActivatePackage(file_path), Ok());
+  UnmountOnTearDown(file_path);
+
+  std::optional<std::string> loop_device;
+  auto& db = GetApexDatabaseForTesting();
+  // Check that upgraded APEX is mounted on top of dm-verity device.
+  db.ForallMountedApexes("com.android.apex.test_package",
+                         [&](const MountedApexData& data, bool /*latest*/) {
+                           loop_device.emplace(data.loop_name);
+                         });
+
+  ASSERT_TRUE(loop_device.has_value());
+  const std::string sysfs_path = StringPrintf("/sys/block/%s/queue/nr_requests",
+                                              (Basename(*loop_device)).c_str());
+  std::string actual_str;
+  ASSERT_TRUE(ReadFileToString(sysfs_path, &actual_str))
+      << "Failed to read " << sysfs_path;
+  actual_str = android::base::Trim(actual_str);
+  uint32_t actual = 0;
+  ASSERT_TRUE(ParseUint(actual_str.c_str(), &actual))
+      << "Failed to parse " << actual_str;
+
+  auto expected = loop::BlockDeviceQueueDepth("/data");
+  ASSERT_THAT(expected, Ok());
+  ASSERT_EQ(*expected, actual);
+}
+
 }  // namespace apex
 }  // namespace android