Merge "start aconfig storage daemon" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 7d20995..235fdfd 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -21,8 +21,11 @@
local_include_dirs: ["include"],
product_variables: {
debuggable: {
- cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"],
- }
+ cflags: [
+ "-UANDROID_DEBUGGABLE",
+ "-DANDROID_DEBUGGABLE=1",
+ ],
+ },
},
}
@@ -256,7 +259,7 @@
],
static_libs: [
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
@@ -299,7 +302,7 @@
},
android: {
runtime_libs: [
- "libdexfile", // libdexfile_support dependency
+ "libdexfile", // libdexfile_support dependency
],
},
},
@@ -368,6 +371,11 @@
},
},
+ sanitize: {
+ memtag_heap: true,
+ memtag_stack: true,
+ },
+
shared_libs: [
"libbase",
"libcutils",
@@ -494,7 +502,7 @@
header_libs: [
"bionic_libc_platform_headers",
- "libdebuggerd_common_headers"
+ "libdebuggerd_common_headers",
],
static_libs: [
@@ -552,7 +560,6 @@
},
}
-
// This installs the "other" architecture (so 32-bit on 64-bit device).
prebuilt_etc {
name: "crash_dump.policy_other",
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index 8633cb8..61d7155 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -14,5 +14,10 @@
{
"name": "debuggerd_test"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsCrashDetailHostTestCases"
+ }
]
}
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index fe1689c..4c6a400 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -15,7 +15,7 @@
"-fstack-protector-all",
"-Wno-date-time",
],
- tidy: false, // crasher.cpp tests many memory access errors
+ tidy: false, // crasher.cpp tests many memory access errors
srcs: ["crasher.cpp"],
arch: {
arm: {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f396b1d..526e2ca 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -21,6 +21,7 @@
#include <linux/prctl.h>
#include <malloc.h>
#include <pthread.h>
+#include <setjmp.h>
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/mman.h>
@@ -601,6 +602,54 @@
#endif
}
+__attribute__((noinline)) void mte_illegal_setjmp_helper(jmp_buf& jump_buf) {
+ // This frame is at least 8 bytes for storing and restoring the LR before the
+ // setjmp below. So this can never get an empty stack frame, even if we omit
+ // the frame pointer. So, the SP of this is always less (numerically) than the
+ // calling function frame.
+ setjmp(jump_buf);
+}
+
+TEST_F(CrasherTest, mte_illegal_setjmp) {
+ // This setjmp is illegal because it jumps back into a function that already returned.
+ // Quoting man 3 setjmp:
+ // If the function which called setjmp() returns before longjmp() is
+ // called, the behavior is undefined. Some kind of subtle or
+ // unsubtle chaos is sure to result.
+ // https://man7.org/linux/man-pages/man3/longjmp.3.html
+#if defined(__aarch64__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "Requires MTE";
+ }
+
+ int intercept_result;
+ unique_fd output_fd;
+ StartProcess([&]() {
+ SetTagCheckingLevelSync();
+ jmp_buf jump_buf;
+ mte_illegal_setjmp_helper(jump_buf);
+ longjmp(jump_buf, 1);
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+
+ // In our test-case, we have a NEGATIVE stack adjustment, which is being
+ // interpreted as unsigned integer, and thus is "too large".
+ // TODO(fmayer): fix the error message for this
+ ASSERT_MATCH(result, R"(memtag_handle_longjmp: stack adjustment too large)");
+#else
+ GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
TEST_F(CrasherTest, mte_async) {
#if defined(__aarch64__)
if (!mte_supported()) {
@@ -2647,7 +2696,7 @@
match_str += format_full_pointer(crash_uptr);
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)");
// Verifies that the fault address error message is at the end of the
// maps section. To do this, the check below looks for the start of the
@@ -2699,7 +2748,7 @@
match_str += format_full_pointer(reinterpret_cast<uintptr_t>(middle_ptr));
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)");
match_str = android::base::StringPrintf(
R"( %s.*\n--->Fault address falls at %s between mapped regions\n %s)",
@@ -2737,7 +2786,7 @@
match_str += format_full_pointer(reinterpret_cast<uintptr_t>(ptr));
ASSERT_MATCH(result, match_str);
- ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
+ ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)");
match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str());
ASSERT_MATCH(result, match_str);
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 7be5d61..7b9e780 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -52,4 +52,3 @@
sdk_version: "current",
static_libs: ["libprotobuf-java-lite"],
}
-
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
index 2007f39..bf19bb7 100644
--- a/debuggerd/rust/tombstoned_client/Android.bp
+++ b/debuggerd/rust/tombstoned_client/Android.bp
@@ -8,7 +8,7 @@
"wrapper.cpp",
],
generated_sources: [
- "libtombstoned_client_rust_bridge_code"
+ "libtombstoned_client_rust_bridge_code",
],
header_libs: [
"libbase_headers",
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 972a575..dc751da 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,8 +25,8 @@
faccessat: 1
recvmsg: 1
recvfrom: 1
-sysinfo: 1
setsockopt: 1
+sysinfo: 1
process_vm_readv: 1
@@ -53,20 +53,29 @@
#if 0
libminijail on vendor partitions older than P does not have constants from <sys/mman.h>.
-Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility.
+Define values for PROT_READ, PROT_WRITE and PROT_MTE ourselves to maintain backwards compatibility.
#else
#define PROT_READ 0x1
#define PROT_WRITE 0x2
+#define PROT_MTE 0x20
#endif
madvise: 1
+#if defined(__aarch64__)
+mprotect: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
mprotect: arg2 in PROT_READ|PROT_WRITE
+#endif
munmap: 1
#if defined(__LP64__)
getuid: 1
fstat: 1
+#if defined(__aarch64__)
+mmap: arg2 in PROT_READ|PROT_WRITE|PROT_MTE
+#else
mmap: arg2 in PROT_READ|PROT_WRITE
+#endif
#else
getuid32: 1
fstat64: 1
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
index d3f7520..0ad3243 100644
--- a/debuggerd/test_permissive_mte/Android.bp
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -17,22 +17,28 @@
}
cc_binary {
- name: "mte_crash",
- tidy: false,
- srcs: ["mte_crash.cpp"],
- sanitize: {
- memtag_heap: true,
- diag: {
- memtag_heap: true,
+ name: "mte_crash",
+ tidy: false,
+ srcs: ["mte_crash.cpp"],
+ sanitize: {
+ memtag_heap: true,
+ diag: {
+ memtag_heap: true,
+ },
},
- },
}
java_test_host {
name: "permissive_mte_test",
libs: ["tradefed"],
- static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
- srcs: ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+ static_libs: [
+ "frameworks-base-hostutils",
+ "cts-install-lib-host",
+ ],
+ srcs: [
+ "src/**/PermissiveMteTest.java",
+ ":libtombstone_proto-src",
+ ],
data: [":mte_crash"],
test_config: "AndroidTest.xml",
test_suites: ["general-tests"],
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index cf7904f..e2a67a2 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -61,7 +61,6 @@
struct CrashArtifact {
unique_fd fd;
- std::optional<std::string> temporary_path;
static CrashArtifact devnull() {
CrashArtifact result;
@@ -145,16 +144,13 @@
std::optional<std::string> path;
result.fd.reset(openat(dir_fd_, ".", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0660));
if (result.fd == -1) {
- // We might not have O_TMPFILE. Try creating with an arbitrary filename instead.
- static size_t counter = 0;
- std::string tmp_filename = StringPrintf(".temporary%zu", counter++);
- result.fd.reset(openat(dir_fd_, tmp_filename.c_str(),
- O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_CLOEXEC, 0660));
- if (result.fd == -1) {
- PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
- }
+ PLOG(FATAL) << "failed to create temporary tombstone in " << dir_path_;
+ }
- result.temporary_path = std::move(tmp_filename);
+ // We need to fchmodat after creating to avoid getting the umask applied.
+ std::string fd_path = StringPrintf("/proc/self/fd/%d", result.fd.get());
+ if (fchmodat(dir_fd_, fd_path.c_str(), 0664, 0) != 0) {
+ PLOG(ERROR) << "Failed to make tombstone world-readable";
}
return std::move(result);
@@ -417,6 +413,7 @@
return false;
}
+ // This fd is created inside of dirfd in CrashQueue::create_temporary_file.
std::string fd_path = StringPrintf("/proc/self/fd/%d", fd.get());
rc = linkat(AT_FDCWD, fd_path.c_str(), dirfd.get(), path.c_str(), AT_SYMLINK_FOLLOW);
if (rc != 0) {
@@ -471,20 +468,6 @@
rename_tombstone_fd(crash->output.proto->fd, queue->dir_fd(), *paths.proto);
}
}
-
- // If we don't have O_TMPFILE, we need to clean up after ourselves.
- if (crash->output.text.temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.text.temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary tombstone at " << paths.text;
- }
- }
- if (crash->output.proto && crash->output.proto->temporary_path) {
- rc = unlinkat(queue->dir_fd().get(), crash->output.proto->temporary_path->c_str(), 0);
- if (rc != 0) {
- PLOG(ERROR) << "failed to unlink temporary proto tombstone";
- }
- }
}
static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {
diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 90d91a0..1f6bd1a 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -61,6 +61,10 @@
return block_device_ + " " + std::to_string(physical_sector_);
}
+std::string DmTargetStripe::GetParameterString() const {
+ return "2 " + std::to_string(chunksize) + " " + block_device0_ + " 0 " + block_device1_ + " 0";
+}
+
DmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
const std::string& block_device, const std::string& hash_device,
uint32_t data_block_size, uint32_t hash_block_size,
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index a0129c2..d043be6 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -181,6 +181,13 @@
ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
}
+TEST_F(DmTest, StripeArgs) {
+ DmTargetStripe target(0, 4096, 1024, "/dev/loop0", "/dev/loop1");
+ ASSERT_EQ(target.name(), "striped");
+ ASSERT_TRUE(target.Valid());
+ ASSERT_EQ(target.GetParameterString(), "2 1024 /dev/loop0 0 /dev/loop1 0");
+}
+
TEST_F(DmTest, DmVerityArgsAvb2) {
std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
std::string algorithm = "sha1";
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 09fe200..97f3c13 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -116,6 +116,24 @@
uint64_t physical_sector_;
};
+class DmTargetStripe final : public DmTarget {
+ public:
+ DmTargetStripe(uint64_t start, uint64_t length, uint64_t chunksize,
+ const std::string& block_device0, const std::string& block_device1)
+ : DmTarget(start, length),
+ chunksize(chunksize),
+ block_device0_(block_device0),
+ block_device1_(block_device1) {}
+
+ std::string name() const override { return "striped"; }
+ std::string GetParameterString() const override;
+
+ private:
+ uint64_t chunksize;
+ std::string block_device0_;
+ std::string block_device1_;
+};
+
class DmTargetVerity final : public DmTarget {
public:
DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 30c5135..de2e528 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -603,41 +603,47 @@
std::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithThreadedCompression(
const size_t num_blocks, const void* data, CowOperationType type) {
const size_t num_threads = num_compress_threads_;
- const size_t blocks_per_thread = DivRoundUp(num_blocks, num_threads);
const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+ // We will alternate which thread to send compress work to. E.g. alternate between T1 and T2
+ // until all blocks are processed
std::vector<CompressedBuffer> compressed_vec;
- // Submit the blocks per thread. The retrieval of
- // compressed buffers has to be done in the same order.
- // We should not poll for completed buffers in a different order as the
- // buffers are tightly coupled with block ordering.
- for (size_t i = 0; i < num_threads; i++) {
- CompressWorker* worker = compress_threads_[i].get();
- auto blocks_in_batch = std::min(num_blocks - i * blocks_per_thread, blocks_per_thread);
- // Enqueue the blocks to be compressed for each thread.
- while (blocks_in_batch) {
- CompressedBuffer buffer;
+ int iteration = 0;
+ int blocks_to_compress = static_cast<int>(num_blocks);
+ while (blocks_to_compress) {
+ CompressedBuffer buffer;
+ CompressWorker* worker = compress_threads_[iteration % num_threads].get();
- const size_t compression_factor = GetCompressionFactor(blocks_in_batch, type);
- size_t num_blocks = compression_factor / header_.block_size;
+ const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);
+ size_t num_blocks = compression_factor / header_.block_size;
- buffer.compression_factor = compression_factor;
- worker->EnqueueCompressBlocks(iter, compression_factor, 1);
- compressed_vec.push_back(std::move(buffer));
- blocks_in_batch -= num_blocks;
- iter += compression_factor;
- }
+ worker->EnqueueCompressBlocks(iter, compression_factor, 1);
+ buffer.compression_factor = compression_factor;
+ compressed_vec.push_back(std::move(buffer));
+
+ iteration++;
+ iter += compression_factor;
+ blocks_to_compress -= num_blocks;
}
- // Fetch compressed buffers from the threads
std::vector<std::vector<uint8_t>> compressed_buf;
+ std::vector<std::vector<std::vector<uint8_t>>> worker_buffers(num_threads);
compressed_buf.clear();
for (size_t i = 0; i < num_threads; i++) {
CompressWorker* worker = compress_threads_[i].get();
- if (!worker->GetCompressedBuffers(&compressed_buf)) {
+ if (!worker->GetCompressedBuffers(&worker_buffers[i])) {
return {};
}
}
+ // compressed_vec | CB 1 | CB 2 | CB 3 | CB 4 | <-compressed buffers
+ // t1 t2 t1 t2 <- processed by these threads
+ // Ordering is important here. We need to retrieve the compressed data in the same order we
+ // processed it and assume that that we submit data beginning with the first thread and then
+ // round robin the consecutive data calls. We need to Fetch compressed buffers from the threads
+ // via the same ordering
+ for (size_t i = 0; i < compressed_vec.size(); i++) {
+ compressed_buf.emplace_back(worker_buffers[i % num_threads][i / num_threads]);
+ }
if (compressed_vec.size() != compressed_buf.size()) {
LOG(ERROR) << "Compressed buffer size: " << compressed_buf.size()
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index cf26a16..a4a2c1a 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -39,6 +39,7 @@
namespace android {
namespace snapshot {
+// @VsrTest = 3.7.6
class PartitionCowCreatorTest : public ::testing::Test {
public:
void SetUp() override {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index cbe8285..e6c4de6 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -508,8 +508,6 @@
// When snapshots are on current slot, we determine the size
// of block device based on the number of COW operations. We cannot
// use base device as it will be from older image.
- size_t num_ops = 0;
- uint64_t dev_sz = 0;
unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
if (fd < 0) {
PLOG(ERROR) << "Failed to open " << cow_file;
@@ -522,13 +520,18 @@
return false;
}
+ uint64_t dev_sz = 0;
const auto& header = reader.GetHeader();
- if (header.prefix.major_version > 2) {
- LOG(ERROR) << "COW format not supported";
- return false;
+ if (header.prefix.major_version == 2) {
+ const size_t num_ops = reader.get_num_total_data_ops();
+ dev_sz = (num_ops * header.block_size);
+ } else {
+ // create_snapshot will skip in-place copy ops. Hence, fetch this
+ // information directly from v3 header.
+ const auto& v3_header = reader.header_v3();
+ dev_sz = v3_header.op_count_max * v3_header.block_size;
}
- num_ops = reader.get_num_total_data_ops();
- dev_sz = (num_ops * header.block_size);
+
base_sectors = dev_sz >> 9;
} else {
// For userspace snapshots, the size of the base device is taken as the
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index e538d50..47e6ce9 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -104,6 +104,7 @@
void MountMetadata();
+// @VsrTest = 3.7.6
class SnapshotTest : public ::testing::Test {
public:
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 0396a55..5d3f96c 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -15,6 +15,7 @@
//
#include <sysexits.h>
+#include <unistd.h>
#include <chrono>
#include <filesystem>
@@ -46,13 +47,14 @@
#include "partition_cow_creator.h"
-#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
#include <BootControlClient.h>
-#endif
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace android::storage_literals;
+using android::base::LogdLogger;
+using android::base::StderrLogger;
+using android::base::TeeLogger;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::FindPartition;
using android::fs_mgr::GetPartitionSize;
@@ -79,7 +81,11 @@
" revert-snapshots\n"
" Prepares devices to boot without snapshots on next boot.\n"
" This does not delete the snapshot. It only removes the indicators\n"
- " so that first stage init will not mount from snapshots.\n";
+ " so that first stage init will not mount from snapshots.\n"
+ " apply-update\n"
+ " Apply the incremental OTA update wherein the snapshots are\n"
+ " directly written to COW block device. This will bypass update-engine\n"
+ " and the device will be ready to boot from the target build.\n";
return EX_USAGE;
}
@@ -96,14 +102,22 @@
bool DeleteSnapshots();
bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
bool BeginUpdate();
+ bool ApplyUpdate();
private:
std::optional<std::string> GetCowImagePath(std::string& name);
+ bool PrepareUpdate();
bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+ std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,
+ const std::string& partiton_name);
std::unique_ptr<SnapshotManager::LockedFile> lock_;
std::unique_ptr<SnapshotManager> sm_;
std::vector<std::future<bool>> threads_;
std::string snapshot_dir_path_;
+ std::unordered_map<std::string, chromeos_update_engine::DynamicPartitionGroup*> group_map_;
+
+ std::vector<std::string> patchfiles_;
+ chromeos_update_engine::DeltaArchiveManifest manifest_;
};
MapSnapshots::MapSnapshots(std::string path) {
@@ -115,6 +129,178 @@
snapshot_dir_path_ = path + "/";
}
+std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
+ const std::string& partition_name) {
+ std::string group_name;
+ for (const auto& partition : pt.partitions) {
+ std::string name = android::fs_mgr::GetPartitionName(partition);
+ auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
+ std::string pname = name.substr(0, name.size() - suffix.size());
+ if (pname == partition_name) {
+ std::string group_name =
+ android::fs_mgr::GetPartitionGroupName(pt.groups[partition.group_index]);
+ return group_name.substr(0, group_name.size() - suffix.size());
+ }
+ }
+ return "";
+}
+
+bool MapSnapshots::PrepareUpdate() {
+ auto source_slot = fs_mgr_get_slot_suffix();
+ auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+ auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
+
+ // Get current partition information.
+ PartitionOpener opener;
+ auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
+ if (!source_metadata) {
+ LOG(ERROR) << "Could not read source partition metadata.\n";
+ return false;
+ }
+
+ auto dap = manifest_.mutable_dynamic_partition_metadata();
+ dap->set_snapshot_enabled(true);
+ dap->set_vabc_enabled(true);
+ dap->set_vabc_compression_param("lz4");
+ dap->set_cow_version(3);
+
+ for (const auto& entry : std::filesystem::directory_iterator(snapshot_dir_path_)) {
+ if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+ patchfiles_.push_back(android::base::Basename(entry.path().generic_string()));
+ }
+ }
+
+ for (auto& patchfile : patchfiles_) {
+ std::string parsing_file = snapshot_dir_path_ + patchfile;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+ if (fd < 0) {
+ LOG(ERROR) << "Failed to open file: " << parsing_file;
+ return false;
+ }
+ uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+ if (!dev_sz) {
+ LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+ return false;
+ }
+
+ const int block_sz = 4_KiB;
+ dev_sz += block_sz - 1;
+ dev_sz &= ~(block_sz - 1);
+
+ auto npos = patchfile.rfind(".patch");
+ auto partition_name = patchfile.substr(0, npos);
+
+ chromeos_update_engine::DynamicPartitionGroup* group = nullptr;
+ std::string group_name = GetGroupName(*source_metadata.get(), partition_name);
+ if (group_map_.find(group_name) != group_map_.end()) {
+ group = group_map_[group_name];
+ } else {
+ group = dap->add_groups();
+ group->set_name(group_name);
+ group_map_[group_name] = group;
+ }
+ group->add_partition_names(partition_name);
+
+ auto pu = manifest_.mutable_partitions()->Add();
+ pu->set_partition_name(partition_name);
+ pu->set_estimate_cow_size(dev_sz);
+
+ CowReader reader;
+ if (!reader.Parse(fd)) {
+ LOG(ERROR) << "COW reader parse failed";
+ return false;
+ }
+
+ uint64_t new_device_size = 0;
+ const auto& header = reader.GetHeader();
+ if (header.prefix.major_version == 2) {
+ size_t num_ops = reader.get_num_total_data_ops();
+ new_device_size = (num_ops * header.block_size);
+ } else {
+ const auto& v3_header = reader.header_v3();
+ new_device_size = v3_header.op_count_max * v3_header.block_size;
+ }
+
+ LOG(INFO) << "Partition: " << partition_name << " Group_name: " << group_name
+ << " size: " << new_device_size << " COW-size: " << dev_sz;
+ pu->mutable_new_partition_info()->set_size(new_device_size);
+ }
+ return true;
+}
+
+bool MapSnapshots::ApplyUpdate() {
+ if (!PrepareUpdate()) {
+ LOG(ERROR) << "PrepareUpdate failed";
+ return false;
+ }
+ if (!sm_->BeginUpdate()) {
+ LOG(ERROR) << "BeginUpdate failed";
+ return false;
+ }
+ if (!sm_->CreateUpdateSnapshots(manifest_)) {
+ LOG(ERROR) << "Could not apply snapshots";
+ return false;
+ }
+
+ LOG(INFO) << "CreateUpdateSnapshots success";
+ if (!sm_->MapAllSnapshots(10s)) {
+ LOG(ERROR) << "MapAllSnapshots failed";
+ return false;
+ }
+
+ LOG(INFO) << "MapAllSnapshots success";
+
+ auto& dm = android::dm::DeviceMapper::Instance();
+ auto target_slot = fs_mgr_get_other_slot_suffix();
+ for (auto& patchfile : patchfiles_) {
+ auto npos = patchfile.rfind(".patch");
+ auto partition_name = patchfile.substr(0, npos) + target_slot;
+ auto cow_device = partition_name + "-cow";
+ std::string cow_path;
+ if (!dm.GetDmDevicePathByName(cow_device, &cow_path)) {
+ LOG(ERROR) << "Failed to cow path";
+ return false;
+ }
+ threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,
+ this, cow_path, patchfile));
+ }
+
+ bool ret = true;
+ for (auto& t : threads_) {
+ ret = t.get() && ret;
+ }
+ if (!ret) {
+ LOG(ERROR) << "Snapshot writes failed";
+ return false;
+ }
+ if (!sm_->UnmapAllSnapshots()) {
+ LOG(ERROR) << "UnmapAllSnapshots failed";
+ return false;
+ }
+
+ LOG(INFO) << "Pre-created snapshots successfully copied";
+ // All snapshots have been written.
+ if (!sm_->FinishedSnapshotWrites(false /* wipe */)) {
+ LOG(ERROR) << "Could not finalize snapshot writes.\n";
+ return false;
+ }
+
+ auto hal = hal::BootControlClient::WaitForService();
+ if (!hal) {
+ LOG(ERROR) << "Could not find IBootControl HAL.\n";
+ return false;
+ }
+ auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
+ auto cr = hal->SetActiveBootSlot(target_slot_number);
+ if (!cr.IsOk()) {
+ LOG(ERROR) << "Could not set active boot slot: " << cr.errMsg;
+ return false;
+ }
+
+ LOG(INFO) << "ApplyUpdate success";
+ return true;
+}
+
bool MapSnapshots::BeginUpdate() {
lock_ = sm_->LockExclusive();
std::vector<std::string> snapshots;
@@ -227,11 +413,10 @@
if (file_offset >= dev_sz) {
break;
}
-
- if (fsync(cfd.get()) < 0) {
- PLOG(ERROR) << "Fsync failed at offset: " << file_offset << " size: " << to_read;
- return false;
- }
+ }
+ if (fsync(cfd.get()) < 0) {
+ PLOG(ERROR) << "Fsync failed";
+ return false;
}
return true;
}
@@ -279,23 +464,23 @@
}
bool DumpCmdHandler(int /*argc*/, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
return SnapshotManager::New()->Dump(std::cout);
}
bool MapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
using namespace std::chrono_literals;
return SnapshotManager::New()->MapAllSnapshots(5000ms);
}
bool UnmapCmdHandler(int, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
return SnapshotManager::New()->UnmapAllSnapshots();
}
bool MergeCmdHandler(int /*argc*/, char** argv) {
- android::base::InitLogging(argv, &android::base::StderrLogger);
+ android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));
LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead.";
return false;
}
@@ -367,6 +552,30 @@
return snapshot.DeleteSnapshots();
}
+bool ApplyUpdate(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ // Make sure we are root.
+ if (::getuid() != 0) {
+ LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+ return EXIT_FAILURE;
+ }
+
+ if (argc < 3) {
+ std::cerr << " apply-update <directory location where snapshot patches are present>"
+ " Apply the snapshots to the COW block device\n";
+ return false;
+ }
+
+ std::string path = std::string(argv[2]);
+ MapSnapshots cow(path);
+ if (!cow.ApplyUpdate()) {
+ return false;
+ }
+ LOG(INFO) << "Apply update success. Please reboot the device";
+ return true;
+}
+
bool MapPrecreatedSnapshots(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
@@ -554,6 +763,7 @@
{"test-blank-ota", TestOtaHandler},
#endif
{"unmap", UnmapCmdHandler},
+ {"apply-update", ApplyUpdate},
{"map-snapshots", MapPrecreatedSnapshots},
{"unmap-snapshots", UnMapPrecreatedSnapshots},
{"delete-snapshots", DeletePrecreatedSnapshots},
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
index d387eb3..b5618c4 100755
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -21,6 +21,7 @@
return android::base::GetIntProperty("ro.vendor.api_level", -1);
}
+// @VsrTest = 3.7.6
TEST(VAB, Enabled) {
if (!android::base::GetBoolProperty("ro.build.ab_update", false) && (GetVsrLevel() < __ANDROID_API_T__)) {
GTEST_SKIP();
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 7ac7a16..526c761 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1081,7 +1081,9 @@
LOG RUN "Testing adb disable-verity -R"
T=$(adb_date)
-adb_su disable-verity -R >&2 ||
+adb_su disable-verity -R >&2
+err=${?}
+[[ ${err} -eq 0 || ${err} -eq 255 ]] ||
die -t "${T}" "disable-verity -R failed"
sleep 2
adb_wait "${ADB_WAIT}" ||
@@ -1192,7 +1194,9 @@
LOG RUN "Testing adb remount -R"
T=$(adb_date)
-adb_su remount -R </dev/null >&2 ||
+adb_su remount -R </dev/null >&2
+err=${?}
+[[ ${err} -eq 0 || ${err} -eq 255 ]] ||
die -t "${T}" "adb remount -R failed"
sleep 2
adb_wait "${ADB_WAIT}" ||
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7273087..9dc8c24 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -115,6 +115,21 @@
std::string block_device = NextArg();
return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
block_device);
+ } else if (target_type == "striped") {
+ if (!HasArgs(3)) {
+ std::cerr << "Expected \"striped\" <block_device0> <block_device1> <chunksize>"
+ << std::endl;
+ return nullptr;
+ }
+ std::string block_device0 = NextArg();
+ std::string block_device1 = NextArg();
+ uint64_t chunk_size;
+ if (!android::base::ParseUint(NextArg(), &chunk_size)) {
+ std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
+ return nullptr;
+ }
+ return std::make_unique<DmTargetStripe>(start_sector, num_sectors, chunk_size,
+ block_device0, block_device1);
} else if (target_type == "bow") {
if (!HasArgs(1)) {
std::cerr << "Expected \"bow\" <block_device>" << std::endl;
diff --git a/init/Android.bp b/init/Android.bp
index 2d16f60..12ca15a 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,6 +162,7 @@
},
static_libs: [
"libavb",
+ "libavf_cc_flags",
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
@@ -359,6 +360,7 @@
static_libs: [
"libc++fs",
"libfs_avb",
+ "libavf_cc_flags",
"libfs_mgr",
"libfec",
"libfec_rs",
@@ -459,7 +461,10 @@
cc_binary {
name: "init_first_stage.microdroid",
- defaults: ["init_first_stage_defaults"],
+ defaults: [
+ "avf_build_flags_cc",
+ "init_first_stage_defaults",
+ ],
cflags: ["-DMICRODROID=1"],
installable: false,
}
@@ -597,7 +602,6 @@
},
generated_headers: [
"generated_stub_builtin_function_map",
- "generated_android_ids",
],
target: {
android: {
@@ -613,6 +617,9 @@
name: "host_init_verifier",
defaults: ["init_host_defaults"],
srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
+ generated_headers: [
+ "generated_android_ids",
+ ],
}
cc_library_host_static {
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 05e00ed..a686d05 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -132,11 +132,19 @@
bool BlockDevInitializer::InitDmDevice(const std::string& device) {
const std::string device_name(basename(device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
+ return InitDevice(syspath, device_name);
+}
+
+bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
+ return InitDevice("/sys/devices/platform", dev_name);
+}
+
+bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
bool found = false;
- auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
+ auto uevent_callback = [&device_name, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
- LOG(VERBOSE) << "Creating device-mapper device : " << device;
+ LOG(VERBOSE) << "Creating device : " << device_name;
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
@@ -146,13 +154,13 @@
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
if (!found) {
- LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
+ LOG(INFO) << "device '" << device_name << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
- LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
+ LOG(INFO) << "wait for device '" << device_name << "' returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
+ LOG(ERROR) << "device '" << device_name << "' not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index ec39ce0..d5b1f60 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -24,6 +24,7 @@
namespace android {
namespace init {
+// TODO: should this be renamed to FirstStageDevInitialize?
class BlockDevInitializer final {
public:
BlockDevInitializer();
@@ -32,11 +33,13 @@
bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
+ bool InitPlatformDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
bool InitMiscDevice(const std::string& name);
+ bool InitDevice(const std::string& syspath, const std::string& device);
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c4d0f75..356aaa0 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -37,6 +37,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android/avf_cc_flags.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -385,7 +386,12 @@
// /second_stage_resources is used to preserve files from first to second
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=0"))
+ "mode=0755,uid=0,gid=0"));
+
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ CHECKCALL(mount("tmpfs", "/microdroid_resources", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0750,uid=0,gid=0"));
+ }
#undef CHECKCALL
SetStdioToDevNull(argv);
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index c0b9281..836d536 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -16,6 +16,7 @@
#include "first_stage_mount.h"
+#include <signal.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
@@ -33,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/avf_cc_flags.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
@@ -272,6 +274,11 @@
return true;
}
+// TODO: should this be in a library in packages/modules/Virtualization first_stage_init links?
+static bool IsMicrodroidStrictBoot() {
+ return access("/proc/device-tree/chosen/avf,strict-boot", F_OK) == 0;
+}
+
bool FirstStageMountVBootV2::InitDevices() {
std::set<std::string> devices;
GetSuperDeviceName(&devices);
@@ -283,6 +290,14 @@
return false;
}
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (IsMicrodroidStrictBoot()) {
+ if (!block_dev_init_.InitPlatformDevice("open-dice0")) {
+ return false;
+ }
+ }
+ }
+
if (IsDmLinearEnabled()) {
auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
if (!android::base::Realpath(super_symlink, &super_path_)) {
@@ -527,9 +542,48 @@
return true;
}
+static bool MaybeDeriveMicrodroidVendorDiceNode(Fstab* fstab) {
+ std::optional<std::string> microdroid_vendor_block_dev;
+ for (auto entry = fstab->begin(); entry != fstab->end(); entry++) {
+ if (entry->mount_point == "/vendor") {
+ microdroid_vendor_block_dev.emplace(entry->blk_device);
+ break;
+ }
+ }
+ if (!microdroid_vendor_block_dev.has_value()) {
+ LOG(VERBOSE) << "No microdroid vendor partition to mount";
+ return true;
+ }
+ // clang-format off
+ const std::array<const char*, 7> args = {
+ "/system/bin/derive_microdroid_vendor_dice_node",
+ "--dice-driver", "/dev/open-dice0",
+ "--microdroid-vendor-disk-image", microdroid_vendor_block_dev->data(),
+ "--output", "/microdroid_resources/dice_chain.raw",
+ };
+ // clang-format-on
+ // ForkExecveAndWaitForCompletion calls waitpid to wait for the fork-ed process to finish.
+ // The first_stage_console adds SA_NOCLDWAIT flag to the SIGCHLD handler, which means that
+ // waitpid will always return -ECHLD. Here we re-register a default handler, so that waitpid
+ // works.
+ LOG(INFO) << "Deriving dice node for microdroid vendor partition";
+ signal(SIGCHLD, SIG_DFL);
+ if (!ForkExecveAndWaitForCompletion(args[0], (char**)args.data())) {
+ LOG(ERROR) << "Failed to derive microdroid vendor dice node";
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMountVBootV2::MountPartitions() {
if (!TrySwitchSystemAsRoot()) return false;
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (!MaybeDeriveMicrodroidVendorDiceNode(&fstab_)) {
+ return false;
+ }
+ }
+
if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
for (auto current = fstab_.begin(); current != fstab_.end();) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index bc4ef42..58a0a7f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -58,6 +58,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
+#include <private/android_filesystem_config.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -117,12 +118,13 @@
static bool persistent_properties_loaded = false;
-static int property_set_fd = -1;
static int from_init_socket = -1;
static int init_socket = -1;
static bool accept_messages = false;
static std::mutex accept_messages_lock;
+static std::mutex selinux_check_access_lock;
static std::thread property_service_thread;
+static std::thread property_service_for_system_thread;
static std::unique_ptr<PersistWriteThread> persist_write_thread;
@@ -167,6 +169,7 @@
ucred cr = {.pid = 0, .uid = 0, .gid = 0};
audit_data.cr = &cr;
+ auto lock = std::lock_guard{selinux_check_access_lock};
return selinux_check_access(source_context.c_str(), target_context, "file", "read",
&audit_data) == 0;
}
@@ -182,10 +185,9 @@
audit_data.name = name.c_str();
audit_data.cr = &cr;
- bool has_access = (selinux_check_access(source_context, target_context, "property_service",
- "set", &audit_data) == 0);
-
- return has_access;
+ auto lock = std::lock_guard{selinux_check_access_lock};
+ return selinux_check_access(source_context, target_context, "property_service", "set",
+ &audit_data) == 0;
}
void NotifyPropertyChange(const std::string& name, const std::string& value) {
@@ -400,30 +402,38 @@
return {PROP_ERROR_INVALID_VALUE};
}
- prop_info* pi = (prop_info*)__system_property_find(name.c_str());
- if (pi != nullptr) {
- // ro.* properties are actually "write-once".
- if (StartsWith(name, "ro.")) {
- *error = "Read-only property was already set";
- return {PROP_ERROR_READ_ONLY_PROPERTY};
- }
-
- __system_property_update(pi, value.c_str(), valuelen);
+ if (name == "sys.powerctl") {
+ // No action here - NotifyPropertyChange will trigger the appropriate action, and since this
+ // can come to the second thread, we mustn't call out to the __system_property_* functions
+ // which support multiple readers but only one mutator.
} else {
- int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
- if (rc < 0) {
- *error = "__system_property_add failed";
- return {PROP_ERROR_SET_FAILED};
- }
- }
+ prop_info* pi = (prop_info*)__system_property_find(name.c_str());
+ if (pi != nullptr) {
+ // ro.* properties are actually "write-once".
+ if (StartsWith(name, "ro.")) {
+ *error = "Read-only property was already set";
+ return {PROP_ERROR_READ_ONLY_PROPERTY};
+ }
- bool need_persist = StartsWith(name, "persist.") || StartsWith(name, "next_boot.");
- if (socket && persistent_properties_loaded && need_persist) {
- if (persist_write_thread) {
- persist_write_thread->Write(name, value, std::move(*socket));
- return {};
+ __system_property_update(pi, value.c_str(), valuelen);
+ } else {
+ int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
+ if (rc < 0) {
+ *error = "__system_property_add failed";
+ return {PROP_ERROR_SET_FAILED};
+ }
}
- WritePersistentProperty(name, value);
+
+ // Don't write properties to disk until after we have read all default
+ // properties to prevent them from being overwritten by default values.
+ bool need_persist = StartsWith(name, "persist.") || StartsWith(name, "next_boot.");
+ if (socket && persistent_properties_loaded && need_persist) {
+ if (persist_write_thread) {
+ persist_write_thread->Write(name, value, std::move(*socket));
+ return {};
+ }
+ WritePersistentProperty(name, value);
+ }
}
NotifyPropertyChange(name, value);
@@ -584,10 +594,10 @@
return *ret;
}
-static void handle_property_set_fd() {
+static void handle_property_set_fd(int fd) {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
- int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
+ int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
@@ -1090,6 +1100,12 @@
// required to support.
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
+ if (__system_property_find(VENDOR_API_LEVEL_PROP) != nullptr) {
+ // The device already have ro.vendor.api_level in its vendor/build.prop.
+ // Skip initializing the ro.vendor.api_level property.
+ return;
+ }
+
auto vendor_api_level = GetIntProperty("ro.board.first_api_level", __ANDROID_VENDOR_API_MAX__);
if (vendor_api_level != __ANDROID_VENDOR_API_MAX__) {
// Update the vendor_api_level with "ro.board.api_level" only if both "ro.board.api_level"
@@ -1295,7 +1311,7 @@
mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);
if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,
false)) {
- PLOG(ERROR) << "Unable to write vendor overrides to file";
+ PLOG(ERROR) << "Unable to write appcompat override property infos to file";
}
selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);
}
@@ -1432,19 +1448,21 @@
}
}
-static void PropertyServiceThread() {
+static void PropertyServiceThread(int fd, bool listen_init) {
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
LOG(FATAL) << result.error();
}
- if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
+ if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
!result.ok()) {
LOG(FATAL) << result.error();
}
- if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
- LOG(FATAL) << result.error();
+ if (listen_init) {
+ if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
+ LOG(FATAL) << result.error();
+ }
}
while (true) {
@@ -1493,6 +1511,23 @@
cv_.notify_all();
}
+void StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {
+ int fd = -1;
+ if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+ /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,
+ /*gid=*/gid, /*socketcon=*/{});
+ result.ok()) {
+ fd = *result;
+ } else {
+ LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+ }
+
+ listen(fd, 8);
+
+ auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);
+ t.swap(new_thread);
+}
+
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");
@@ -1504,19 +1539,9 @@
init_socket = sockets[1];
StartSendingMessages();
- if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
- /*gid=*/0, /*socketcon=*/{});
- result.ok()) {
- property_set_fd = *result;
- } else {
- LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
- }
-
- listen(property_set_fd, 8);
-
- auto new_thread = std::thread{PropertyServiceThread};
- property_service_thread.swap(new_thread);
+ StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,
+ true);
+ StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);
auto async_persist_writes =
android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 1a26c4d..150f8f4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1083,7 +1083,8 @@
return;
}
}
- } else if (reboot_target == "quiescent") {
+ } else if (std::find(cmd_params.begin(), cmd_params.end(), "quiescent")
+ != cmd_params.end()) { // Quiescent can be either subreason or details.
bootloader_message boot = {};
if (std::string err; !read_bootloader_message(&boot, &err)) {
LOG(ERROR) << "Failed to read bootloader message: " << err;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 1f211dd..e191b60 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -111,97 +111,6 @@
return true;
}
-// Forks, executes the provided program in the child, and waits for the completion in the parent.
-// Child's stderr is captured and logged using LOG(ERROR).
-bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[0]);
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- close(pipe_fds[1]);
-
- if (execv(filename, argv) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[1]);
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- close(child_out_fd);
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
bool ReadFirstLine(const char* file, std::string* line) {
line->clear();
diff --git a/init/util.cpp b/init/util.cpp
index e760a59..e5efc7d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -747,5 +748,96 @@
return filtered_configs;
}
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[0]);
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ close(pipe_fds[1]);
+
+ if (execv(filename, argv) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[1]);
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ close(child_out_fd);
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2d02182..aa24123 100644
--- a/init/util.h
+++ b/init/util.h
@@ -117,5 +117,10 @@
// (.rc == .0rc for ranking purposes)
std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
int active_sdk);
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]);
+
} // namespace init
} // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 8ae7d9e..b7752d9 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -116,27 +116,6 @@
},
}
-cc_test {
- name: "libcutils_sockets_test",
- test_suites: ["device-tests"],
- static_libs: ["libbase", "libcutils_sockets"],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-
- srcs: ["sockets_test.cpp"],
- target: {
- android: {
- srcs: [
- "android_get_control_file_test.cpp",
- "android_get_control_socket_test.cpp",
- ],
- },
- },
-}
-
// some files must not be compiled when building against Mingw
// they correspond to features not used by our host development tools
// which are also hard or even impossible to port to native Win32
@@ -347,7 +326,10 @@
cc_test {
name: "KernelLibcutilsTest",
- test_suites: ["general-tests", "vts"],
+ test_suites: [
+ "general-tests",
+ "vts",
+ ],
defaults: ["libcutils_test_static_defaults"],
test_config: "KernelLibcutilsTest.xml",
}
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 410dbfd..46b8ef2 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -301,6 +301,12 @@
return -1;
}
+ // forbid size changes to match ashmem behaviour
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) == -1) {
+ ALOGE("memfd_create(%s, %zd) F_ADD_SEALS failed: %m", name, size);
+ return -1;
+ }
+
if (debug_log) {
ALOGE("memfd_create(%s, %zd) success. fd=%d\n", name, size, fd.get());
}
@@ -352,14 +358,29 @@
}
static int memfd_set_prot_region(int fd, int prot) {
- /* Only proceed if an fd needs to be write-protected */
+ int seals = fcntl(fd, F_GET_SEALS);
+ if (seals == -1) {
+ ALOGE("memfd_set_prot_region(%d, %d): F_GET_SEALS failed: %s\n", fd, prot, strerror(errno));
+ return -1;
+ }
+
if (prot & PROT_WRITE) {
+ /* Now we want the buffer to be read-write, let's check if the buffer
+ * has been previously marked as read-only before, if so return error
+ */
+ if (seals & F_SEAL_FUTURE_WRITE) {
+ ALOGE("memfd_set_prot_region(%d, %d): region is write protected\n", fd, prot);
+ errno = EINVAL; // inline with ashmem error code, if already in
+ // read-only mode
+ return -1;
+ }
return 0;
}
- if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
- ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %s\n", fd, prot,
- strerror(errno));
+ /* We would only allow read-only for any future file operations */
+ if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE | F_SEAL_SEAL) == -1) {
+ ALOGE("memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE | F_SEAL_SEAL seal failed: %s\n",
+ fd, prot, strerror(errno));
return -1;
}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 5d79d6a..d7a90c4 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -41,6 +41,7 @@
std::vector<std::string>* post_dependencies);
void ResetModuleCount() { module_count_ = 0; }
int GetModuleCount() { return module_count_; }
+ bool IsBlocklisted(const std::string& module_name);
private:
std::string MakeCanonical(const std::string& module_path);
@@ -52,7 +53,6 @@
void AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value);
std::string GetKernelCmdline();
- bool IsBlocklisted(const std::string& module_name);
bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 54d559b..0239b68 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_CALLSTACK_H
-#define ANDROID_CALLSTACK_H
+#pragma once
#include <memory>
@@ -27,14 +26,14 @@
#include <sys/types.h>
#if !defined(__APPLE__) && !defined(_WIN32)
-# define WEAKS_AVAILABLE 1
+# define CALLSTACK_WEAKS_AVAILABLE 1
#endif
#ifndef CALLSTACK_WEAK
-# ifdef WEAKS_AVAILABLE
+# ifdef CALLSTACK_WEAKS_AVAILABLE
# define CALLSTACK_WEAK __attribute__((weak))
-# else // !WEAKS_AVAILABLE
+# else // !CALLSTACK_WEAKS_AVAILABLE
# define CALLSTACK_WEAK
-# endif // !WEAKS_AVAILABLE
+# endif // !CALLSTACK_WEAKS_AVAILABLE
#endif // CALLSTACK_WEAK predefined
#ifndef CALLSTACK_ALWAYS_INLINE
@@ -91,7 +90,7 @@
//
// DO NOT USE THESE. They will disappear.
struct StackDeleter {
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
void operator()(CallStack* stack) {
deleteStack(stack);
}
@@ -103,7 +102,7 @@
typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;
// Return current call stack if possible, nullptr otherwise.
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
ALOGW("CallStack::getCurrentInternal not linked, returning null");
@@ -112,13 +111,13 @@
return getCurrentInternal(ignoreDepth);
}
}
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t = 1) {
return CallStackUPtr(nullptr);
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static void CALLSTACK_ALWAYS_INLINE logStack(const char* logtag,
CallStack* stack = getCurrent().get(),
android_LogPriority priority = ANDROID_LOG_DEBUG) {
@@ -135,9 +134,9 @@
android_LogPriority = ANDROID_LOG_DEBUG) {
ALOG(LOG_WARN, logtag, "CallStack::logStackInternal not linked");
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static String8 CALLSTACK_ALWAYS_INLINE
stackToString(const char* prefix = nullptr, const CallStack* stack = getCurrent().get()) {
if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
@@ -146,15 +145,15 @@
return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
}
-#else // !WEAKS_AVAILABLE
+#else // !CALLSTACK_WEAKS_AVAILABLE
static String8 CALLSTACK_ALWAYS_INLINE stackToString(const char* prefix = nullptr,
const CallStack* = getCurrent().get()) {
return String8::format("%s<CallStack package not linked>", (prefix ? prefix : ""));
}
-#endif // !WEAKS_AVAILABLE
+#endif // !CALLSTACK_WEAKS_AVAILABLE
private:
-#ifdef WEAKS_AVAILABLE
+#ifdef CALLSTACK_WEAKS_AVAILABLE
static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
android_LogPriority priority);
@@ -162,13 +161,13 @@
// The deleter is only invoked on non-null pointers. Hence it will never be
// invoked if CallStack is not linked.
static void CALLSTACK_WEAK deleteStack(CallStack* stack);
-#endif // WEAKS_AVAILABLE
+#endif // CALLSTACK_WEAKS_AVAILABLE
Vector<String8> mFrameLines;
};
} // namespace android
+#undef CALLSTACK_WEAKS_AVAILABLE
+#undef CALLSTACK_WEAK
#undef CALLSTACK_ALWAYS_INLINE
-
-#endif // ANDROID_CALLSTACK_H
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
index b4457b1..e87959e 100644
--- a/libvendorsupport/Android.bp
+++ b/libvendorsupport/Android.bp
@@ -34,3 +34,32 @@
"liblog",
],
}
+
+cc_library_headers {
+ name: "libvendorsupport_llndk_headers",
+ host_supported: true,
+ vendor_available: true,
+ recovery_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
+ native_bridge_supported: true,
+
+ export_include_dirs: ["include_llndk"],
+ llndk: {
+ llndk_headers: true,
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ min_sdk_version: "apex_inherit",
+
+ system_shared_libs: [],
+ stl: "none",
+
+ // This header library is used for libc and must be available to any sdk
+ // versions.
+ // Setting sdk_version to the lowest version allows the dependencies.
+ sdk_version: "1",
+}
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
new file mode 100644
index 0000000..b500149
--- /dev/null
+++ b/libvendorsupport/include_llndk/android/llndk-versioning.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+#if defined(__ANDROID_VENDOR__)
+
+// LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
+// NDK, but uses its own versioning of YYYYMM format for vendor builds. The LLNDK symbols are
+// enabled when the vendor api level is equal to or newer than the ro.board.api_level.
+#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
+ _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+ __attribute__((enable_if( \
+ __ANDROID_VENDOR_API__ >= vendor_api_level, \
+ "available in vendor API level " #vendor_api_level " that " \
+ "is newer than the current vendor API level. Guard the API " \
+ "call with '#if (__ANDROID_VENDOR_API__ >= " #vendor_api_level ")'."))) \
+ _Pragma("clang diagnostic pop")
+
+// Use this macro as an `if` statement to call an API that are available to both NDK and LLNDK.
+// This returns true for the vendor modules if the vendor_api_level is less than or equal to the
+// ro.board.api_level.
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ constexpr(__ANDROID_VENDOR_API__ >= vendor_api_level)
+
+#else // __ANDROID_VENDOR__
+
+// __INTRODUCED_IN_LLNDK is for LLNDK only but not for NDK. Ignore this for non-vendor modules.
+// It leaves a no-op annotation for ABI analysis.
+#if !defined(__INTRODUCED_IN_LLNDK)
+#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
+ __attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
+#endif
+
+// For non-vendor modules, API_LEVEL_AT_LEAST is replaced with __builtin_available(sdk_api_level) to
+// guard the API for __INTRODUCED_IN.
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ (__builtin_available(android sdk_api_level, *))
+
+#endif // __ANDROID_VENDOR__
+
+__END_DECLS
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2b29e7d..70bb38a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -247,6 +247,7 @@
write /dev/blkio/background/blkio.bfq.weight 10
write /dev/blkio/blkio.group_idle 0
write /dev/blkio/background/blkio.group_idle 0
+ write /dev/blkio/blkio.prio.class promote-to-rt
write /dev/blkio/background/blkio.prio.class restrict-to-be
restorecon_recursive /mnt
@@ -303,6 +304,9 @@
mkdir /mnt/runtime/full 0755 root root
mkdir /mnt/runtime/full/self 0755 root root
+ # For Pre-reboot Dexopt
+ mkdir /mnt/pre_reboot_dexopt 0755 artd artd
+
# Symlink to keep legacy apps working in multi-user world
symlink /storage/self/primary /mnt/sdcard
symlink /mnt/user/0/primary /mnt/runtime/default/self/primary
@@ -710,7 +714,7 @@
# Start tombstoned early to be able to store tombstones.
mkdir /data/anr 0775 system system encryption=Require
- mkdir /data/tombstones 0771 system system encryption=Require
+ mkdir /data/tombstones 0775 system system encryption=Require
mkdir /data/vendor/tombstones 0771 root root
mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
start tombstoned
@@ -1359,3 +1363,16 @@
write /sys/kernel/mm/lru_gen/enabled 5
on property:persist.device_config.mglru_native.lru_gen_config=all
write /sys/kernel/mm/lru_gen/enabled 7
+
+# Allow other processes to run `snapshotctl` through `init`. This requires
+# `set_prop` permission on `snapshotctl_prop`.
+on property:sys.snapshotctl.map=requested
+ # "root" is needed to talk to gsid and pass its check on uid.
+ # "system" is needed to write to "/dev/socket/snapuserd" to talk to
+ # snapuserd.
+ exec - root root system -- /system/bin/snapshotctl map
+ setprop sys.snapshotctl.map "finished"
+
+on property:sys.snapshotctl.unmap=requested
+ exec - root root system -- /system/bin/snapshotctl unmap
+ setprop sys.snapshotctl.unmap "finished"
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 17d4e31..56cf140 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -257,6 +257,7 @@
switch (mode) {
case AddModulesMode:
if (!m.LoadWithAliases(module, true, module_parameters)) {
+ if (m.IsBlocklisted(module)) continue;
PLOG(ERROR) << "Failed to load module " << module;
rv = EXIT_FAILURE;
}
diff --git a/trusty/metrics/include/trusty/metrics/tipc.h b/trusty/metrics/include/trusty/metrics/tipc.h
index b29628e..e2cf57b 100644
--- a/trusty/metrics/include/trusty/metrics/tipc.h
+++ b/trusty/metrics/include/trusty/metrics/tipc.h
@@ -39,14 +39,17 @@
* repository. They must be kept in sync.
*/
-#define METRICS_PORT "com.android.trusty.metrics"
+#define METRICS_PORT "com.android.trusty.metrics.consumer"
+
+#define UUID_STR_SIZE (37)
/**
* enum metrics_cmd - command identifiers for metrics interface
- * @METRICS_CMD_RESP_BIT: message is a response
- * @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
- * @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
- * @METRICS_CMD_REPORT_CRASH: report an app crash event
+ * @METRICS_CMD_RESP_BIT: message is a response
+ * @METRICS_CMD_REQ_SHIFT: number of bits used by @METRICS_CMD_RESP_BIT
+ * @METRICS_CMD_REPORT_EVENT_DROP: report gaps in the event stream
+ * @METRICS_CMD_REPORT_CRASH: report an app crash event
+ * @METRICS_CMD_REPORT_STORAGE_ERROR: report trusty storage error
*/
enum metrics_cmd {
METRICS_CMD_RESP_BIT = 1,
@@ -54,6 +57,7 @@
METRICS_CMD_REPORT_EVENT_DROP = (1 << METRICS_CMD_REQ_SHIFT),
METRICS_CMD_REPORT_CRASH = (2 << METRICS_CMD_REQ_SHIFT),
+ METRICS_CMD_REPORT_STORAGE_ERROR = (3 << METRICS_CMD_REQ_SHIFT),
};
/**
@@ -90,17 +94,70 @@
/**
* struct metrics_report_crash_req - arguments of %METRICS_CMD_REPORT_CRASH
* requests
- * @app_id_len: length of app ID that follows this structure
+ * @app_id: uuid of the app that crashed
* @crash_reason: architecture-specific code representing the reason for the
* crash
*/
struct metrics_report_crash_req {
- uint32_t app_id_len;
+ char app_id[UUID_STR_SIZE];
uint32_t crash_reason;
} __attribute__((__packed__));
-#define METRICS_MAX_APP_ID_LEN 256
+enum TrustyStorageErrorType {
+ TRUSTY_STORAGE_ERROR_UNKNOWN = 0,
+ TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID = 1,
+ TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH = 2,
+ TRUSTY_STORAGE_ERROR_BLOCK_HEADER_INVALID = 3,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH = 4,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH_RECOVERED = 5,
+ TRUSTY_STORAGE_ERROR_RPMB_COUNTER_READ_FAILURE = 6,
+ TRUSTY_STORAGE_ERROR_RPMB_MAC_MISMATCH = 7,
+ TRUSTY_STORAGE_ERROR_RPMB_ADDR_MISMATCH = 8,
+ TRUSTY_STORAGE_ERROR_RPMB_FAILURE_RESPONSE = 9,
+ TRUSTY_STORAGE_ERROR_RPMB_UNKNOWN = 10,
+ TRUSTY_STORAGE_ERROR_RPMB_SCSI_ERROR = 11,
+ TRUSTY_STORAGE_ERROR_IO_ERROR = 12,
+ TRUSTY_STORAGE_ERROR_PROXY_COMMUNICATION_FAILURE = 13,
+};
-#define METRICS_MAX_MSG_SIZE \
- (sizeof(struct metrics_req) + sizeof(struct metrics_report_crash_req) + \
- METRICS_MAX_APP_ID_LEN)
+enum TrustyFileSystem {
+ TRUSTY_FS_UNKNOWN = 0,
+ TRUSTY_FS_TP = 1,
+ TRUSTY_FS_TD = 2,
+ TRUSTY_FS_TDP = 3,
+ TRUSTY_FS_TDEA = 4,
+ TRUSTY_FS_NSP = 5,
+};
+
+enum TrustyBlockType {
+ TRUSTY_BLOCKTYPE_UNKNOWN = 0,
+ TRUSTY_BLOCKTYPE_FILES_ROOT = 1,
+ TRUSTY_BLOCKTYPE_FREE_ROOT = 2,
+ TRUSTY_BLOCKTYPE_FILES_INTERNAL = 3,
+ TRUSTY_BLOCKTYPE_FREE_INTERNAL = 4,
+ TRUSTY_BLOCKTYPE_FILE_ENTRY = 5,
+ TRUSTY_BLOCKTYPE_FILE_BLOCK_MAP = 6,
+ TRUSTY_BLOCKTYPE_FILE_DATA = 7,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_ROOT = 8,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_FILES_ROOT = 9,
+ TRUSTY_BLOCKTYPE_CHECKPOINT_FREE_ROOT = 10,
+};
+
+struct metrics_report_storage_error_req {
+ enum TrustyStorageErrorType error;
+ char app_id[UUID_STR_SIZE];
+ char client_app_id[UUID_STR_SIZE];
+ uint32_t write;
+ enum TrustyFileSystem file_system;
+ uint64_t file_path_hash;
+ enum TrustyBlockType block_type;
+ uint64_t repair_counter;
+} __attribute__((__packed__));
+
+struct metrics_msg {
+ struct metrics_req req;
+ union {
+ struct metrics_report_crash_req crash_args;
+ struct metrics_report_storage_error_req storage_args;
+ };
+} __attribute__((__packed__));
\ No newline at end of file
diff --git a/trusty/metrics/metrics.cpp b/trusty/metrics/metrics.cpp
index 3ac128a..d2f0b0a 100644
--- a/trusty/metrics/metrics.cpp
+++ b/trusty/metrics/metrics.cpp
@@ -78,9 +78,8 @@
return Error() << "connection to Metrics TA has not been initialized yet";
}
- uint8_t msg[METRICS_MAX_MSG_SIZE];
-
- auto rc = read(metrics_fd_, msg, sizeof(msg));
+ struct metrics_msg metrics_msg;
+ int rc = read(metrics_fd_, &metrics_msg, sizeof(metrics_msg));
if (rc < 0) {
return ErrnoError() << "failed to read metrics message";
}
@@ -89,23 +88,14 @@
if (msg_len < sizeof(metrics_req)) {
return Error() << "message too small: " << rc;
}
- auto req = reinterpret_cast<metrics_req*>(msg);
- size_t offset = sizeof(metrics_req);
+ uint32_t cmd = metrics_msg.req.cmd;
uint32_t status = METRICS_NO_ERROR;
- switch (req->cmd) {
+ switch (cmd) {
case METRICS_CMD_REPORT_CRASH: {
- if (msg_len < offset + sizeof(metrics_report_crash_req)) {
- return Error() << "message too small: " << rc;
- }
- auto crash_args = reinterpret_cast<metrics_report_crash_req*>(msg + offset);
- offset += sizeof(metrics_report_crash_req);
-
- if (msg_len < offset + crash_args->app_id_len) {
- return Error() << "message too small: " << rc;
- }
- auto app_id_ptr = reinterpret_cast<char*>(msg + offset);
- std::string app_id(app_id_ptr, crash_args->app_id_len);
+ struct metrics_report_crash_req crash_args = metrics_msg.crash_args;
+ auto app_id_ptr = crash_args.app_id;
+ std::string app_id(app_id_ptr, UUID_STR_SIZE);
HandleCrash(app_id);
break;
@@ -121,7 +111,7 @@
}
metrics_resp resp = {
- .cmd = req->cmd | METRICS_CMD_RESP_BIT,
+ .cmd = cmd | METRICS_CMD_RESP_BIT,
.status = status,
};