summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-01-10 00:27:28 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-01-10 00:27:28 +0000
commit21056d928eee3f91db5c019e47cc35850fa7a892 (patch)
treebdbf69cf21cd64d60215785023da955942ba3a74
parent24ef68efb15ccb4cc0ddc86de2f3bb4ff37011d2 (diff)
parentd2c816eb179071a39247c6fdcf3e4de278f9231b (diff)
downloadapex-android13-qpr3-s4-release.tar.gz
Change-Id: Ic633d825bf4a07c9ae849e72bbaeb8fac359e6de
-rw-r--r--apexd/Android.bp18
-rw-r--r--apexd/apex_file_repository.cpp23
-rw-r--r--apexd/apex_file_repository.h10
-rw-r--r--apexd/apex_file_repository_test.cpp7
-rw-r--r--apexd/apexd.cpp14
-rw-r--r--apexd/apexd_loop.cpp6
-rw-r--r--apexd/apexd_loop.h4
-rw-r--r--apexd/apexd_test.cpp112
-rw-r--r--shim/build/Android.bp36
-rw-r--r--shim/build/AndroidManifestInstallConstraints_empty.xml8
-rw-r--r--shim/build/AndroidManifestInstallConstraints_invalid_fingerprint.xml9
-rw-r--r--shim/build/AndroidManifestInstallConstraints_no_value.xml15
12 files changed, 223 insertions, 39 deletions
diff --git a/apexd/Android.bp b/apexd/Android.bp
index ce5d170b..bf4bce34 100644
--- a/apexd/Android.bp
+++ b/apexd/Android.bp
@@ -73,8 +73,19 @@ cc_defaults {
],
}
+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 @@ cc_defaults {
"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 e82feb4c..834eef53 100644
--- a/apexd/apex_file_repository.cpp
+++ b/apexd/apex_file_repository.cpp
@@ -279,20 +279,17 @@ Result<int> ApexFileRepository::AddBlockApex(
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 @@ Result<const std::string> ApexFileRepository::GetDataPath(
}
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<std::string> ApexFileRepository::GetBlockApexRootDigest(
}
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 1a1cf946..f90dcbc7 100644
--- a/apexd/apex_file_repository.h
+++ b/apexd/apex_file_repository.h
@@ -104,13 +104,13 @@ class ApexFileRepository final {
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 @@ class ApexFileRepository final {
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 bf730923..272979f4 100644
--- a/apexd/apex_file_repository_test.cpp
+++ b/apexd/apex_file_repository_test.cpp
@@ -740,8 +740,7 @@ TEST_F(ApexFileRepositoryTestAddBlockApex, GetBlockApexRootDigest) {
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 @@ TEST_F(ApexFileRepositoryTestAddBlockApex, GetBlockApexLastUpdateSeconds) {
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 ff60ed72..5cf19ad7 100644
--- a/apexd/apexd.cpp
+++ b/apexd/apexd.cpp
@@ -538,8 +538,7 @@ Result<MountedApexData> MountPackageImpl(const ApexFile& apex,
<< ": " << 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
@@ -1232,6 +1231,15 @@ Result<void> ResumeRevertIfNeeded() {
}
Result<void> ActivateSharedLibsPackage(const std::string& mount_point) {
+ // Having static mutex here is not great, but since this function is called
+ // only twice during boot we can probably live with that. In U+ we will have
+ // a proper solution implemented.
+ static std::mutex mtx;
+ // ActivateSharedLibsPackage can be called concurrently from multiple threads.
+ // Since this function mutates the shared state in /apex/sharedlibs hold the
+ // mutex to avoid potential race conditions.
+ std::lock_guard guard(mtx);
+
for (const auto& lib_path : {"lib", "lib64"}) {
std::string apex_lib_path = mount_point + "/" + lib_path;
auto lib_dir = PathExists(apex_lib_path);
@@ -3429,7 +3437,7 @@ void CollectApexInfoList(std::ostream& os,
}
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 36521b05..2f8d8f28 100644
--- a/apexd/apexd_loop.cpp
+++ b/apexd/apexd_loop.cpp
@@ -158,7 +158,7 @@ static std::string BlockdevName(dev_t dev) {
// /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 @@ Result<LoopbackDeviceUniqueFd> CreateAndConfigureLoopDevice(
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 @@ Result<LoopbackDeviceUniqueFd> CreateAndConfigureLoopDevice(
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 3c356d94..330eff3c 100644
--- a/apexd/apexd_loop.h
+++ b/apexd/apexd_loop.h
@@ -55,6 +55,10 @@ struct LoopbackDeviceUniqueFd {
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 cc6d0721..732e23fb 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 @@ namespace fs = std::filesystem;
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 @@ class ApexdUnitTest : public ::testing::Test {
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 @@ class ApexdUnitTest : public ::testing::Test {
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;
+ // The first partition is metadata partition
+ auto metadata_partition = vm_payload_disk_ + "1";
+ if (access(metadata_partition.c_str(), F_OK) == 0) {
+ metadata = *android::microdroid::ReadMetadata(metadata_partition);
+ }
auto apex = metadata.add_apexes();
- apex->set_name("apex");
+ apex->set_name(apex_name);
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);
+ apex->set_is_factory(is_factory);
- // 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);
-
android::microdroid::WriteMetadata(metadata, out);
}
@@ -306,6 +309,7 @@ class ApexdUnitTest : public ::testing::Test {
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 @@ TEST_F(ApexdMountTest, OnStartInVmModeFailsWithDuplicateNames) {
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 @@ TEST_F(ApexdMountTest, FailsToActivateApexFallbacksToSystemOne) {
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
diff --git a/shim/build/Android.bp b/shim/build/Android.bp
index 99121d21..801899e4 100644
--- a/shim/build/Android.bp
+++ b/shim/build/Android.bp
@@ -267,6 +267,9 @@ genrule {
":com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p",
":com.android.apex.cts.shim.v2_apk_in_apex_upgrades",
":com.android.apex.cts.shim.v2_rebootless",
+ ":com.android.apex.cts.shim.v2_install_constraints_empty",
+ ":com.android.apex.cts.shim.v2_install_constraints_invalid_fingerprint",
+ ":com.android.apex.cts.shim.v2_install_constraints_no_value",
":com.android.apex.cts.shim.v3",
":com.android.apex.cts.shim.v3_rebootless",
":com.android.apex.cts.shim.v3_signed_bob",
@@ -596,3 +599,36 @@ apex {
installable: false,
updatable: false,
}
+
+apex {
+ name: "com.android.apex.cts.shim.v2_install_constraints_empty",
+ manifest: "manifest_v2.json",
+ androidManifest: "AndroidManifestInstallConstraints_empty.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.cts.shim.key",
+ prebuilts: ["hash_of_dev_null"],
+ installable: false,
+ updatable: false,
+}
+
+apex {
+ name: "com.android.apex.cts.shim.v2_install_constraints_invalid_fingerprint",
+ manifest: "manifest_v2.json",
+ androidManifest: "AndroidManifestInstallConstraints_invalid_fingerprint.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.cts.shim.key",
+ prebuilts: ["hash_of_dev_null"],
+ installable: false,
+ updatable: false,
+}
+
+apex {
+ name: "com.android.apex.cts.shim.v2_install_constraints_no_value",
+ manifest: "manifest_v2.json",
+ androidManifest: "AndroidManifestInstallConstraints_no_value.xml",
+ file_contexts: ":apex.test-file_contexts",
+ key: "com.android.apex.cts.shim.key",
+ prebuilts: ["hash_of_dev_null"],
+ installable: false,
+ updatable: false,
+}
diff --git a/shim/build/AndroidManifestInstallConstraints_empty.xml b/shim/build/AndroidManifestInstallConstraints_empty.xml
new file mode 100644
index 00000000..9c04d748
--- /dev/null
+++ b/shim/build/AndroidManifestInstallConstraints_empty.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apex.cts.shim">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false"/>
+ <install-constraints>
+ </install-constraints>
+</manifest>
diff --git a/shim/build/AndroidManifestInstallConstraints_invalid_fingerprint.xml b/shim/build/AndroidManifestInstallConstraints_invalid_fingerprint.xml
new file mode 100644
index 00000000..c0cda96f
--- /dev/null
+++ b/shim/build/AndroidManifestInstallConstraints_invalid_fingerprint.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apex.cts.shim">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false"/>
+ <install-constraints>
+ <fingerprint-prefix android:value="does/not/exist"/>
+ </install-constraints>
+</manifest>
diff --git a/shim/build/AndroidManifestInstallConstraints_no_value.xml b/shim/build/AndroidManifestInstallConstraints_no_value.xml
new file mode 100644
index 00000000..e2e6bc84
--- /dev/null
+++ b/shim/build/AndroidManifestInstallConstraints_no_value.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.apex.cts.shim">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false"/>
+ <install-constraints>
+ <!-- PackageManager will allow installation if at least one of the fingerprint-prefixes
+ matches the build fingerprint of a device.
+ Since one of them is an empty string, the apex with these constraints can be installed
+ on all devices as long as the packagename is allowlisted by the system.
+ -->
+ <fingerprint-prefix android:value="does/not/exist"/>
+ <fingerprint-prefix android:value=""/>
+ </install-constraints>
+</manifest>