Merge "Remove healthd."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 6391acc..fd62392 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -225,9 +225,6 @@
debuggable: {
cflags: ["-DROOT_POSSIBLE"],
},
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
- },
},
}
@@ -297,12 +294,6 @@
},
test_suites: ["device-tests"],
-
- product_variables: {
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
- },
- },
}
cc_benchmark {
@@ -353,12 +344,6 @@
apex_available: [
"com.android.runtime",
],
-
- product_variables: {
- experimental_mte: {
- cflags: ["-DANDROID_EXPERIMENTAL_MTE"],
- },
- },
}
cc_binary {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index b3e81b0..4f60005 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -40,7 +40,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <cutils/sockets.h>
#include <log/log.h>
@@ -484,7 +483,6 @@
continue;
}
-#ifdef ANDROID_EXPERIMENTAL_MTE
struct iovec iov = {
&info.tagged_addr_ctrl,
sizeof(info.tagged_addr_ctrl),
@@ -493,7 +491,6 @@
reinterpret_cast<void*>(&iov)) == -1) {
info.tagged_addr_ctrl = -1;
}
-#endif
if (thread == g_target_thread) {
// Read the thread's registers along with the rest of the crash info out of the pipe.
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e5af425..7938a61 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -383,7 +383,7 @@
#endif
}
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
static void SetTagCheckingLevelSync() {
HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level))) {
@@ -393,7 +393,7 @@
#endif
TEST_F(CrasherTest, mte_uaf) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
}
@@ -425,12 +425,12 @@
ASSERT_MATCH(result, R"(deallocated by thread .*
#00 pc)");
#else
- GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+ GTEST_SKIP() << "Requires aarch64";
#endif
}
TEST_F(CrasherTest, mte_overflow) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
}
@@ -459,12 +459,12 @@
allocated by thread .*
#00 pc)");
#else
- GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+ GTEST_SKIP() << "Requires aarch64";
#endif
}
TEST_F(CrasherTest, mte_underflow) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
}
@@ -493,12 +493,12 @@
allocated by thread .*
#00 pc)");
#else
- GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+ GTEST_SKIP() << "Requires aarch64";
#endif
}
TEST_F(CrasherTest, mte_multiple_causes) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
}
@@ -547,11 +547,11 @@
// overflows), so we can't match explicitly for an underflow message.
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
#else
- GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+ GTEST_SKIP() << "Requires aarch64";
#endif
}
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
static uintptr_t CreateTagMapping() {
uintptr_t mapping =
reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
@@ -568,7 +568,7 @@
#endif
TEST_F(CrasherTest, mte_tag_dump) {
-#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
}
@@ -596,7 +596,7 @@
01.............0 0000000000000000 0000000000000000 ................
00.............0)");
#else
- GTEST_SKIP() << "Requires aarch64 + ANDROID_EXPERIMENTAL_MTE";
+ GTEST_SKIP() << "Requires aarch64";
#endif
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d7067ca..7826efc 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -35,7 +35,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <bionic/mte_kernel.h>
#include <bionic/reserved_signals.h>
#include <debuggerd/handler.h>
#include <log/log.h>
@@ -370,14 +369,12 @@
return "SEGV_ADIDERR";
case SEGV_ADIPERR:
return "SEGV_ADIPERR";
-#if defined(ANDROID_EXPERIMENTAL_MTE)
case SEGV_MTEAERR:
return "SEGV_MTEAERR";
case SEGV_MTESERR:
return "SEGV_MTESERR";
-#endif
}
- static_assert(NSIGSEGV == SEGV_ADIPERR, "missing SEGV_* si_code");
+ static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
break;
case SIGSYS:
switch (si->si_code) {
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 2088ae3..a72ee07 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,4 +1,4 @@
-dpursell@google.com
+dvander@google.com
+hridya@google.com
enh@google.com
jmgao@google.com
-tomcherry@google.com
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 4601960..2b2a0bf 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -228,6 +228,11 @@
return device->WriteStatus(FastbootResult::FAIL, "Unable to open fastboot HAL");
}
+ //Disable "oem postwipedata userdata" to prevent user wipe oem userdata only.
+ if (args[0] == "oem postwipedata userdata") {
+ return device->WriteStatus(FastbootResult::FAIL, "Unable to do oem postwipedata userdata");
+ }
+
Result ret;
auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
if (!ret_val.isOk()) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index 8addcb6..458a7a1 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -168,6 +168,19 @@
return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
}
+enum {
+ // clang-format off
+ FSCK_SUCCESS = 0,
+ FSCK_ERROR_CORRECTED = 1 << 0,
+ FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
+ FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
+ FSCK_OPERATIONAL_ERROR = 1 << 3,
+ FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
+ FSCK_USER_CANCELLED = 1 << 5,
+ FSCK_SHARED_LIB_ERROR = 1 << 7,
+ // clang-format on
+};
+
static int generate_f2fs_image(const char* fileName, long long partSize,
const std::string& initial_dir, unsigned /* unused */,
unsigned /* unused */, const unsigned fsOptions) {
@@ -216,7 +229,11 @@
std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
"-f", initial_dir.c_str(), fileName, nullptr};
- return exec_cmd(sload_args[0], sload_args.data(), nullptr);
+ ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
+ if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
+ return -1;
+ }
+ return 0;
}
static const struct fs_generator {
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index cbbd3bc..cf353a1 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,3 +1,2 @@
bowgotsai@google.com
dvander@google.com
-tomcherry@google.com
diff --git a/fs_mgr/README.overlayfs.md b/fs_mgr/README.overlayfs.md
index ca782b9..94b2f8c 100644
--- a/fs_mgr/README.overlayfs.md
+++ b/fs_mgr/README.overlayfs.md
@@ -8,8 +8,8 @@
system partition as read-write and then add or modify any number of files
without reflashing the system image, which is efficient for a development cycle.
-Limited memory systems use read-only types of file systems or logical resizable
-Android partitions (LRAPs). These file systems land system partition images
+Limited memory systems use read-only types of file systems or dynamic
+Android partitions (DAPs). These file systems land system partition images
right-sized, and have been deduped at the block level to compress the content.
This means that a remount either isn’t possible, or isn't useful because of
space limitations or support logistics.
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 4c9fd9b..2876094 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1324,6 +1324,7 @@
int error_count = 0;
CheckpointManager checkpoint_manager;
AvbUniquePtr avb_handle(nullptr);
+ bool wiped = false;
bool userdata_mounted = false;
if (fstab->empty()) {
@@ -1457,7 +1458,8 @@
encryptable = status;
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
- attempted_entry.mount_point},
+ attempted_entry.mount_point, wiped ? "true" : "false",
+ attempted_entry.fs_type},
nullptr)) {
LERROR << "Encryption failed";
set_type_property(encryptable);
@@ -1474,7 +1476,7 @@
}
// Mounting failed, understand why and retry.
- bool wiped = partition_wiped(current_entry.blk_device.c_str());
+ wiped = partition_wiped(current_entry.blk_device.c_str());
bool crypt_footer = false;
if (mount_errno != EBUSY && mount_errno != EACCES &&
current_entry.fs_mgr_flags.formattable && wiped) {
@@ -1499,6 +1501,27 @@
} else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
crypt_footer = true;
}
+
+ // EncryptInplace will be used when vdc gives an error or needs to format partitions
+ // other than /data
+ if (should_use_metadata_encryption(current_entry) &&
+ current_entry.mount_point == "/data") {
+
+ // vdc->Format requires "ro.crypto.type" to set an encryption flag
+ encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
+ set_type_property(encryptable);
+
+ if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
+ current_entry.mount_point, "true" /* shouldFormat */,
+ current_entry.fs_type},
+ nullptr)) {
+ LERROR << "Encryption failed";
+ } else {
+ userdata_mounted = true;
+ continue;
+ }
+ }
+
if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
// Let's replay the mount actions.
i = top_idx - 1;
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 1e65587..fdaffbe 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -111,7 +111,8 @@
return true;
}
- static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "none"};
+ static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "erofs",
+ "none"};
if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
return false;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index e32fde0..d36a7f0 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -24,6 +24,7 @@
],
shared_libs: [
"libbase",
+ "libchrome",
"libcutils",
"liblog",
],
@@ -44,7 +45,6 @@
"libz",
],
header_libs: [
- "libchrome",
"libfiemap_headers",
"libstorage_literals_headers",
"libupdate_engine_headers",
@@ -351,6 +351,7 @@
static_libs: [
"libbase",
"libbrotli",
+ "libchrome",
"libcrypto_static",
"libcutils",
"libext2_uuid",
@@ -367,7 +368,6 @@
"libz",
],
header_libs: [
- "libchrome",
"libfiemap_headers",
"libstorage_literals_headers",
"libupdate_engine_headers",
@@ -423,10 +423,11 @@
"libbase",
"libbrotli",
"libcutils_sockets",
- "liblog",
"libdm",
- "libz",
+ "libgflags",
+ "liblog",
"libsnapshot_cow",
+ "libz",
],
}
@@ -440,6 +441,7 @@
system_shared_libs: [],
ramdisk_available: true,
vendor_ramdisk_available: true,
+ recovery_available: true,
}
cc_test {
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 35020f4..a96352a 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -60,6 +60,7 @@
TEST_F(CowTest, ReadWrite) {
CowOptions options;
+ options.cluster_ops = 0;
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -137,6 +138,7 @@
TEST_F(CowTest, CompressGz) {
CowOptions options;
+ options.cluster_ops = 0;
options.compression = "gz";
CowWriter writer(options);
@@ -171,9 +173,74 @@
ASSERT_TRUE(iter->Done());
}
+TEST_F(CowTest, ClusterCompressGz) {
+ CowOptions options;
+ options.compression = "gz";
+ options.cluster_ops = 2;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+
+ std::string data2 = "More data!";
+ data2.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
+
+ ASSERT_TRUE(writer.Finalize());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ StringSink sink;
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowClusterOp);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ sink.Reset();
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->data_length, 41); // compressed!
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data2);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowClusterOp);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
TEST_F(CowTest, CompressTwoBlocks) {
CowOptions options;
options.compression = "gz";
+ options.cluster_ops = 0;
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -216,6 +283,7 @@
TEST_P(CompressionTest, HorribleSink) {
CowOptions options;
options.compression = GetParam();
+ options.cluster_ops = 0;
CowWriter writer(options);
ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -245,6 +313,7 @@
TEST_F(CowTest, GetSize) {
CowOptions options;
+ options.cluster_ops = 0;
CowWriter writer(options);
if (ftruncate(cow_->fd, 0) < 0) {
perror("Fails to set temp file size");
@@ -270,6 +339,7 @@
TEST_F(CowTest, AppendLabelSmall) {
CowOptions options;
+ options.cluster_ops = 0;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
@@ -335,6 +405,7 @@
TEST_F(CowTest, AppendLabelMissing) {
CowOptions options;
+ options.cluster_ops = 0;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
@@ -388,6 +459,7 @@
TEST_F(CowTest, AppendExtendedCorrupted) {
CowOptions options;
+ options.cluster_ops = 0;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
@@ -440,6 +512,7 @@
TEST_F(CowTest, AppendbyLabel) {
CowOptions options;
+ options.cluster_ops = 0;
auto writer = std::make_unique<CowWriter>(options);
ASSERT_TRUE(writer->Initialize(cow_->fd));
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 3fbc5f3..c15a05b 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -222,8 +222,6 @@
LOG(ERROR) << "ops checksum does not match";
return false;
}
- } else {
- LOG(INFO) << "No COW Footer, recovered data";
}
ops_ = ops_buffer;
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 16d9313..4cc0fd3 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -169,7 +169,7 @@
ASSERT_GE(pid, 0);
if (pid == 0) {
std::string arg0 = "/system/bin/snapuserd";
- std::string arg1 = kSnapuserdSocketTest;
+ std::string arg1 = "-socket="s + kSnapuserdSocketTest;
char* const argv[] = {arg0.data(), arg1.data(), nullptr};
ASSERT_GE(execv(arg0.c_str(), argv), 0);
} else {
@@ -239,16 +239,12 @@
ASSERT_TRUE(rnd_fd > 0);
std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
- std::unique_ptr<uint8_t[]> random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
// Fill random data
for (size_t j = 0; j < (size_ / 1_MiB); j++) {
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
true);
- ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
- true);
-
offset += 1_MiB;
}
@@ -280,7 +276,7 @@
size_t blk_random2_replace_start = blk_zero_copy_end;
- ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
+ ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
// Flush operations
ASSERT_TRUE(writer.Finalize());
@@ -292,7 +288,7 @@
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
- memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_2_.get(), size_);
+ memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
}
void CowSnapuserdTest::InitCowDevice() {
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 8535252..c1a5f32 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -433,11 +433,6 @@
}
bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
- // If there isn't room for this op and the cluster end op, end the current cluster
- if (cluster_size_ && op.type != kCowClusterOp &&
- cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
- if (!EmitCluster()) return false;
- }
if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed for writing operation.";
return false;
@@ -449,6 +444,11 @@
if (!WriteRawData(data, size)) return false;
}
AddOperation(op);
+ // If there isn't room for another op and the cluster end op, end the current cluster
+ if (cluster_size_ && op.type != kCowClusterOp &&
+ cluster_size_ < current_cluster_size_ + 2 * sizeof(op)) {
+ if (!EmitCluster()) return false;
+ }
return true;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index fd43cce..22ddfa6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -35,7 +35,7 @@
std::optional<uint64_t> max_blocks;
// Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.
- uint32_t cluster_ops = 0;
+ uint32_t cluster_ops = 200;
};
// Interface for writing to a snapuserd COW. All operations are ordered; merges
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index f8d3cdc..397ff2e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -306,13 +306,17 @@
// Helper function for second stage init to restorecon on the rollback indicator.
static std::string GetGlobalRollbackIndicatorPath();
- // Initiate the transition from first-stage to second-stage snapuserd. This
- // process involves re-creating the dm-user table entries for each device,
- // so that they connect to the new daemon. Once all new tables have been
- // activated, we ask the first-stage daemon to cleanly exit.
- //
- // The caller must pass a function which starts snapuserd.
- bool PerformSecondStageTransition();
+ // Detach dm-user devices from the current snapuserd, and populate
+ // |snapuserd_argv| with the necessary arguments to restart snapuserd
+ // and reattach them.
+ bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);
+
+ // Perform the transition from the selinux stage of snapuserd into the
+ // second-stage of snapuserd. This process involves re-creating the dm-user
+ // table entries for each device, so that they connect to the new daemon.
+ // Once all new tables have been activated, we ask the first-stage daemon
+ // to cleanly exit.
+ bool PerformSecondStageInitTransition();
// ISnapshotManager overrides.
bool BeginUpdate() override;
@@ -626,6 +630,9 @@
// The reverse of MapPartitionWithSnapshot.
bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
+ // Unmap a dm-user device through snapuserd.
+ bool UnmapDmUserDevice(const std::string& snapshot_name);
+
// If there isn't a previous update, return true. |needs_merge| is set to false.
// If there is a previous update but the device has not boot into it, tries to cancel the
// update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
@@ -693,6 +700,19 @@
// returns true.
bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);
+ enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };
+
+ // Initiate the transition from first-stage to second-stage snapuserd. This
+ // process involves re-creating the dm-user table entries for each device,
+ // so that they connect to the new daemon. Once all new tables have been
+ // activated, we ask the first-stage daemon to cleanly exit.
+ //
+ // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will
+ // be populated with a list of snapuserd arguments to pass to execve(). It
+ // is otherwise ignored.
+ bool PerformInitTransition(InitTransition transition,
+ std::vector<std::string>* snapuserd_argv = nullptr);
+
std::string gsid_dir_;
std::string metadata_dir_;
std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index aa9ba6e..1dab361 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -32,8 +32,6 @@
static constexpr char kSnapuserdSocket[] = "snapuserd";
-static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
-
// Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
index e8dbe6e..7941e68 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -50,6 +50,8 @@
static constexpr uint32_t BLOCK_SIZE = 4096;
static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);
+#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d))
+
// This structure represents the kernel COW header.
// All the below fields should be in Little Endian format.
struct disk_header {
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 5ad61f3..453b5c6 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -36,10 +36,30 @@
}
static void usage(void) {
- LOG(ERROR) << "Usage: inspect_cow [-s] <COW_FILE>";
+ LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
+ LOG(ERROR) << "\t -s Run Silent";
+ LOG(ERROR) << "\t -d Attempt to decompress\n";
}
-static bool Inspect(const std::string& path, bool silent) {
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+ public:
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ size_t old_size = stream_.size();
+ stream_.resize(old_size + requested, '\0');
+ *actual = requested;
+ return stream_.data() + old_size;
+ }
+ bool ReturnData(void*, size_t) override { return true; }
+ void Reset() { stream_.clear(); }
+
+ std::string& stream() { return stream_; }
+
+ private:
+ std::string stream_;
+};
+
+static bool Inspect(const std::string& path, bool silent, bool decompress) {
android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
if (fd < 0) {
PLOG(ERROR) << "open failed: " << path;
@@ -76,15 +96,25 @@
}
auto iter = reader.GetOpIter();
+ StringSink sink;
+ bool success = true;
while (!iter->Done()) {
const CowOperation& op = iter->Get();
if (!silent) std::cout << op << "\n";
+ if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+ if (!reader.ReadData(op, &sink)) {
+ std::cerr << "Failed to decompress for :" << op << "\n";
+ success = false;
+ }
+ sink.Reset();
+ }
+
iter->Next();
}
- return true;
+ return success;
}
} // namespace snapshot
@@ -93,11 +123,15 @@
int main(int argc, char** argv) {
int ch;
bool silent = false;
- while ((ch = getopt(argc, argv, "s")) != -1) {
+ bool decompress = false;
+ while ((ch = getopt(argc, argv, "sd")) != -1) {
switch (ch) {
case 's':
silent = true;
break;
+ case 'd':
+ decompress = true;
+ break;
default:
android::snapshot::usage();
}
@@ -109,7 +143,7 @@
return 1;
}
- if (!android::snapshot::Inspect(argv[optind], silent)) {
+ if (!android::snapshot::Inspect(argv[optind], silent, decompress)) {
return 1;
}
return 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 04c3ccc..f0646fc 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1247,6 +1247,10 @@
return false;
}
+ if (status.compression_enabled()) {
+ UnmapDmUserDevice(name);
+ }
+
// Cleanup the base device as well, since it is no longer used. This does
// not block cleanup.
auto base_name = GetBaseDeviceName(name);
@@ -1291,12 +1295,13 @@
return RemoveAllUpdateState(lock, before_cancel);
}
-bool SnapshotManager::PerformSecondStageTransition() {
- LOG(INFO) << "Performing second-stage transition for snapuserd.";
+bool SnapshotManager::PerformInitTransition(InitTransition transition,
+ std::vector<std::string>* snapuserd_argv) {
+ LOG(INFO) << "Performing transition for snapuserd.";
// Don't use EnsuerSnapuserdConnected() because this is called from init,
// and attempting to do so will deadlock.
- if (!snapuserd_client_) {
+ if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {
snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
if (!snapuserd_client_) {
LOG(ERROR) << "Unable to connect to snapuserd";
@@ -1343,6 +1348,9 @@
}
auto misc_name = user_cow_name;
+ if (transition == InitTransition::SELINUX_DETACH) {
+ misc_name += "-selinux";
+ }
DmTable table;
table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
@@ -1378,6 +1386,17 @@
continue;
}
+ if (transition == InitTransition::SELINUX_DETACH) {
+ auto message = misc_name + "," + cow_image_device + "," + backing_device;
+ snapuserd_argv->emplace_back(std::move(message));
+
+ // Do not attempt to connect to the new snapuserd yet, it hasn't
+ // been started. We do however want to wait for the misc device
+ // to have been created.
+ ok_cows++;
+ continue;
+ }
+
uint64_t base_sectors =
snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, backing_device);
if (base_sectors == 0) {
@@ -2048,27 +2067,8 @@
auto& dm = DeviceMapper::Instance();
- auto dm_user_name = GetDmUserCowName(name);
- if (IsCompressionEnabled() && dm.GetState(dm_user_name) != DmDeviceState::INVALID) {
- if (!EnsureSnapuserdConnected()) {
- return false;
- }
- if (!dm.DeleteDeviceIfExists(dm_user_name)) {
- LOG(ERROR) << "Cannot unmap " << dm_user_name;
- return false;
- }
-
- if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
- LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
- return false;
- }
-
- // Ensure the control device is gone so we don't run into ABA problems.
- auto control_device = "/dev/dm-user/" + dm_user_name;
- if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
- LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
- return false;
- }
+ if (IsCompressionEnabled() && !UnmapDmUserDevice(name)) {
+ return false;
}
auto cow_name = GetCowName(name);
@@ -2085,6 +2085,37 @@
return true;
}
+bool SnapshotManager::UnmapDmUserDevice(const std::string& snapshot_name) {
+ auto& dm = DeviceMapper::Instance();
+
+ if (!EnsureSnapuserdConnected()) {
+ return false;
+ }
+
+ auto dm_user_name = GetDmUserCowName(snapshot_name);
+ if (dm.GetState(dm_user_name) == DmDeviceState::INVALID) {
+ return true;
+ }
+
+ if (!dm.DeleteDeviceIfExists(dm_user_name)) {
+ LOG(ERROR) << "Cannot unmap " << dm_user_name;
+ return false;
+ }
+
+ if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
+ LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
+ return false;
+ }
+
+ // Ensure the control device is gone so we don't run into ABA problems.
+ auto control_device = "/dev/dm-user/" + dm_user_name;
+ if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
+ LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
+ return false;
+ }
+ return true;
+}
+
bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {
auto lock = LockExclusive();
if (!lock) return false;
@@ -3311,5 +3342,13 @@
return status.state() != UpdateState::None && status.compression_enabled();
}
+bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
+ return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
+}
+
+bool SnapshotManager::PerformSecondStageInitTransition() {
+ return PerformInitTransition(InitTransition::SECOND_STAGE);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c6a1786..bb44425 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -146,6 +146,7 @@
"base-device",
"test_partition_b",
"test_partition_b-base",
+ "test_partition_b-base",
};
for (const auto& partition : partitions) {
DeleteDevice(partition);
@@ -180,12 +181,22 @@
}
// If |path| is non-null, the partition will be mapped after creation.
- bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
+ bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr,
+ const std::optional<std::string> group = {}) {
TestPartitionOpener opener(fake_super);
auto builder = MetadataBuilder::New(opener, "super", 0);
if (!builder) return false;
- auto partition = builder->AddPartition(name, 0);
+ std::string partition_group = std::string(android::fs_mgr::kDefaultGroup);
+ if (group) {
+ partition_group = *group;
+ }
+ return CreatePartition(builder.get(), name, size, path, partition_group);
+ }
+
+ bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size,
+ std::string* path, const std::string& group) {
+ auto partition = builder->AddPartition(name, group, 0);
if (!partition) return false;
if (!builder->ResizePartition(partition, size)) {
return false;
@@ -194,6 +205,8 @@
// Update the source slot.
auto metadata = builder->Export();
if (!metadata) return false;
+
+ TestPartitionOpener opener(fake_super);
if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
return false;
}
@@ -210,39 +223,54 @@
return CreateLogicalPartition(params, path);
}
- bool MapUpdatePartitions() {
+ AssertionResult MapUpdateSnapshot(const std::string& name,
+ std::unique_ptr<ICowWriter>* writer) {
TestPartitionOpener opener(fake_super);
- auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
- if (!builder) return false;
+ CreateLogicalPartitionParams params{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = &opener,
+ };
- auto metadata = builder->Export();
- if (!metadata) return false;
-
- // Update the destination slot, mark it as updated.
- if (!UpdatePartitionTable(opener, "super", *metadata.get(), 1)) {
- return false;
+ auto result = sm->OpenSnapshotWriter(params, {});
+ if (!result) {
+ return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+ }
+ if (!result->Initialize()) {
+ return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
}
- for (const auto& partition : metadata->partitions) {
- CreateLogicalPartitionParams params = {
- .block_device = fake_super,
- .metadata = metadata.get(),
- .partition = &partition,
- .force_writable = true,
- .timeout_ms = 10s,
- .device_name = GetPartitionName(partition) + "-base",
- };
- std::string ignore_path;
- if (!CreateLogicalPartition(params, &ignore_path)) {
- return false;
- }
+ if (writer) {
+ *writer = std::move(result);
}
- return true;
+ return AssertionSuccess();
+ }
+
+ AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
+ TestPartitionOpener opener(fake_super);
+ CreateLogicalPartitionParams params{
+ .block_device = fake_super,
+ .metadata_slot = 1,
+ .partition_name = name,
+ .timeout_ms = 10s,
+ .partition_opener = &opener,
+ };
+
+ auto result = sm->MapUpdateSnapshot(params, path);
+ if (!result) {
+ return AssertionFailure() << "Cannot open snapshot for writing: " << name;
+ }
+ return AssertionSuccess();
}
AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
AssertionResult res = AssertionSuccess();
if (!(res = DeleteDevice(snapshot))) return res;
+ if (!sm->UnmapDmUserDevice(snapshot)) {
+ return AssertionFailure() << "Cannot delete dm-user device for " << snapshot;
+ }
if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
if (!(res = DeleteDevice(snapshot + "-cow"))) return res;
if (!image_manager_->UnmapImageIfExists(snapshot + "-cow-img")) {
@@ -289,37 +317,55 @@
// Prepare A/B slot for a partition named "test_partition".
AssertionResult PrepareOneSnapshot(uint64_t device_size,
- std::string* out_snap_device = nullptr) {
- std::string base_device, cow_device, snap_device;
- if (!CreatePartition("test_partition_a", device_size)) {
- return AssertionFailure();
+ std::unique_ptr<ICowWriter>* writer = nullptr) {
+ lock_ = nullptr;
+
+ DeltaArchiveManifest manifest;
+
+ auto group = manifest.mutable_dynamic_partition_metadata()->add_groups();
+ group->set_name("group");
+ group->set_size(device_size * 2);
+ group->add_partition_names("test_partition");
+
+ auto pu = manifest.add_partitions();
+ pu->set_partition_name("test_partition");
+ pu->set_estimate_cow_size(device_size);
+ SetSize(pu, device_size);
+
+ auto extent = pu->add_operations()->add_dst_extents();
+ extent->set_start_block(0);
+ if (device_size) {
+ extent->set_num_blocks(device_size / manifest.block_size());
}
- if (!MapUpdatePartitions()) {
- return AssertionFailure();
+
+ TestPartitionOpener opener(fake_super);
+ auto builder = MetadataBuilder::New(opener, "super", 0);
+ if (!builder) {
+ return AssertionFailure() << "Failed to open MetadataBuilder";
}
- if (!dm_.GetDmDevicePathByName("test_partition_b-base", &base_device)) {
- return AssertionFailure();
+ builder->AddGroup("group_a", 16_GiB);
+ builder->AddGroup("group_b", 16_GiB);
+ if (!CreatePartition(builder.get(), "test_partition_a", device_size, nullptr, "group_a")) {
+ return AssertionFailure() << "Failed create test_partition_a";
}
- SnapshotStatus status;
- status.set_name("test_partition_b");
- status.set_device_size(device_size);
- status.set_snapshot_size(device_size);
- status.set_cow_file_size(device_size);
- if (!sm->CreateSnapshot(lock_.get(), &status)) {
- return AssertionFailure();
+
+ if (!sm->CreateUpdateSnapshots(manifest)) {
+ return AssertionFailure() << "Failed to create update snapshots";
}
- if (!CreateCowImage("test_partition_b")) {
- return AssertionFailure();
+
+ if (writer) {
+ auto res = MapUpdateSnapshot("test_partition_b", writer);
+ if (!res) {
+ return res;
+ }
+ } else if (!IsCompressionEnabled()) {
+ std::string ignore;
+ if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
+ return AssertionFailure() << "Failed to map test_partition_b";
+ }
}
- if (!MapCowImage("test_partition_b", 10s, &cow_device)) {
- return AssertionFailure();
- }
- if (!sm->MapSnapshot(lock_.get(), "test_partition_b", base_device, cow_device, 10s,
- &snap_device)) {
- return AssertionFailure();
- }
- if (out_snap_device) {
- *out_snap_device = std::move(snap_device);
+ if (!AcquireLock()) {
+ return AssertionFailure() << "Failed to acquire lock";
}
return AssertionSuccess();
}
@@ -328,20 +374,37 @@
AssertionResult SimulateReboot() {
lock_ = nullptr;
if (!sm->FinishedSnapshotWrites(false)) {
- return AssertionFailure();
+ return AssertionFailure() << "Failed to finish snapshot writes";
}
- if (!dm_.DeleteDevice("test_partition_b")) {
- return AssertionFailure();
+ if (!sm->UnmapUpdateSnapshot("test_partition_b")) {
+ return AssertionFailure() << "Failed to unmap COW for test_partition_b";
}
- if (!DestroyLogicalPartition("test_partition_b-base")) {
- return AssertionFailure();
+ if (!dm_.DeleteDeviceIfExists("test_partition_b")) {
+ return AssertionFailure() << "Failed to delete test_partition_b";
}
- if (!sm->UnmapCowImage("test_partition_b")) {
- return AssertionFailure();
+ if (!dm_.DeleteDeviceIfExists("test_partition_b-base")) {
+ return AssertionFailure() << "Failed to destroy test_partition_b-base";
}
return AssertionSuccess();
}
+ std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(
+ const std::string& slot_suffix = "_a") {
+ auto info = new TestDeviceInfo(fake_super, slot_suffix);
+ return NewManagerForFirstStageMount(info);
+ }
+
+ std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {
+ auto init = SnapshotManager::NewForFirstStageMount(info);
+ if (!init) {
+ return nullptr;
+ }
+ init->SetUeventRegenCallback([](const std::string& device) -> bool {
+ return android::fs_mgr::WaitForFile(device, snapshot_timeout_);
+ });
+ return init;
+ }
+
static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
@@ -439,8 +502,7 @@
TEST_F(SnapshotTest, CleanFirstStageMount) {
// If there's no update in progress, there should be no first-stage mount
// needed.
- TestDeviceInfo* info = new TestDeviceInfo(fake_super);
- auto sm = SnapshotManager::NewForFirstStageMount(info);
+ auto sm = NewManagerForFirstStageMount();
ASSERT_NE(sm, nullptr);
ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
}
@@ -449,8 +511,7 @@
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
// We didn't change the slot, so we shouldn't need snapshots.
- TestDeviceInfo* info = new TestDeviceInfo(fake_super);
- auto sm = SnapshotManager::NewForFirstStageMount(info);
+ auto sm = NewManagerForFirstStageMount();
ASSERT_NE(sm, nullptr);
ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());
@@ -462,32 +523,30 @@
ASSERT_TRUE(AcquireLock());
static const uint64_t kDeviceSize = 1024 * 1024;
- std::string snap_device;
- ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &snap_device));
- std::string test_string = "This is a test string.";
- {
- unique_fd fd(open(snap_device.c_str(), O_RDWR | O_CLOEXEC | O_SYNC));
- ASSERT_GE(fd, 0);
- ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
- }
-
- // Note: we know there is no inner/outer dm device since we didn't request
- // a linear segment.
- DeviceMapper::TargetInfo target;
- ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
- ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+ std::unique_ptr<ICowWriter> writer;
+ ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
// Release the lock.
lock_ = nullptr;
+ std::string test_string = "This is a test string.";
+ test_string.resize(writer->options().block_size);
+ ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
+ ASSERT_TRUE(writer->Finalize());
+ writer = nullptr;
+
// Done updating.
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+ ASSERT_TRUE(sm->UnmapUpdateSnapshot("test_partition_b"));
+
test_device->set_slot_suffix("_b");
+ ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
ASSERT_TRUE(sm->InitiateMerge());
// The device should have been switched to a snapshot-merge target.
+ DeviceMapper::TargetInfo target;
ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
@@ -503,7 +562,7 @@
// Test that we can read back the string we wrote to the snapshot. Note
// that the base device is gone now. |snap_device| contains the correct
// partition.
- unique_fd fd(open(snap_device.c_str(), O_RDONLY | O_CLOEXEC));
+ unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
ASSERT_GE(fd, 0);
std::string buffer(test_string.size(), '\0');
@@ -518,7 +577,7 @@
ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
ASSERT_TRUE(SimulateReboot());
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -547,7 +606,7 @@
FormatFakeSuper();
ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -574,7 +633,7 @@
ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
ASSERT_TRUE(SimulateReboot());
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -899,47 +958,7 @@
return AssertionSuccess();
}
- AssertionResult MapUpdateSnapshot(const std::string& name,
- std::unique_ptr<ICowWriter>* writer) {
- CreateLogicalPartitionParams params{
- .block_device = fake_super,
- .metadata_slot = 1,
- .partition_name = name,
- .timeout_ms = 10s,
- .partition_opener = opener_.get(),
- };
-
- auto result = sm->OpenSnapshotWriter(params, {});
- if (!result) {
- return AssertionFailure() << "Cannot open snapshot for writing: " << name;
- }
- if (!result->Initialize()) {
- return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
- }
-
- if (writer) {
- *writer = std::move(result);
- }
- return AssertionSuccess();
- }
-
- AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {
- CreateLogicalPartitionParams params{
- .block_device = fake_super,
- .metadata_slot = 1,
- .partition_name = name,
- .timeout_ms = 10s,
- .partition_opener = opener_.get(),
- };
-
- auto result = sm->MapUpdateSnapshot(params, path);
- if (!result) {
- return AssertionFailure() << "Cannot open snapshot for writing: " << name;
- }
- return AssertionSuccess();
- }
-
- AssertionResult MapUpdateSnapshot(const std::string& name) {
+ AssertionResult MapOneUpdateSnapshot(const std::string& name) {
if (IsCompressionEnabled()) {
std::unique_ptr<ICowWriter> writer;
return MapUpdateSnapshot(name, &writer);
@@ -983,7 +1002,7 @@
AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {"sys_b", "vnd_b",
"prd_b"}) {
for (const auto& name : names) {
- auto res = MapUpdateSnapshot(name);
+ auto res = MapOneUpdateSnapshot(name);
if (!res) {
return res;
}
@@ -1070,7 +1089,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1202,7 +1221,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1214,7 +1233,7 @@
// Simulate shutting down the device again.
ASSERT_TRUE(UnmapAll());
- init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
+ init = NewManagerForFirstStageMount("_a");
ASSERT_NE(init, nullptr);
ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1251,7 +1270,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1387,8 +1406,8 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- // Normally we should use NewForFirstStageMount, but if so, "gsid.mapped_image.sys_b-cow-img"
- // won't be set.
+ // Normally we should use NewManagerForFirstStageMount, but if so,
+ // "gsid.mapped_image.sys_b-cow-img" won't be set.
auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1495,7 +1514,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1509,7 +1528,7 @@
// Simulate a reboot into recovery.
auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
test_device->set_recovery(true);
- new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ new_sm = NewManagerForFirstStageMount(test_device.release());
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1527,7 +1546,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1541,7 +1560,7 @@
// Simulate a reboot into recovery.
auto test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
test_device->set_recovery(true);
- new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ new_sm = NewManagerForFirstStageMount(test_device.release());
ASSERT_TRUE(new_sm->FinishMergeInRecovery());
@@ -1551,12 +1570,12 @@
// Finish the merge in a normal boot.
test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
- init = SnapshotManager::NewForFirstStageMount(test_device.release());
+ init = NewManagerForFirstStageMount(test_device.release());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
init = nullptr;
test_device = std::make_unique<TestDeviceInfo>(fake_super, "_b");
- new_sm = SnapshotManager::NewForFirstStageMount(test_device.release());
+ new_sm = NewManagerForFirstStageMount(test_device.release());
ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);
}
@@ -1575,7 +1594,7 @@
// Simulate a reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_b");
test_device->set_recovery(true);
- auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+ auto new_sm = NewManagerForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below.
@@ -1600,7 +1619,7 @@
// Simulate a rollback, with reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_a");
test_device->set_recovery(true);
- auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+ auto new_sm = NewManagerForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
@@ -1628,7 +1647,7 @@
// Simulate a reboot into recovery.
auto test_device = new TestDeviceInfo(fake_super, "_b");
test_device->set_recovery(true);
- auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
+ auto new_sm = NewManagerForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
// Manually mount metadata so that we can call GetUpdateState() below.
@@ -1639,7 +1658,7 @@
// Now reboot into new slot.
test_device = new TestDeviceInfo(fake_super, "_b");
- auto init = SnapshotManager::NewForFirstStageMount(test_device);
+ auto init = NewManagerForFirstStageMount(test_device);
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
// Verify that we are on the downgraded build.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
@@ -1685,7 +1704,7 @@
ASSERT_TRUE(UnmapAll());
// After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -1773,7 +1792,7 @@
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
ASSERT_TRUE(UnmapAll());
- auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+ auto init = NewManagerForFirstStageMount("_b");
ASSERT_NE(init, nullptr);
ASSERT_TRUE(init->EnsureSnapuserdConnected());
@@ -1785,7 +1804,7 @@
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
- ASSERT_TRUE(init->PerformSecondStageTransition());
+ ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
// The control device should have been renamed.
ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
@@ -1887,8 +1906,7 @@
ASSERT_TRUE(UnmapAll());
// Simulate reboot. After reboot, init does first stage mount.
- auto init = SnapshotManager::NewForFirstStageMount(
- new TestDeviceInfo(fake_super, flashed_slot_suffix));
+ auto init = NewManagerForFirstStageMount(flashed_slot_suffix);
ASSERT_NE(init, nullptr);
if (flashed_slot && after_merge) {
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 34254a3..573b6a5 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -14,12 +14,11 @@
* limitations under the License.
*/
+#include "snapuserd.h"
+
#include <csignal>
-#include <libsnapshot/snapuserd.h>
#include <libsnapshot/snapuserd_client.h>
-#include <libsnapshot/snapuserd_daemon.h>
-#include <libsnapshot/snapuserd_server.h>
namespace android {
namespace snapshot {
@@ -130,88 +129,126 @@
return true;
}
-/*
- * Read the data of size bytes from a given chunk.
- *
- * Kernel can potentially merge the blocks if the
- * successive chunks are contiguous. For chunk size of 8,
- * there can be 256 disk exceptions; and if
- * all 256 disk exceptions are contiguous, kernel can merge
- * them into a single IO.
- *
- * Since each chunk in the disk exception
- * mapping represents a 4k block, kernel can potentially
- * issue 256*4k = 1M IO in one shot.
- *
- * Even though kernel assumes that the blocks are
- * contiguous, we need to split the 1M IO into 4k chunks
- * as each operation represents 4k and it can either be:
- *
- * 1: Replace operation
- * 2: Copy operation
- * 3: Zero operation
- *
- */
-bool Snapuserd::ReadData(chunk_t chunk, size_t size) {
- size_t read_size = size;
- bool ret = true;
- chunk_t chunk_key = chunk;
+bool Snapuserd::ProcessCowOp(const CowOperation* cow_op) {
+ CHECK(cow_op != nullptr);
- if (!((read_size & (BLOCK_SIZE - 1)) == 0)) {
- SNAP_LOG(ERROR) << "ReadData - unaligned read_size: " << read_size;
- return false;
+ switch (cow_op->type) {
+ case kCowReplaceOp: {
+ return ProcessReplaceOp(cow_op);
+ }
+
+ case kCowZeroOp: {
+ return ProcessZeroOp();
+ }
+
+ case kCowCopyOp: {
+ return ProcessCopyOp(cow_op);
+ }
+
+ default: {
+ SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ }
+ }
+ return false;
+}
+
+int Snapuserd::ReadUnalignedSector(sector_t sector, size_t size,
+ std::map<sector_t, const CowOperation*>::iterator& it) {
+ size_t skip_sector_size = 0;
+
+ SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+ << " Aligned sector: " << it->second;
+
+ if (!ProcessCowOp(it->second)) {
+ SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed";
+ return -1;
}
- while (read_size > 0) {
- const CowOperation* cow_op = chunk_map_[chunk_key];
- CHECK(cow_op != nullptr);
+ int num_sectors_skip = sector - it->first;
- switch (cow_op->type) {
- case kCowReplaceOp: {
- ret = ProcessReplaceOp(cow_op);
- break;
- }
+ if (num_sectors_skip > 0) {
+ skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
+ char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
+ struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
- case kCowZeroOp: {
- ret = ProcessZeroOp();
- break;
- }
+ memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
+ (BLOCK_SIZE - skip_sector_size));
+ }
- case kCowCopyOp: {
- ret = ProcessCopyOp(cow_op);
- break;
- }
+ bufsink_.ResetBufferOffset();
+ return std::min(size, (BLOCK_SIZE - skip_sector_size));
+}
- default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- ret = false;
- break;
- }
+/*
+ * Read the data for a given COW Operation.
+ *
+ * Kernel can issue IO at a sector granularity.
+ * Hence, an IO may end up with reading partial
+ * data from a COW operation or we may also
+ * end up with interspersed request between
+ * two COW operations.
+ *
+ */
+int Snapuserd::ReadData(sector_t sector, size_t size) {
+ /*
+ * chunk_map stores COW operation at 4k granularity.
+ * If the requested IO with the sector falls on the 4k
+ * boundary, then we can read the COW op directly without
+ * any issue.
+ *
+ * However, if the requested sector is not 4K aligned,
+ * then we will have the find the nearest COW operation
+ * and chop the 4K block to fetch the requested sector.
+ */
+ std::map<sector_t, const CowOperation*>::iterator it = chunk_map_.find(sector);
+ if (it == chunk_map_.end()) {
+ it = chunk_map_.lower_bound(sector);
+ if (it != chunk_map_.begin()) {
+ --it;
}
- if (!ret) {
- SNAP_LOG(ERROR) << "ReadData failed for operation: " << cow_op->type;
- return false;
- }
+ /*
+ * If the IO is spanned between two COW operations,
+ * split the IO into two parts:
+ *
+ * 1: Read the first part from the single COW op
+ * 2: Read the second part from the next COW op.
+ *
+ * Ex: Let's say we have a 1024 Bytes IO request.
+ *
+ * 0 COW OP-1 4096 COW OP-2 8192
+ * |******************|*******************|
+ * |*****|*****|
+ * 3584 4608
+ * <- 1024B - >
+ *
+ * We have two COW operations which are 4k blocks.
+ * The IO is requested for 1024 Bytes which are spanned
+ * between two COW operations. We will split this IO
+ * into two parts:
+ *
+ * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
+ * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
+ */
+ return ReadUnalignedSector(sector, size, it);
+ }
+ int num_ops = DIV_ROUND_UP(size, BLOCK_SIZE);
+ while (num_ops) {
+ if (!ProcessCowOp(it->second)) {
+ return -1;
+ }
+ num_ops -= 1;
+ it++;
// Update the buffer offset
bufsink_.UpdateBufferOffset(BLOCK_SIZE);
- read_size -= BLOCK_SIZE;
-
- // Start iterating the chunk incrementally; Since while
- // constructing the metadata, we know that the chunk IDs
- // are contiguous
- chunk_key += 1;
-
- if (cow_op->type == kCowCopyOp) {
- CHECK(read_size == 0);
- }
+ SNAP_LOG(DEBUG) << "ReadData at sector: " << sector << " size: " << size;
}
// Reset the buffer offset
bufsink_.ResetBufferOffset();
- return ret;
+ return size;
}
/*
@@ -262,9 +299,7 @@
if (divresult.quot < vec_.size()) {
size = exceptions_per_area_ * sizeof(struct disk_exception);
- if (read_size > size) {
- return false;
- }
+ CHECK(read_size == size);
void* buffer = bufsink_.GetPayloadBuffer(size);
CHECK(buffer != nullptr);
@@ -330,7 +365,7 @@
if (cow_de->new_chunk != 0) {
merged_ops_cur_iter += 1;
offset += sizeof(struct disk_exception);
- const CowOperation* cow_op = chunk_map_[cow_de->new_chunk];
+ const CowOperation* cow_op = chunk_map_[ChunkToSector(cow_de->new_chunk)];
CHECK(cow_op != nullptr);
CHECK(cow_op->new_block == cow_de->old_chunk);
if (cow_op->type == kCowCopyOp) {
@@ -564,7 +599,7 @@
SNAP_LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
// Store operation pointer.
- chunk_map_[data_chunk_id] = cow_op;
+ chunk_map_[ChunkToSector(data_chunk_id)] = cow_op;
num_ops += 1;
offset += sizeof(struct disk_exception);
cowop_riter_->Next();
@@ -681,6 +716,126 @@
return true;
}
+bool Snapuserd::DmuserWriteRequest() {
+ struct dm_user_header* header = bufsink_.GetHeaderPtr();
+
+ // device mapper has the capability to allow
+ // targets to flush the cache when writes are completed. This
+ // is controlled by each target by a flag "flush_supported".
+ // This flag is set by dm-user. When flush is supported,
+ // a number of zero-length bio's will be submitted to
+ // the target for the purpose of flushing cache. It is the
+ // responsibility of the target driver - which is dm-user in this
+ // case, to remap these bio's to the underlying device. Since,
+ // there is no underlying device for dm-user, this zero length
+ // bio's gets routed to daemon.
+ //
+ // Flush operations are generated post merge by dm-snap by having
+ // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
+ // to flush per se; hence, just respond back with a success message.
+ if (header->sector == 0) {
+ CHECK(header->len == 0);
+ header->type = DM_USER_RESP_SUCCESS;
+ if (!WriteDmUserPayload(0)) {
+ return false;
+ }
+ return true;
+ }
+
+ size_t remaining_size = header->len;
+ size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+ CHECK(read_size == BLOCK_SIZE);
+
+ CHECK(header->sector > 0);
+ chunk_t chunk = SectorToChunk(header->sector);
+ CHECK(chunk_map_.find(header->sector) == chunk_map_.end());
+
+ void* buffer = bufsink_.GetPayloadBuffer(read_size);
+ CHECK(buffer != nullptr);
+ header->type = DM_USER_RESP_SUCCESS;
+
+ if (!ReadDmUserPayload(buffer, read_size)) {
+ SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ }
+
+ if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
+ SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ }
+
+ if (!WriteDmUserPayload(0)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool Snapuserd::DmuserReadRequest() {
+ struct dm_user_header* header = bufsink_.GetHeaderPtr();
+ size_t remaining_size = header->len;
+ loff_t offset = 0;
+ sector_t sector = header->sector;
+ do {
+ size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+
+ int ret = read_size;
+ header->type = DM_USER_RESP_SUCCESS;
+ chunk_t chunk = SectorToChunk(header->sector);
+
+ // Request to sector 0 is always for kernel
+ // representation of COW header. This IO should be only
+ // once during dm-snapshot device creation. We should
+ // never see multiple IO requests. Additionally this IO
+ // will always be a single 4k.
+ if (header->sector == 0) {
+ CHECK(metadata_read_done_ == true);
+ CHECK(read_size == BLOCK_SIZE);
+ ConstructKernelCowHeader();
+ SNAP_LOG(DEBUG) << "Kernel header constructed";
+ } else {
+ if (!offset && (read_size == BLOCK_SIZE) &&
+ chunk_map_.find(header->sector) == chunk_map_.end()) {
+ if (!ReadDiskExceptions(chunk, read_size)) {
+ SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ }
+ } else {
+ chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
+ ret = ReadData(sector + num_sectors_read, read_size);
+ if (ret < 0) {
+ SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ header->type = DM_USER_RESP_ERROR;
+ } else {
+ SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
+ << "Sector: " << header->sector;
+ }
+ }
+ }
+
+ // Daemon will not be terminated if there is any error. We will
+ // just send the error back to dm-user.
+ if (!WriteDmUserPayload(ret)) {
+ return false;
+ }
+
+ remaining_size -= ret;
+ offset += ret;
+ } while (remaining_size > 0);
+
+ return true;
+}
+
bool Snapuserd::Run() {
struct dm_user_header* header = bufsink_.GetHeaderPtr();
@@ -699,122 +854,16 @@
switch (header->type) {
case DM_USER_REQ_MAP_READ: {
- size_t remaining_size = header->len;
- loff_t offset = 0;
- do {
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
- header->type = DM_USER_RESP_SUCCESS;
-
- // Request to sector 0 is always for kernel
- // representation of COW header. This IO should be only
- // once during dm-snapshot device creation. We should
- // never see multiple IO requests. Additionally this IO
- // will always be a single 4k.
- if (header->sector == 0) {
- CHECK(metadata_read_done_ == true);
- CHECK(read_size == BLOCK_SIZE);
- ConstructKernelCowHeader();
- SNAP_LOG(DEBUG) << "Kernel header constructed";
- } else {
- // Convert the sector number to a chunk ID.
- //
- // Check if the chunk ID represents a metadata
- // page. If the chunk ID is not found in the
- // vector, then it points to a metadata page.
- chunk_t chunk = SectorToChunk(header->sector);
-
- if (chunk_map_.find(chunk) == chunk_map_.end()) {
- if (!ReadDiskExceptions(chunk, read_size)) {
- SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- } else {
- SNAP_LOG(DEBUG) << "ReadData: chunk: " << chunk << " len: " << header->len
- << " read_size: " << read_size << " offset: " << offset;
- chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
- if (!ReadData(chunk + num_chunks_read, read_size)) {
- SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
- }
- }
-
- // Daemon will not be terminated if there is any error. We will
- // just send the error back to dm-user.
- if (!WriteDmUserPayload(read_size)) {
- return false;
- }
-
- remaining_size -= read_size;
- offset += read_size;
- } while (remaining_size);
-
+ if (!DmuserReadRequest()) {
+ return false;
+ }
break;
}
case DM_USER_REQ_MAP_WRITE: {
- // device mapper has the capability to allow
- // targets to flush the cache when writes are completed. This
- // is controlled by each target by a flag "flush_supported".
- // This flag is set by dm-user. When flush is supported,
- // a number of zero-length bio's will be submitted to
- // the target for the purpose of flushing cache. It is the
- // responsibility of the target driver - which is dm-user in this
- // case, to remap these bio's to the underlying device. Since,
- // there is no underlying device for dm-user, this zero length
- // bio's gets routed to daemon.
- //
- // Flush operations are generated post merge by dm-snap by having
- // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
- // to flush per se; hence, just respond back with a success message.
- if (header->sector == 0) {
- CHECK(header->len == 0);
- header->type = DM_USER_RESP_SUCCESS;
- if (!WriteDmUserPayload(0)) {
- return false;
- }
- break;
- }
-
- size_t remaining_size = header->len;
- size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
- CHECK(read_size == BLOCK_SIZE);
-
- CHECK(header->sector > 0);
- chunk_t chunk = SectorToChunk(header->sector);
- CHECK(chunk_map_.find(chunk) == chunk_map_.end());
-
- void* buffer = bufsink_.GetPayloadBuffer(read_size);
- CHECK(buffer != nullptr);
- header->type = DM_USER_RESP_SUCCESS;
-
- if (!ReadDmUserPayload(buffer, read_size)) {
- SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- }
-
- if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
- SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
- << "Sector: " << header->sector;
- header->type = DM_USER_RESP_ERROR;
- } else {
- SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
- << "Sector: " << header->sector;
- }
-
- if (!WriteDmUserPayload(0)) {
+ if (!DmuserWriteRequest()) {
return false;
}
-
break;
}
}
@@ -824,18 +873,3 @@
} // namespace snapshot
} // namespace android
-
-int main([[maybe_unused]] int argc, char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
-
- android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
-
- std::string socket = android::snapshot::kSnapuserdSocket;
- if (argc >= 2) {
- socket = argv[1];
- }
- daemon.StartServer(socket);
- daemon.Run();
-
- return 0;
-}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
similarity index 84%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd.h
index eec6d45..c01fee3 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -22,9 +22,9 @@
#include <cstring>
#include <iostream>
#include <limits>
+#include <map>
#include <string>
#include <thread>
-#include <unordered_map>
#include <vector>
#include <android-base/file.h>
@@ -72,6 +72,9 @@
bool IsAttached() const { return ctrl_fd_ >= 0; }
private:
+ bool DmuserReadRequest();
+ bool DmuserWriteRequest();
+
bool ReadDmUserHeader();
bool ReadDmUserPayload(void* buffer, size_t size);
bool WriteDmUserPayload(size_t size);
@@ -79,10 +82,13 @@
bool ReadMetadata();
bool ZerofillDiskExceptions(size_t read_size);
bool ReadDiskExceptions(chunk_t chunk, size_t size);
- bool ReadData(chunk_t chunk, size_t size);
+ int ReadUnalignedSector(sector_t sector, size_t size,
+ std::map<sector_t, const CowOperation*>::iterator& it);
+ int ReadData(sector_t sector, size_t size);
bool IsChunkIdMetadata(chunk_t chunk);
chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
+ bool ProcessCowOp(const CowOperation* cow_op);
bool ProcessReplaceOp(const CowOperation* cow_op);
bool ProcessCopyOp(const CowOperation* cow_op);
bool ProcessZeroOp();
@@ -94,6 +100,7 @@
bool ProcessMergeComplete(chunk_t chunk, void* buffer);
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
+ bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SIZE - 1)) == 0); }
std::string cow_device_;
std::string backing_store_device_;
@@ -116,9 +123,15 @@
// mapping of old-chunk to new-chunk
std::vector<std::unique_ptr<uint8_t[]>> vec_;
- // Key - Chunk ID
+ // Key - Sector
// Value - cow operation
- std::unordered_map<chunk_t, const CowOperation*> chunk_map_;
+ //
+ // chunk_map stores the pseudo mapping of sector
+ // to COW operations. Each COW op is 4k; however,
+ // we can get a read request which are as small
+ // as 512 bytes. Hence, we need to binary search
+ // in the chunk_map to find the nearest COW op.
+ std::map<sector_t, const CowOperation*> chunk_map_;
bool metadata_read_done_ = false;
BufferSink bufsink_;
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
index 4c8fa57..7fa01b7 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -14,17 +14,44 @@
* limitations under the License.
*/
+#include "snapuserd_daemon.h"
+
#include <android-base/logging.h>
-#include <libsnapshot/snapuserd_daemon.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/snapuserd_client.h>
+
+#include "snapuserd_server.h"
+
+DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
+DEFINE_bool(no_socket, false,
+ "If true, no socket is used. Each additional argument is an INIT message.");
namespace android {
namespace snapshot {
-bool Daemon::StartServer(const std::string& socketname) {
- if (!server_.Start(socketname)) {
- LOG(ERROR) << "Snapuserd daemon failed to start...";
- exit(EXIT_FAILURE);
+bool Daemon::StartServer(int argc, char** argv) {
+ int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ if (!FLAGS_no_socket) {
+ return server_.Start(FLAGS_socket);
}
+
+ for (int i = arg_start; i < argc; i++) {
+ auto parts = android::base::Split(argv[i], ",");
+ if (parts.size() != 3) {
+ LOG(ERROR) << "Malformed message, expected three sub-arguments.";
+ return false;
+ }
+ auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
+ if (!handler || !server_.StartHandler(handler)) {
+ return false;
+ }
+ }
+
+ // Skip the accept() call to avoid spurious log spam. The server will still
+ // run until all handlers have completed.
+ server_.SetTerminating();
return true;
}
@@ -89,3 +116,17 @@
} // namespace snapshot
} // namespace android
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+ if (!daemon.StartServer(argc, argv)) {
+ LOG(ERROR) << "Snapuserd daemon failed to start.";
+ exit(EXIT_FAILURE);
+ }
+ daemon.Run();
+
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd_daemon.h
similarity index 91%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd_daemon.h
index c6779b8..f8afac5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.h
@@ -16,7 +16,10 @@
#include <poll.h>
-#include <libsnapshot/snapuserd_server.h>
+#include <string>
+#include <vector>
+
+#include "snapuserd_server.h"
namespace android {
namespace snapshot {
@@ -32,7 +35,7 @@
return instance;
}
- bool StartServer(const std::string& socketname);
+ bool StartServer(int argc, char** argv);
void Run();
void Interrupt();
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 7a5cead..38abaec 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -26,8 +26,9 @@
#include <unistd.h>
#include <android-base/logging.h>
-#include <libsnapshot/snapuserd.h>
-#include <libsnapshot/snapuserd_server.h>
+
+#include "snapuserd.h"
+#include "snapuserd_server.h"
namespace android {
namespace snapshot {
@@ -76,9 +77,8 @@
JoinAllThreads();
}
-const std::string& DmUserHandler::GetMiscName() const {
- return snapuserd_->GetMiscName();
-}
+DmUserHandler::DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
+ : snapuserd_(std::move(snapuserd)), misc_name_(snapuserd_->GetMiscName()) {}
bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0));
@@ -115,7 +115,7 @@
switch (op) {
case DaemonOperations::INIT: {
// Message format:
- // init,<misc_name>,<cow_device_path>,<control_device>
+ // init,<misc_name>,<cow_device_path>,<backing_device>
//
// Reads the metadata and send the number of sectors
if (out.size() != 4) {
@@ -123,24 +123,12 @@
return Sendmsg(fd, "fail");
}
- auto snapuserd = std::make_unique<Snapuserd>(out[1], out[2], out[3]);
- if (!snapuserd->InitCowDevice()) {
- LOG(ERROR) << "Failed to initialize Snapuserd";
+ auto handler = AddHandler(out[1], out[2], out[3]);
+ if (!handler) {
return Sendmsg(fd, "fail");
}
- std::string retval = "success," + std::to_string(snapuserd->GetNumSectors());
-
- auto handler = std::make_unique<DmUserHandler>(std::move(snapuserd));
- {
- std::lock_guard<std::mutex> lock(lock_);
- if (FindHandler(&lock, out[1]) != dm_users_.end()) {
- LOG(ERROR) << "Handler already exists: " << out[1];
- return Sendmsg(fd, "fail");
- }
- dm_users_.push_back(std::move(handler));
- }
-
+ auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
return Sendmsg(fd, retval);
}
case DaemonOperations::START: {
@@ -159,15 +147,13 @@
LOG(ERROR) << "Could not find handler: " << out[1];
return Sendmsg(fd, "fail");
}
- if ((*iter)->snapuserd()->IsAttached()) {
+ if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
LOG(ERROR) << "Tried to re-attach control device: " << out[1];
return Sendmsg(fd, "fail");
}
- if (!((*iter)->snapuserd()->InitBackingAndControlDevice())) {
- LOG(ERROR) << "Failed to initialize control device: " << out[1];
+ if (!StartHandler(*iter)) {
return Sendmsg(fd, "fail");
}
- (*iter)->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, *iter));
return Sendmsg(fd, "success");
}
case DaemonOperations::STOP: {
@@ -198,7 +184,7 @@
LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
- if (!RemoveHandler(out[1], true)) {
+ if (!RemoveAndJoinHandler(out[1])) {
return Sendmsg(fd, "fail");
}
return Sendmsg(fd, "success");
@@ -216,20 +202,38 @@
}
void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName();
+ LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
while (!StopRequested()) {
if (!handler->snapuserd()->Run()) {
- LOG(INFO) << "Snapuserd: Thread terminating";
break;
}
}
- LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName();
+ auto misc_name = handler->misc_name();
+ LOG(INFO) << "Handler thread about to exit: " << misc_name;
- // If the main thread called RemoveHandler, the handler was already removed
- // from within the lock, and calling RemoveHandler again has no effect.
- RemoveHandler(handler->GetMiscName(), false);
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, handler->misc_name());
+ if (iter == dm_users_.end()) {
+ // RemoveAndJoinHandler() already removed us from the list, and is
+ // now waiting on a join(), so just return.
+ LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
+ return;
+ }
+
+ LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
+
+ if (handler->snapuserd()->IsAttached()) {
+ handler->thread().detach();
+ }
+
+ // Important: free resources within the lock. This ensures that if
+ // WaitForDelete() is called, the handler is either in the list, or
+ // it's not and its resources are guaranteed to be freed.
+ handler->FreeResources();
+ }
}
bool SnapuserdServer::Start(const std::string& socketname) {
@@ -339,19 +343,52 @@
SetTerminating();
}
+std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
+ const std::string& cow_device_path,
+ const std::string& backing_device) {
+ auto snapuserd = std::make_unique<Snapuserd>(misc_name, cow_device_path, backing_device);
+ if (!snapuserd->InitCowDevice()) {
+ LOG(ERROR) << "Failed to initialize Snapuserd";
+ return nullptr;
+ }
+
+ auto handler = std::make_shared<DmUserHandler>(std::move(snapuserd));
+ {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (FindHandler(&lock, misc_name) != dm_users_.end()) {
+ LOG(ERROR) << "Handler already exists: " << misc_name;
+ return nullptr;
+ }
+ dm_users_.push_back(handler);
+ }
+ return handler;
+}
+
+bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
+ CHECK(!handler->snapuserd()->IsAttached());
+
+ if (!handler->snapuserd()->InitBackingAndControlDevice()) {
+ LOG(ERROR) << "Failed to initialize control device: " << handler->misc_name();
+ return false;
+ }
+
+ handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
+ return true;
+}
+
auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
const std::string& misc_name) -> HandlerList::iterator {
CHECK(proof_of_lock);
for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
- if ((*iter)->GetMiscName() == misc_name) {
+ if ((*iter)->misc_name() == misc_name) {
return iter;
}
}
return dm_users_.end();
}
-bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) {
+bool SnapuserdServer::RemoveAndJoinHandler(const std::string& misc_name) {
std::shared_ptr<DmUserHandler> handler;
{
std::lock_guard<std::mutex> lock(lock_);
@@ -366,10 +403,8 @@
}
auto& th = handler->thread();
- if (th.joinable() && wait) {
+ if (th.joinable()) {
th.join();
- } else if (handler->snapuserd()->IsAttached()) {
- th.detach();
}
return true;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
similarity index 84%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd_server.h
index 1491aac..7cbc2de 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -28,7 +28,7 @@
#include <vector>
#include <android-base/unique_fd.h>
-#include <libsnapshot/snapuserd.h>
+#include "snapuserd.h"
namespace android {
namespace snapshot {
@@ -46,18 +46,19 @@
};
class DmUserHandler {
- private:
- std::thread thread_;
- std::unique_ptr<Snapuserd> snapuserd_;
-
public:
- explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
- : snapuserd_(std::move(snapuserd)) {}
+ explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd);
+ void FreeResources() { snapuserd_ = nullptr; }
const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
std::thread& thread() { return thread_; }
- const std::string& GetMiscName() const;
+ const std::string& misc_name() const { return misc_name_; }
+
+ private:
+ std::thread thread_;
+ std::unique_ptr<Snapuserd> snapuserd_;
+ std::string misc_name_;
};
class Stoppable {
@@ -71,8 +72,9 @@
bool StopRequested() {
// checks if value in future object is available
- if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
+ if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout) {
return false;
+ }
return true;
}
// Request the thread to stop by setting value in promise object
@@ -98,12 +100,11 @@
bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
void ShutdownThreads();
- bool RemoveHandler(const std::string& control_device, bool wait);
+ bool RemoveAndJoinHandler(const std::string& control_device);
DaemonOperations Resolveop(std::string& input);
std::string GetDaemonStatus();
void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
- void SetTerminating() { terminating_ = true; }
bool IsTerminating() { return terminating_; }
void RunThread(std::shared_ptr<DmUserHandler> handler);
@@ -120,6 +121,13 @@
bool Start(const std::string& socketname);
bool Run();
void Interrupt();
+
+ std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
+ const std::string& cow_device_path,
+ const std::string& backing_device);
+ bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
+
+ void SetTerminating() { terminating_ = true; }
};
} // namespace snapshot
diff --git a/init/Android.bp b/init/Android.bp
index 19ba21b..cd295cf 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -60,6 +60,7 @@
"selabel.cpp",
"selinux.cpp",
"sigchld_handler.cpp",
+ "snapuserd_transition.cpp",
"switch_root.cpp",
"uevent_listener.cpp",
"ueventd.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index ac31ef1..561d641 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -57,6 +57,8 @@
reboot_utils.cpp \
selabel.cpp \
selinux.cpp \
+ service_utils.cpp \
+ snapuserd_transition.cpp \
switch_root.cpp \
uevent_listener.cpp \
util.cpp \
@@ -75,13 +77,22 @@
adb_debug.prop \
# Set up the directories that first stage init mounts on.
-LOCAL_POST_INSTALL_CMD := mkdir -p \
- $(TARGET_RAMDISK_OUT)/debug_ramdisk \
- $(TARGET_RAMDISK_OUT)/dev \
- $(TARGET_RAMDISK_OUT)/mnt \
- $(TARGET_RAMDISK_OUT)/proc \
- $(TARGET_RAMDISK_OUT)/second_stage_resources \
- $(TARGET_RAMDISK_OUT)/sys \
+
+my_ramdisk_dirs := \
+ debug_ramdisk \
+ dev \
+ metadata \
+ mnt \
+ proc \
+ second_stage_resources \
+ sys \
+
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(my_ramdisk_dirs))
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
+ LOCAL_POST_INSTALL_CMD += $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(my_ramdisk_dirs))
+endif
+
+my_ramdisk_dirs :=
LOCAL_STATIC_LIBRARIES := \
libc++fs \
diff --git a/init/OWNERS b/init/OWNERS
index babbe4d..9e70e7d 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1 @@
-tomcherry@google.com
+dvander@google.com
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 4363f3c..2a76620 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -13,6 +13,16 @@
uevent_socket_rcvbuf_size 16M
Sets the uevent socket rcvbuf_size to 16 megabytes.
+## Importing configuration files
+--------------------------------
+Ueventd reads /system/etc/ueventd.rc, all other files are imported via the `import` command, which
+takes the format of
+
+ import <path>
+This command parses an ueventd config file, extending the current configuration. If _path_ is a
+directory, each file in the directory is parsed as a config file. It is not recursive, nested
+directories will not be parsed. Imported files are parsed after the current file has been parsed.
+
## /dev
----
Ueventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the
@@ -32,7 +42,7 @@
The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
lines take the format of
- devname mode uid gid
+ devname mode uid gid [options]
For example
/dev/null 0666 root root
@@ -70,7 +80,7 @@
certain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script
and a line that begins with `/sys`. These lines take the format of
- nodename attr mode uid gid
+ nodename attr mode uid gid [options]
For example
/sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system
@@ -78,7 +88,15 @@
attribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and
its group set to `system`.
-Note that `*` matches as a wildcard and can be used anywhere in a path.
+## Path matching
+----------------
+The path for a `/dev` or `/sys` entry can contain a `*` anywhere in the path.
+1. If the only `*` appears at the end of the string or if the _options_ parameter is set to
+`no_fnm_pathname`, ueventd matches the entry by `fnmatch(entry_path, incoming_path, 0)`
+2. Otherwise, ueventd matches the entry by `fnmatch(entry_path, incoming_path, FNM_PATHNAME)`
+
+See the [man page for fnmatch](https://www.man7.org/linux/man-pages/man3/fnmatch.3.html) for more
+details.
## Firmware loading
----------------
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index 79fe4ec..ec39ce0 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#pragma once
+
#include <memory>
#include <set>
#include <string>
diff --git a/init/devices.cpp b/init/devices.cpp
index 3513bc9..ce6298a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -124,8 +124,15 @@
return true;
}
-Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
- : name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
+Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,
+ bool no_fnm_pathname)
+ : name_(name),
+ perm_(perm),
+ uid_(uid),
+ gid_(gid),
+ prefix_(false),
+ wildcard_(false),
+ no_fnm_pathname_(no_fnm_pathname) {
// Set 'prefix_' or 'wildcard_' based on the below cases:
//
// 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
@@ -136,7 +143,6 @@
//
// 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
// with FNM_PATHNAME to compare 'name' to a given path.
-
auto wildcard_position = name_.find('*');
if (wildcard_position != std::string::npos) {
if (wildcard_position == name_.length() - 1) {
@@ -150,7 +156,8 @@
bool Permissions::Match(const std::string& path) const {
if (prefix_) return StartsWith(path, name_);
- if (wildcard_) return fnmatch(name_.c_str(), path.c_str(), FNM_PATHNAME) == 0;
+ if (wildcard_)
+ return fnmatch(name_.c_str(), path.c_str(), no_fnm_pathname_ ? 0 : FNM_PATHNAME) == 0;
return path == name_;
}
diff --git a/init/devices.h b/init/devices.h
index 05d64da..d70d746 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -38,7 +38,7 @@
public:
friend void TestPermissions(const Permissions& expected, const Permissions& test);
- Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid);
+ Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid, bool no_fnm_pathname);
bool Match(const std::string& path) const;
@@ -56,6 +56,7 @@
gid_t gid_;
bool prefix_;
bool wildcard_;
+ bool no_fnm_pathname_;
};
class SysfsPermissions : public Permissions {
@@ -63,8 +64,8 @@
friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);
SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,
- gid_t gid)
- : Permissions(name, perm, uid, gid), attribute_(attribute) {}
+ gid_t gid, bool no_fnm_pathname)
+ : Permissions(name, perm, uid, gid, no_fnm_pathname), attribute_(attribute) {}
bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;
void SetPermissions(const std::string& path) const;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index c408bc1..e7bac68 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -221,7 +221,7 @@
TEST(device_handler, DevPermissionsMatchNormal) {
// Basic from ueventd.rc
// /dev/null 0666 root root
- Permissions permissions("/dev/null", 0666, 0, 0);
+ Permissions permissions("/dev/null", 0666, 0, 0, false);
EXPECT_TRUE(permissions.Match("/dev/null"));
EXPECT_FALSE(permissions.Match("/dev/nullsuffix"));
EXPECT_FALSE(permissions.Match("/dev/nul"));
@@ -233,7 +233,7 @@
TEST(device_handler, DevPermissionsMatchPrefix) {
// Prefix from ueventd.rc
// /dev/dri/* 0666 root graphics
- Permissions permissions("/dev/dri/*", 0666, 0, 1000);
+ Permissions permissions("/dev/dri/*", 0666, 0, 1000, false);
EXPECT_TRUE(permissions.Match("/dev/dri/some_dri_device"));
EXPECT_TRUE(permissions.Match("/dev/dri/some_other_dri_device"));
EXPECT_TRUE(permissions.Match("/dev/dri/"));
@@ -246,7 +246,7 @@
TEST(device_handler, DevPermissionsMatchWildcard) {
// Wildcard example
// /dev/device*name 0666 root graphics
- Permissions permissions("/dev/device*name", 0666, 0, 1000);
+ Permissions permissions("/dev/device*name", 0666, 0, 1000, false);
EXPECT_TRUE(permissions.Match("/dev/devicename"));
EXPECT_TRUE(permissions.Match("/dev/device123name"));
EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
@@ -260,13 +260,31 @@
TEST(device_handler, DevPermissionsMatchWildcardPrefix) {
// Wildcard+Prefix example
// /dev/device*name* 0666 root graphics
- Permissions permissions("/dev/device*name*", 0666, 0, 1000);
+ Permissions permissions("/dev/device*name*", 0666, 0, 1000, false);
EXPECT_TRUE(permissions.Match("/dev/devicename"));
EXPECT_TRUE(permissions.Match("/dev/device123name"));
EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
// FNM_PATHNAME doesn't match '/' with *
EXPECT_FALSE(permissions.Match("/dev/device123name/something"));
+ EXPECT_FALSE(permissions.Match("/dev/device/1/2/3name/something"));
+ EXPECT_FALSE(permissions.Match("/dev/deviceame"));
+ EXPECT_EQ(0666U, permissions.perm());
+ EXPECT_EQ(0U, permissions.uid());
+ EXPECT_EQ(1000U, permissions.gid());
+}
+
+TEST(device_handler, DevPermissionsMatchWildcardPrefix_NoFnmPathName) {
+ // Wildcard+Prefix example with no_fnm_pathname
+ // /dev/device*name* 0666 root graphics
+ Permissions permissions("/dev/device*name*", 0666, 0, 1000, true);
+ EXPECT_TRUE(permissions.Match("/dev/devicename"));
+ EXPECT_TRUE(permissions.Match("/dev/device123name"));
+ EXPECT_TRUE(permissions.Match("/dev/deviceabcname"));
+ EXPECT_TRUE(permissions.Match("/dev/device123namesomething"));
+ // With NoFnmPathName, the below matches, unlike DevPermissionsMatchWildcardPrefix.
+ EXPECT_TRUE(permissions.Match("/dev/device123name/something"));
+ EXPECT_TRUE(permissions.Match("/dev/device/1/2/3name/something"));
EXPECT_FALSE(permissions.Match("/dev/deviceame"));
EXPECT_EQ(0666U, permissions.perm());
EXPECT_EQ(0U, permissions.uid());
@@ -275,7 +293,8 @@
TEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {
// /sys/devices/virtual/input/input* enable 0660 root input
- SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001);
+ SysfsPermissions permissions("/sys/devices/virtual/input/input*", "enable", 0660, 0, 1001,
+ false);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/input0", "input"));
EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/virtual/input/not_input0", "input"));
EXPECT_EQ(0660U, permissions.perm());
@@ -285,7 +304,7 @@
TEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {
// /sys/class/input/event* enable 0660 root input
- SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001);
+ SysfsPermissions permissions("/sys/class/input/event*", "enable", 0660, 0, 1001, false);
EXPECT_TRUE(permissions.MatchWithSubsystem(
"/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0", "input"));
EXPECT_FALSE(permissions.MatchWithSubsystem(
@@ -299,7 +318,7 @@
TEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {
// /sys/bus/i2c/devices/i2c-* enable 0660 root input
- SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001);
+ SysfsPermissions permissions("/sys/bus/i2c/devices/i2c-*", "enable", 0660, 0, 1001, false);
EXPECT_TRUE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/i2c-5", "i2c"));
EXPECT_FALSE(permissions.MatchWithSubsystem("/sys/devices/soc.0/f9967000.i2c/not-i2c", "i2c"));
EXPECT_FALSE(
diff --git a/init/firmware_handler_test.cpp b/init/firmware_handler_test.cpp
index 7bb603c..5124a6f 100644
--- a/init/firmware_handler_test.cpp
+++ b/init/firmware_handler_test.cpp
@@ -79,6 +79,8 @@
}
int HandleAbort(int argc, char** argv) {
+ // Since this is an expected failure, disable debuggerd to not generate a tombstone.
+ signal(SIGABRT, SIG_DFL);
abort();
return 0;
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 83a32e7..6954c03 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -42,6 +42,7 @@
#include "first_stage_mount.h"
#include "reboot_utils.h"
#include "second_stage_resources.h"
+#include "snapuserd_transition.h"
#include "switch_root.h"
#include "util.h"
@@ -90,6 +91,12 @@
}
}
}
+ } else if (de->d_type == DT_REG) {
+ // Do not free snapuserd if we will need the ramdisk copy during the
+ // selinux transition.
+ if (de->d_name == "snapuserd"s && IsFirstStageSnapuserdRunning()) {
+ continue;
+ }
}
unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);
}
@@ -99,34 +106,6 @@
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
-// Move e2fsck before switching root, so that it is available at the same path
-// after switching root.
-void PrepareSwitchRoot() {
- constexpr const char* src = "/system/bin/e2fsck";
- constexpr const char* dst = "/first_stage_ramdisk/system/bin/e2fsck";
-
- if (access(dst, X_OK) == 0) {
- LOG(INFO) << dst << " already exists and it can be executed";
- return;
- }
-
- if (access(src, F_OK) != 0) {
- PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
- return;
- }
-
- auto dst_dir = android::base::Dirname(dst);
- std::error_code ec;
- if (!fs::create_directories(dst_dir, ec) && !!ec) {
- LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
- }
- if (rename(src, dst) != 0) {
- PLOG(FATAL) << "Cannot move " << src << " to " << dst
- << ". Either install e2fsck.ramdisk so that it is at the correct place (" << dst
- << "), or make ramdisk writable";
- }
-}
-
} // namespace
std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -327,7 +306,6 @@
if (ForceNormalBoot(cmdline)) {
mkdir("/first_stage_ramdisk", 0755);
- PrepareSwitchRoot();
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index a0511cc..7c46281 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -44,6 +44,7 @@
#include "block_dev_initializer.h"
#include "devices.h"
+#include "snapuserd_transition.h"
#include "switch_root.h"
#include "uevent.h"
#include "uevent_listener.h"
@@ -87,6 +88,7 @@
protected:
bool InitRequiredDevices(std::set<std::string> devices);
bool CreateLogicalPartitions();
+ bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm);
bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
@@ -109,6 +111,7 @@
bool need_dm_verity_;
bool dsu_not_on_userdata_ = false;
+ bool use_snapuserd_ = false;
Fstab fstab_;
// The super path is only set after InitDevices, and is invalid before.
@@ -338,21 +341,7 @@
return false;
}
if (sm->NeedSnapshotsInFirstStageMount()) {
- // When COW images are present for snapshots, they are stored on
- // the data partition.
- if (!InitRequiredDevices({"userdata"})) {
- return false;
- }
- sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
- if (android::base::StartsWith(device, "/dev/block/dm-")) {
- return block_dev_init_.InitDmDevice(device);
- }
- if (android::base::StartsWith(device, "/dev/dm-user/")) {
- return block_dev_init_.InitDmUser(android::base::Basename(device));
- }
- return block_dev_init_.InitDevices({device});
- });
- return sm->CreateLogicalAndSnapshotPartitions(super_path_);
+ return CreateSnapshotPartitions(sm.get());
}
}
@@ -367,6 +356,37 @@
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
+bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) {
+ // When COW images are present for snapshots, they are stored on
+ // the data partition.
+ if (!InitRequiredDevices({"userdata"})) {
+ return false;
+ }
+
+ use_snapuserd_ = sm->IsSnapuserdRequired();
+ if (use_snapuserd_) {
+ LaunchFirstStageSnapuserd();
+ }
+
+ sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
+ if (android::base::StartsWith(device, "/dev/block/dm-")) {
+ return block_dev_init_.InitDmDevice(device);
+ }
+ if (android::base::StartsWith(device, "/dev/dm-user/")) {
+ return block_dev_init_.InitDmUser(android::base::Basename(device));
+ }
+ return block_dev_init_.InitDevices({device});
+ });
+ if (!sm->CreateLogicalAndSnapshotPartitions(super_path_)) {
+ return false;
+ }
+
+ if (use_snapuserd_) {
+ CleanupSnapuserdSocket();
+ }
+ return true;
+}
+
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end) {
// Sets end to begin + 1, so we can just return on failure below.
@@ -466,6 +486,10 @@
if (system_partition == fstab_.end()) return true;
+ if (use_snapuserd_) {
+ SaveRamdiskPathToSnapuserd();
+ }
+
if (MountPartition(system_partition, false /* erase_same_mounts */)) {
if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
LOG(ERROR) << "check_most_at_once forbidden on external media";
diff --git a/init/init.cpp b/init/init.cpp
index 1d0a9dc..ca2d5da 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -79,6 +79,7 @@
#include "service.h"
#include "service_parser.h"
#include "sigchld_handler.h"
+#include "snapuserd_transition.h"
#include "subcontext.h"
#include "system/core/init/property_service.pb.h"
#include "util.h"
@@ -266,12 +267,10 @@
if (shutdown_state.do_shutdown()) {
LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
UnwindMainThreadStack();
- DumpShutdownDebugInformation();
}
if (IsShuttingDown()) {
LOG(ERROR) << "sys.powerctl set while init is already shutting down";
UnwindMainThreadStack();
- DumpShutdownDebugInformation();
}
}
@@ -743,10 +742,15 @@
return {};
}
svc->Start();
+ svc->SetShutdownCritical();
- if (!sm->PerformSecondStageTransition()) {
+ if (!sm->PerformSecondStageInitTransition()) {
LOG(FATAL) << "Failed to transition snapuserd to second-stage";
}
+
+ if (auto pid = GetSnapuserdFirstStagePid()) {
+ KillFirstStageSnapuserd(pid.value());
+ }
return {};
}
@@ -760,7 +764,7 @@
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
- InitSecondStageLogging(argv);
+ InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// Update $PATH in the case the second stage init is newer than first stage init, where it is
@@ -874,6 +878,21 @@
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);
+ /*
+ * For debug builds of S launching devices, init mounts debugfs for
+ * enabling vendor debug data collection setup at boot time. Init will unmount it on
+ * boot-complete after vendor code has performed the required initializations
+ * during boot. Dumpstate will then mount debugfs in order to read data
+ * from the same using the dumpstate HAL during bugreport creation.
+ * Dumpstate will also unmount debugfs after bugreport creation.
+ * first_api_level comparison is done here instead
+ * of init.rc since init.rc parser does not support >/< operators.
+ */
+ auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0);
+ bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ auto mount_debugfs = (is_debuggable && (api_level >= 31)) ? "1" : "0";
+ SetProperty("init.mount_debugfs", mount_debugfs);
+
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
diff --git a/init/init_test.cpp b/init/init_test.cpp
index fa65740..8550ec8 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -17,6 +17,7 @@
#include <functional>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <gtest/gtest.h>
@@ -268,6 +269,17 @@
ASSERT_EQ(1u, parser.parse_error_count());
}
+class TestCaseLogger : public ::testing::EmptyTestEventListener {
+ void OnTestStart(const ::testing::TestInfo& test_info) override {
+#ifdef __ANDROID__
+ LOG(INFO) << "===== " << test_info.test_suite_name() << "::" << test_info.name() << " ("
+ << test_info.file() << ":" << test_info.line() << ")";
+#else
+ UNUSED(test_info);
+#endif
+ }
+};
+
} // namespace init
} // namespace android
@@ -284,5 +296,6 @@
}
testing::InitGoogleTest(&argc, argv);
+ testing::UnitTest::GetInstance()->listeners().Append(new android::init::TestCaseLogger());
return RUN_ALL_TESTS();
}
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 59cc140..ec48cde 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -115,22 +115,29 @@
return {};
}
dirent* entry;
+ std::vector<std::string> entries;
+
while ((entry = readdir(dir.get())) != nullptr) {
if (entry->d_name[0] == '.') continue;
if (entry->d_type == DT_DIR) {
- const std::string apex_path = from_dir + "/" + entry->d_name;
- const auto apex_manifest = GetApexManifest(apex_path);
- if (!apex_manifest.ok()) {
- LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
- continue;
- }
- const std::string mount_path = to_dir + "/" + apex_manifest->name();
- if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
- return result;
- }
- on_activate(apex_path, *apex_manifest);
+ entries.push_back(entry->d_name);
}
}
+
+ std::sort(entries.begin(), entries.end());
+ for (const auto& name : entries) {
+ const std::string apex_path = from_dir + "/" + name;
+ const auto apex_manifest = GetApexManifest(apex_path);
+ if (!apex_manifest.ok()) {
+ LOG(ERROR) << apex_path << " is not an APEX directory: " << apex_manifest.error();
+ continue;
+ }
+ const std::string mount_path = to_dir + "/" + apex_manifest->name();
+ if (auto result = MountDir(apex_path, mount_path); !result.ok()) {
+ return result;
+ }
+ on_activate(apex_path, *apex_manifest);
+ }
return {};
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index e71c386..ce67386 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -890,6 +890,69 @@
}
}
+// If the ro.product.cpu.abilist* properties have not been explicitly
+// set, derive them from ro.${partition}.product.cpu.abilist* properties.
+static void property_initialize_ro_cpu_abilist() {
+ // From high to low priority.
+ const char* kAbilistSources[] = {
+ "product",
+ "odm",
+ "vendor",
+ "system",
+ };
+ const std::string EMPTY = "";
+ const char* kAbilistProp = "ro.product.cpu.abilist";
+ const char* kAbilist32Prop = "ro.product.cpu.abilist32";
+ const char* kAbilist64Prop = "ro.product.cpu.abilist64";
+
+ // If the properties are defined explicitly, just use them.
+ if (GetProperty(kAbilistProp, EMPTY) != EMPTY) {
+ return;
+ }
+
+ // Find the first source defining these properties by order.
+ std::string abilist32_prop_val;
+ std::string abilist64_prop_val;
+ for (const auto& source : kAbilistSources) {
+ const auto abilist32_prop = std::string("ro.") + source + ".product.cpu.abilist32";
+ const auto abilist64_prop = std::string("ro.") + source + ".product.cpu.abilist64";
+ abilist32_prop_val = GetProperty(abilist32_prop, EMPTY);
+ abilist64_prop_val = GetProperty(abilist64_prop, EMPTY);
+ // The properties could be empty on 32-bit-only or 64-bit-only devices,
+ // but we cannot identify a property is empty or undefined by GetProperty().
+ // So, we assume both of these 2 properties are empty as undefined.
+ if (abilist32_prop_val != EMPTY || abilist64_prop_val != EMPTY) {
+ break;
+ }
+ }
+
+ // Merge ABI lists for ro.product.cpu.abilist
+ auto abilist_prop_val = abilist64_prop_val;
+ if (abilist32_prop_val != EMPTY) {
+ if (abilist_prop_val != EMPTY) {
+ abilist_prop_val += ",";
+ }
+ abilist_prop_val += abilist32_prop_val;
+ }
+
+ // Set these properties
+ const std::pair<const char*, const std::string&> set_prop_list[] = {
+ {kAbilistProp, abilist_prop_val},
+ {kAbilist32Prop, abilist32_prop_val},
+ {kAbilist64Prop, abilist64_prop_val},
+ };
+ for (const auto& [prop, prop_val] : set_prop_list) {
+ LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'";
+
+ std::string error;
+ uint32_t res = PropertySet(prop, prop_val, &error);
+ if (res != PROP_SUCCESS) {
+ LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error
+ << ")";
+ }
+ }
+}
+
void PropertyLoadBootDefaults() {
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
@@ -957,8 +1020,6 @@
load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
load_properties_from_partition("product", /* support_legacy_path_until */ 30);
- load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
-
if (access(kDebugRamdiskProp, R_OK) == 0) {
LOG(INFO) << "Loading " << kDebugRamdiskProp;
load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
@@ -974,6 +1035,7 @@
property_initialize_ro_product_props();
property_derive_build_fingerprint();
+ property_initialize_ro_cpu_abilist();
update_sys_usb_config();
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 49baf9e..e3aaa38 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -655,6 +655,7 @@
if (do_shutdown_animation) {
SetProperty("service.bootanim.exit", "0");
+ SetProperty("service.bootanim.progress", "0");
// Could be in the middle of animation. Stop and start so that it can pick
// up the right mode.
boot_anim->Stop();
@@ -853,7 +854,7 @@
sub_reason = "apex";
return result;
}
- if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP)) {
+ if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP).ok()) {
sub_reason = "ns_switch";
return Error() << "Failed to switch to bootstrap namespace";
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index f03ca6b..0336936 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -45,7 +45,7 @@
// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
// have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
// compile the SEPolicy. /system contains the SEPolicy compiler, secilc, and it is used by the
-// LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
// That function contains even more documentation with the specific implementation details of how
// the SEPolicy is compiled if needed.
@@ -74,6 +74,7 @@
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
#include "reboot_utils.h"
+#include "snapuserd_transition.h"
#include "util.h"
using namespace std::string_literals;
@@ -298,7 +299,12 @@
return access(plat_policy_cil_file, R_OK) != -1;
}
-bool LoadSplitPolicy() {
+struct PolicyFile {
+ unique_fd fd;
+ std::string path;
+};
+
+bool OpenSplitPolicy(PolicyFile* policy_file) {
// IMPLEMENTATION NOTE: Split policy consists of three CIL files:
// * platform -- policy needed due to logic contained in the system image,
// * non-platform -- policy needed due to logic contained in the vendor image,
@@ -325,10 +331,8 @@
if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
if (fd != -1) {
- if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << precompiled_sepolicy_file;
- return false;
- }
+ policy_file->fd = std::move(fd);
+ policy_file->path = std::move(precompiled_sepolicy_file);
return true;
}
}
@@ -446,34 +450,39 @@
}
unlink(compiled_sepolicy);
- LOG(INFO) << "Loading compiled SELinux policy";
- if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) < 0) {
- LOG(ERROR) << "Failed to load SELinux policy from " << compiled_sepolicy;
- return false;
- }
-
+ policy_file->fd = std::move(compiled_sepolicy_fd);
+ policy_file->path = compiled_sepolicy;
return true;
}
-bool LoadMonolithicPolicy() {
- LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
- if (selinux_android_load_policy() < 0) {
- PLOG(ERROR) << "Failed to load monolithic SELinux policy";
+bool OpenMonolithicPolicy(PolicyFile* policy_file) {
+ static constexpr char kSepolicyFile[] = "/sepolicy";
+
+ LOG(VERBOSE) << "Opening SELinux policy from monolithic file";
+ policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (policy_file->fd < 0) {
+ PLOG(ERROR) << "Failed to open monolithic SELinux policy";
return false;
}
+ policy_file->path = kSepolicyFile;
return true;
}
-bool LoadPolicy() {
- return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
-}
+void ReadPolicy(std::string* policy) {
+ PolicyFile policy_file;
-void SelinuxInitialize() {
- LOG(INFO) << "Loading SELinux policy";
- if (!LoadPolicy()) {
- LOG(FATAL) << "Unable to load SELinux policy";
+ bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
+ : OpenMonolithicPolicy(&policy_file);
+ if (!ok) {
+ LOG(FATAL) << "Unable to open SELinux policy";
}
+ if (!android::base::ReadFdToString(policy_file.fd, policy)) {
+ PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;
+ }
+}
+
+void SelinuxSetEnforcement() {
bool kernel_enforcing = (security_getenforce() == 1);
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
@@ -670,6 +679,30 @@
}
}
+static void LoadSelinuxPolicy(std::string& policy) {
+ LOG(INFO) << "Loading SELinux policy";
+
+ set_selinuxmnt("/sys/fs/selinux");
+ if (security_load_policy(policy.data(), policy.size()) < 0) {
+ PLOG(FATAL) << "SELinux: Could not load policy";
+ }
+}
+
+// The SELinux setup process is carefully orchestrated around snapuserd. Policy
+// must be loaded off dynamic partitions, and during an OTA, those partitions
+// cannot be read without snapuserd. But, with kernel-privileged snapuserd
+// running, loading the policy will immediately trigger audits.
+//
+// We use a five-step process to address this:
+// (1) Read the policy into a string, with snapuserd running.
+// (2) Rewrite the snapshot device-mapper tables, to generate new dm-user
+// devices and to flush I/O.
+// (3) Kill snapuserd, which no longer has any dm-user devices to attach to.
+// (4) Load the sepolicy and issue critical restorecons in /dev, carefully
+// avoiding anything that would read from /system.
+// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
+//
+// After this sequence, it is safe to enable enforcing mode and continue booting.
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
@@ -682,9 +715,31 @@
MountMissingSystemPartitions();
- // Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
- SelinuxInitialize();
+
+ LOG(INFO) << "Opening SELinux policy";
+
+ // Read the policy before potentially killing snapuserd.
+ std::string policy;
+ ReadPolicy(&policy);
+
+ auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
+ if (snapuserd_helper) {
+ // Kill the old snapused to avoid audit messages. After this we cannot
+ // read from /system (or other dynamic partitions) until we call
+ // FinishTransition().
+ snapuserd_helper->StartTransition();
+ }
+
+ LoadSelinuxPolicy(policy);
+
+ if (snapuserd_helper) {
+ // Before enforcing, finish the pending snapuserd transition.
+ snapuserd_helper->FinishTransition();
+ snapuserd_helper = nullptr;
+ }
+
+ SelinuxSetEnforcement();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
diff --git a/init/service.cpp b/init/service.cpp
index 766eb5d..d84dcd4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -72,12 +72,12 @@
if (getcon(&raw_con) == -1) {
return Error() << "Could not get security context";
}
- std::unique_ptr<char> mycon(raw_con);
+ std::unique_ptr<char, decltype(&freecon)> mycon(raw_con, freecon);
if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {
return Error() << "Could not get file context";
}
- std::unique_ptr<char> filecon(raw_filecon);
+ std::unique_ptr<char, decltype(&freecon)> filecon(raw_filecon, freecon);
char* new_con = nullptr;
int rc = security_compute_create(mycon.get(), filecon.get(),
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
new file mode 100644
index 0000000..19b5c57
--- /dev/null
+++ b/init/snapuserd_transition.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "snapuserd_transition.h"
+
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapuserd_client.h>
+#include <private/android_filesystem_config.h>
+#include <selinux/android.h>
+
+#include "block_dev_initializer.h"
+#include "service_utils.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+using namespace std::string_literals;
+
+using android::base::unique_fd;
+using android::snapshot::SnapshotManager;
+using android::snapshot::SnapuserdClient;
+
+static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
+static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
+static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
+static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
+
+void LaunchFirstStageSnapuserd() {
+ SocketDescriptor socket_desc;
+ socket_desc.name = android::snapshot::kSnapuserdSocket;
+ socket_desc.type = SOCK_STREAM;
+ socket_desc.perm = 0660;
+ socket_desc.uid = AID_SYSTEM;
+ socket_desc.gid = AID_SYSTEM;
+
+ // We specify a label here even though it technically is not needed. During
+ // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,
+ // we bypass the socket entirely.
+ auto socket = socket_desc.Create(kSnapuserdSocketLabel);
+ if (!socket.ok()) {
+ LOG(FATAL) << "Could not create snapuserd socket: " << socket.error();
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "Cannot launch snapuserd; fork failed";
+ }
+ if (pid == 0) {
+ socket->Publish();
+ char arg0[] = "/system/bin/snapuserd";
+ char* const argv[] = {arg0, nullptr};
+ if (execv(arg0, argv) < 0) {
+ PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
+ }
+ _exit(127);
+ }
+
+ setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
+
+ LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+}
+
+std::optional<pid_t> GetSnapuserdFirstStagePid() {
+ const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
+ if (!pid_str) {
+ return {};
+ }
+
+ int pid = 0;
+ if (!android::base::ParseInt(pid_str, &pid)) {
+ LOG(FATAL) << "Could not parse pid in environment, " << kSnapuserdFirstStagePidVar << "="
+ << pid_str;
+ }
+ return {pid};
+}
+
+static void RelabelLink(const std::string& link) {
+ selinux_android_restorecon(link.c_str(), 0);
+
+ std::string path;
+ if (android::base::Readlink(link, &path)) {
+ selinux_android_restorecon(path.c_str(), 0);
+ }
+}
+
+static void RelabelDeviceMapper() {
+ selinux_android_restorecon("/dev/device-mapper", 0);
+
+ std::error_code ec;
+ for (auto& iter : std::filesystem::directory_iterator("/dev/block", ec)) {
+ const auto& path = iter.path();
+ if (android::base::StartsWith(path.string(), "/dev/block/dm-")) {
+ selinux_android_restorecon(path.string().c_str(), 0);
+ }
+ }
+}
+
+static std::optional<int> GetRamdiskSnapuserdFd() {
+ const char* fd_str = getenv(kSnapuserdFirstStageFdVar);
+ if (!fd_str) {
+ return {};
+ }
+
+ int fd;
+ if (!android::base::ParseInt(fd_str, &fd)) {
+ LOG(FATAL) << "Could not parse fd in environment, " << kSnapuserdFirstStageFdVar << "="
+ << fd_str;
+ }
+ return {fd};
+}
+
+void RestoreconRamdiskSnapuserd(int fd) {
+ if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {
+ PLOG(FATAL) << "fsetxattr snapuserd failed";
+ }
+}
+
+SnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)
+ : sm_(std::move(sm)), old_pid_(old_pid) {
+ // Only dm-user device names change during transitions, so the other
+ // devices are expected to be present.
+ sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {
+ if (android::base::StartsWith(device, "/dev/dm-user/")) {
+ return block_dev_init_.InitDmUser(android::base::Basename(device));
+ }
+ return true;
+ });
+}
+
+void SnapuserdSelinuxHelper::StartTransition() {
+ LOG(INFO) << "Starting SELinux transition of snapuserd";
+
+ // The restorecon path reads from /system etc, so make sure any reads have
+ // been cached before proceeding.
+ auto handle = selinux_android_file_context_handle();
+ if (!handle) {
+ LOG(FATAL) << "Could not create SELinux file context handle";
+ }
+ selinux_android_set_sehandle(handle);
+
+ // We cannot access /system after the transition, so make sure init is
+ // pinned in memory.
+ if (mlockall(MCL_CURRENT) < 0) {
+ LOG(FATAL) << "mlockall failed";
+ }
+
+ argv_.emplace_back("snapuserd");
+ argv_.emplace_back("-no_socket");
+ if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
+ LOG(FATAL) << "Could not perform selinux transition";
+ }
+
+ // Make sure the process is gone so we don't have any selinux audits.
+ KillFirstStageSnapuserd(old_pid_);
+}
+
+void SnapuserdSelinuxHelper::FinishTransition() {
+ RelabelLink("/dev/block/by-name/super");
+ RelabelDeviceMapper();
+
+ selinux_android_restorecon("/dev/null", 0);
+ selinux_android_restorecon("/dev/urandom", 0);
+ selinux_android_restorecon("/dev/kmsg", 0);
+ selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
+
+ RelaunchFirstStageSnapuserd();
+
+ if (munlockall() < 0) {
+ PLOG(ERROR) << "munlockall failed";
+ }
+}
+
+void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
+ auto fd = GetRamdiskSnapuserdFd();
+ if (!fd) {
+ LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
+ }
+ unsetenv(kSnapuserdFirstStageFdVar);
+
+ RestoreconRamdiskSnapuserd(fd.value());
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ PLOG(FATAL) << "Fork to relaunch snapuserd failed";
+ }
+ if (pid > 0) {
+ // We don't need the descriptor anymore, and it should be closed to
+ // avoid leaking into subprocesses.
+ close(fd.value());
+
+ setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
+
+ LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
+ return;
+ }
+
+ // Make sure the descriptor is gone after we exec.
+ if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {
+ PLOG(FATAL) << "fcntl FD_CLOEXEC failed for snapuserd fd";
+ }
+
+ std::vector<char*> argv;
+ for (auto& arg : argv_) {
+ argv.emplace_back(arg.data());
+ }
+ argv.emplace_back(nullptr);
+
+ int rv = syscall(SYS_execveat, fd.value(), "", reinterpret_cast<char* const*>(argv.data()),
+ nullptr, AT_EMPTY_PATH);
+ if (rv < 0) {
+ PLOG(FATAL) << "Failed to execveat() snapuserd";
+ }
+}
+
+std::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {
+ if (IsRecoveryMode()) {
+ return nullptr;
+ }
+
+ auto old_pid = GetSnapuserdFirstStagePid();
+ if (!old_pid) {
+ return nullptr;
+ }
+
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ LOG(FATAL) << "Unable to create SnapshotManager";
+ }
+ return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());
+}
+
+void KillFirstStageSnapuserd(pid_t pid) {
+ if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
+ LOG(ERROR) << "Kill snapuserd pid failed: " << pid;
+ } else {
+ LOG(INFO) << "Sent SIGTERM to snapuserd process " << pid;
+ }
+}
+
+void CleanupSnapuserdSocket() {
+ auto socket_path = ANDROID_SOCKET_DIR "/"s + android::snapshot::kSnapuserdSocket;
+ if (access(socket_path.c_str(), F_OK) != 0) {
+ return;
+ }
+
+ // Tell the daemon to stop accepting connections and to gracefully exit
+ // once all outstanding handlers have terminated.
+ if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {
+ client->DetachSnapuserd();
+ }
+
+ // Unlink the socket so we can create it again in second-stage.
+ if (unlink(socket_path.c_str()) < 0) {
+ PLOG(FATAL) << "unlink " << socket_path << " failed";
+ }
+}
+
+void SaveRamdiskPathToSnapuserd() {
+ int fd = open(kSnapuserdPath, O_PATH);
+ if (fd < 0) {
+ PLOG(FATAL) << "Unable to open snapuserd: " << kSnapuserdPath;
+ }
+
+ auto value = std::to_string(fd);
+ if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {
+ PLOG(FATAL) << "setenv failed: " << kSnapuserdFirstStageFdVar << "=" << value;
+ }
+}
+
+bool IsFirstStageSnapuserdRunning() {
+ return GetSnapuserdFirstStagePid().has_value();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
new file mode 100644
index 0000000..a5ab652
--- /dev/null
+++ b/init/snapuserd_transition.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 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/types.h>
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <libsnapshot/snapshot.h>
+
+#include "block_dev_initializer.h"
+
+namespace android {
+namespace init {
+
+// Fork and exec a new copy of snapuserd.
+void LaunchFirstStageSnapuserd();
+
+class SnapuserdSelinuxHelper final {
+ using SnapshotManager = android::snapshot::SnapshotManager;
+
+ public:
+ SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid);
+
+ void StartTransition();
+ void FinishTransition();
+
+ // Return a helper for facilitating the selinux transition of snapuserd.
+ // If snapuserd is not in use, null is returned. StartTransition() should
+ // be called after reading policy. FinishTransition() should be called
+ // after loading policy. In between, no reads of /system or other dynamic
+ // partitions are possible.
+ static std::unique_ptr<SnapuserdSelinuxHelper> CreateIfNeeded();
+
+ private:
+ void RelaunchFirstStageSnapuserd();
+ void ExecSnapuserd();
+
+ std::unique_ptr<SnapshotManager> sm_;
+ BlockDevInitializer block_dev_init_;
+ pid_t old_pid_;
+ std::vector<std::string> argv_;
+};
+
+// Remove /dev/socket/snapuserd. This ensures that (1) the existing snapuserd
+// will receive no new requests, and (2) the next copy we transition to can
+// own the socket.
+void CleanupSnapuserdSocket();
+
+// Kill an instance of snapuserd given a pid.
+void KillFirstStageSnapuserd(pid_t pid);
+
+// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
+// used to later execveat() snapuserd.
+void SaveRamdiskPathToSnapuserd();
+
+// Returns true if first-stage snapuserd is running.
+bool IsFirstStageSnapuserdRunning();
+
+// Return the pid of the first-stage instances of snapuserd, if it was started.
+std::optional<pid_t> GetSnapuserdFirstStagePid();
+
+// Save an open fd to /system/bin (in the ramdisk) into an environment. This is
+// used to later execveat() snapuserd.
+void SaveRamdiskPathToSnapuserd();
+
+// Returns true if first-stage snapuserd is running.
+bool IsFirstStageSnapuserdRunning();
+
+} // namespace init
+} // namespace android
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ee765a7..da1f455 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -202,6 +202,8 @@
// For RecoverAfterAbort
auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {
+ // Since this is an expected failure, disable debuggerd to not generate a tombstone.
+ signal(SIGABRT, SIG_DFL);
return Error() << std::string(4097, 'f');
};
auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 54659c5..923d769 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -283,12 +283,7 @@
std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;
- // Keep the current product name base configuration so we remain backwards compatible and
- // allow it to override everything.
- auto hardware = android::base::GetProperty("ro.hardware", "");
-
- auto ueventd_configuration = ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc",
- "/odm/ueventd.rc", "/ueventd." + hardware + ".rc"});
+ auto ueventd_configuration = ParseConfig("/system/etc/ueventd.rc");
uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
std::move(ueventd_configuration.dev_permissions),
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 09dce44..2605158 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -21,6 +21,7 @@
#include <android-base/parseint.h>
+#include "import_parser.h"
#include "keyword_map.h"
#include "parser.h"
@@ -33,12 +34,12 @@
std::vector<SysfsPermissions>* out_sysfs_permissions,
std::vector<Permissions>* out_dev_permissions) {
bool is_sysfs = out_sysfs_permissions != nullptr;
- if (is_sysfs && args.size() != 5) {
- return Error() << "/sys/ lines must have 5 entries";
+ if (is_sysfs && !(args.size() == 5 || args.size() == 6)) {
+ return Error() << "/sys/ lines must have 5 or 6 entries";
}
- if (!is_sysfs && args.size() != 4) {
- return Error() << "/dev/ lines must have 4 entries";
+ if (!is_sysfs && !(args.size() == 4 || args.size() == 5)) {
+ return Error() << "/dev/ lines must have 4 or 5 entries";
}
auto it = args.begin();
@@ -69,10 +70,19 @@
}
gid_t gid = grp->gr_gid;
+ bool no_fnm_pathname = false;
+ if (it != args.end()) {
+ std::string& flags = *it++;
+ if (flags != "no_fnm_pathname") {
+ return Error() << "invalid option '" << flags << "', only no_fnm_pathname is supported";
+ }
+ no_fnm_pathname = true;
+ }
+
if (is_sysfs) {
- out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid);
+ out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid, no_fnm_pathname);
} else {
- out_dev_permissions->emplace_back(name, perm, uid, gid);
+ out_dev_permissions->emplace_back(name, perm, uid, gid, no_fnm_pathname);
}
return {};
}
@@ -220,10 +230,11 @@
return {};
}
-UeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {
+UeventdConfiguration ParseConfig(const std::string& config) {
Parser parser;
UeventdConfiguration ueventd_configuration;
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
parser.AddSectionParser("subsystem",
std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));
@@ -249,9 +260,7 @@
std::bind(ParseEnabledDisabledLine, _1,
&ueventd_configuration.enable_parallel_restorecon));
- for (const auto& config : configs) {
- parser.ParseConfig(config);
- }
+ parser.ParseConfig(config);
return ueventd_configuration;
}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index eaafa5a..2672626 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -36,7 +36,7 @@
bool enable_parallel_restorecon = false;
};
-UeventdConfiguration ParseConfig(const std::vector<std::string>& configs);
+UeventdConfiguration ParseConfig(const std::string& config);
} // namespace init
} // namespace android
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 172ba0b..b604c53 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -104,21 +104,21 @@
/dev/graphics/* 0660 root graphics
/dev/*/test 0660 root system
-/sys/devices/platform/trusty.* trusty_version 0440 root log
-/sys/devices/virtual/input/input enable 0660 root input
-/sys/devices/virtual/*/input poll_delay 0660 root input
+/sys/devices/platform/trusty.* trusty_version 0440 root log
+/sys/devices/virtual/input/input enable 0660 root input
+/sys/devices/virtual/*/input poll_delay 0660 root input no_fnm_pathname
)";
auto permissions = std::vector<Permissions>{
- {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
- {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
- {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+ {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
+ {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
+ {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
};
auto sysfs_permissions = std::vector<SysfsPermissions>{
- {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
- {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
- {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+ {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
+ {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
+ {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
};
TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
@@ -240,7 +240,7 @@
dirname /dev/graphics
/dev/*/test 0660 root system
-/sys/devices/virtual/*/input poll_delay 0660 root input
+/sys/devices/virtual/*/input poll_delay 0660 root input no_fnm_pathname
firmware_directories /more
external_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh
@@ -259,15 +259,15 @@
{"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
auto permissions = std::vector<Permissions>{
- {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM},
- {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS},
- {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM},
+ {"/dev/rtc0", 0640, AID_SYSTEM, AID_SYSTEM, false},
+ {"/dev/graphics/*", 0660, AID_ROOT, AID_GRAPHICS, false},
+ {"/dev/*/test", 0660, AID_ROOT, AID_SYSTEM, false},
};
auto sysfs_permissions = std::vector<SysfsPermissions>{
- {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG},
- {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT},
- {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT},
+ {"/sys/devices/platform/trusty.*", "trusty_version", 0440, AID_ROOT, AID_LOG, false},
+ {"/sys/devices/virtual/input/input", "enable", 0660, AID_ROOT, AID_INPUT, false},
+ {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
};
auto firmware_directories = std::vector<std::string>{
@@ -299,6 +299,7 @@
/sys/devices/platform/trusty.* trusty_version badmode root log
/sys/devices/platform/trusty.* trusty_version 0440 baduidbad log
/sys/devices/platform/trusty.* trusty_version 0440 root baduidbad
+/sys/devices/platform/trusty.* trusty_version 0440 root root bad_option
uevent_socket_rcvbuf_size blah
diff --git a/init/util.cpp b/init/util.cpp
index aec3173..255434a 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,9 +30,7 @@
#include <time.h>
#include <unistd.h>
-#include <mutex>
#include <thread>
-#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -724,50 +722,5 @@
return access("/system/bin/recovery", F_OK) == 0;
}
-// TODO(b/155203339): remove this
-// Devices in the lab seem to be stuck during shutdown, but the logs don't capture the last actions
-// before shutdown started, so we record those lines, ignoring requests to shutdown, and replay them
-// if we identify that the device is stuck.
-constexpr size_t kRecordedLogsSize = 30;
-std::string recorded_logs[kRecordedLogsSize];
-size_t recorded_log_position = 0;
-std::mutex recorded_logs_lock;
-
-void InitSecondStageLogging(char** argv) {
- SetFatalRebootTarget();
- auto second_stage_logger = [](android::base::LogId log_id, android::base::LogSeverity severity,
- const char* tag, const char* file, unsigned int line,
- const char* message) {
- // We only store logs for init, not its children, and only if they're not related to
- // sys.powerctl.
- if (getpid() == 1 && strstr(message, "sys.powerctl") == nullptr) {
- auto lock = std::lock_guard{recorded_logs_lock};
- recorded_logs[recorded_log_position++] = message;
- if (recorded_log_position == kRecordedLogsSize) {
- recorded_log_position = 0;
- }
- }
- android::base::KernelLogger(log_id, severity, tag, file, line, message);
- };
- android::base::InitLogging(argv, second_stage_logger, InitAborter);
-}
-
-void DumpShutdownDebugInformation() {
- auto lock = std::lock_guard{recorded_logs_lock};
- android::base::KernelLogger(
- android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
- "===================== Dumping previous init lines =====================");
- for (size_t i = recorded_log_position; i < kRecordedLogsSize; ++i) {
- android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
- recorded_logs[i].c_str());
- }
- for (size_t i = 0; i < recorded_log_position; ++i) {
- android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
- recorded_logs[i].c_str());
- }
- android::base::KernelLogger(android::base::MAIN, android::base::ERROR, "init", nullptr, 0,
- "===================== End of dump =====================");
-}
-
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 8a6aa60..3cdc9f4 100644
--- a/init/util.h
+++ b/init/util.h
@@ -98,8 +98,6 @@
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
-void InitSecondStageLogging(char** argv);
-void DumpShutdownDebugInformation();
bool IsRecoveryMode();
} // namespace init
} // namespace android
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 98e3665..ea98ae2 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -167,7 +167,7 @@
EXPECT_EQ(0u, response_.entry_out.attr.gid);
EXPECT_EQ(0u, response_.entry_out.attr.rdev);
EXPECT_EQ(0u, response_.entry_out.attr.blksize);
- EXPECT_EQ(0u, response_.entry_out.attr.padding);
+ EXPECT_EQ(0u, response_.entry_out.attr.flags);
}
TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
@@ -226,7 +226,7 @@
EXPECT_EQ(0u, response_.attr_out.attr.gid);
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
- EXPECT_EQ(0u, response_.attr_out.attr.padding);
+ EXPECT_EQ(0u, response_.attr_out.attr.flags);
}
TEST_F(FuseAppLoopTest, GetAttr_Root) {
@@ -259,7 +259,7 @@
EXPECT_EQ(0u, response_.attr_out.attr.gid);
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
- EXPECT_EQ(0u, response_.attr_out.attr.padding);
+ EXPECT_EQ(0u, response_.attr_out.attr.flags);
}
TEST_F(FuseAppLoopTest, Open) {
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index c18ed51..7529cb9 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1,4 +1 @@
-cferris@google.com
-enh@google.com
-jmgao@google.com
-tomcherry@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index bbbba6d..0f7f8a8 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -18,44 +18,21 @@
#include <sys/types.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#else
+#if defined(_WIN32)
#include <windows.h>
+#else
+#include <pthread.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
-#if !defined(_WIN32)
-
-typedef struct {
- pthread_mutex_t lock;
- int has_tls;
- pthread_key_t tls;
-} thread_store_t;
-
-#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 }
-
-#endif
-
//
// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
//
extern pid_t gettid();
-//
-// Deprecated: use `_Thread_local` in C or `thread_local` in C++.
-//
-#if !defined(_WIN32)
-typedef void (*thread_store_destruct_t)(void* x);
-extern void* thread_store_get(thread_store_t* x)
- __attribute__((__deprecated__("use thread_local instead")));
-extern void thread_store_set(thread_store_t* x, void* y, thread_store_destruct_t z)
- __attribute__((__deprecated__("use thread_local instead")));
-#endif
-
#ifdef __cplusplus
}
#endif
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index eac63b5..8cfee1e 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -16,23 +16,18 @@
#include <cutils/threads.h>
-// For gettid.
#if defined(__APPLE__)
-#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
#include <stdint.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
+#elif defined(__linux__)
#include <syscall.h>
#include <unistd.h>
#elif defined(_WIN32)
#include <windows.h>
#endif
+#if defined(__BIONIC__)
// No definition needed for Android because we'll just pick up bionic's copy.
-#ifndef __ANDROID__
+#else
pid_t gettid() {
#if defined(__APPLE__)
uint64_t tid;
@@ -44,31 +39,4 @@
return GetCurrentThreadId();
#endif
}
-#endif // __ANDROID__
-
-#if !defined(_WIN32)
-void* thread_store_get( thread_store_t* store )
-{
- if (!store->has_tls)
- return NULL;
-
- return pthread_getspecific( store->tls );
-}
-
-extern void thread_store_set( thread_store_t* store,
- void* value,
- thread_store_destruct_t destroy)
-{
- pthread_mutex_lock( &store->lock );
- if (!store->has_tls) {
- if (pthread_key_create( &store->tls, destroy) != 0) {
- pthread_mutex_unlock(&store->lock);
- return;
- }
- store->has_tls = 1;
- }
- pthread_mutex_unlock( &store->lock );
-
- pthread_setspecific( store->tls, value );
-}
#endif
diff --git a/libmodprobe/OWNERS b/libmodprobe/OWNERS
index 4b770b1..e6b5bba 100644
--- a/libmodprobe/OWNERS
+++ b/libmodprobe/OWNERS
@@ -1,2 +1 @@
-tomcherry@google.com
smuckle@google.com
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 65371fa..eec2415 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -21,6 +21,11 @@
cflags: ["-Werror"],
export_include_dirs: ["include"],
+ // TODO: remove connectivity module dependency, or have this lib build against the ndk
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
}
cc_library_static {
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index 27b9a03..8ebb8cc 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,3 +1,2 @@
ccross@google.com
surenb@google.com
-tomcherry@google.com
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 792af6f..5b7a28a 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -42,7 +42,7 @@
"Controllers": [
{
"Controller": "freezer",
- "Path": ".",
+ "Path": "freezer",
"Mode": "0755",
"UID": "system",
"GID": "system"
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 628098b..5b57bdd 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -46,7 +46,7 @@
"File": "cpu.uclamp.latency_sensitive"
},
{
- "Name": "Freezer",
+ "Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
}
@@ -70,11 +70,11 @@
"Name": "Frozen",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "Freezer",
- "Value": "1"
+ "Controller": "freezer",
+ "Path": ""
}
}
]
@@ -83,11 +83,11 @@
"Name": "Unfrozen",
"Actions": [
{
- "Name": "SetAttribute",
+ "Name": "JoinCgroup",
"Params":
{
- "Name": "Freezer",
- "Value": "0"
+ "Controller": "freezer",
+ "Path": "../"
}
}
]
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 860b9ae..5b6ce41 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -28,6 +28,10 @@
enabled: true,
},
},
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
cc_binary {
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
deleted file mode 100644
index 6d6b466..0000000
--- a/libstats/pull/Android.bp
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// Copyright (C) 2019 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.
-//
-
-// ==========================================================
-// Native library to register a pull atom callback with statsd
-// ==========================================================
-cc_defaults {
- name: "libstatspull_defaults",
- srcs: [
- "stats_pull_atom_callback.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- export_include_dirs: ["include"],
- shared_libs: [
- "libbinder_ndk",
- "liblog",
- "libstatssocket",
- ],
- static_libs: [
- "libutils",
- "statsd-aidl-ndk_platform",
- ],
-}
-cc_library_shared {
- name: "libstatspull",
- defaults: [
- "libstatspull_defaults"
- ],
- // enumerate stable entry points for APEX use
- stubs: {
- symbol_file: "libstatspull.map.txt",
- versions: [
- "30",
- ],
- },
- apex_available: [
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
-
- stl: "libc++_static",
-
- // TODO(b/151102177): Enable it when the build error is fixed.
- header_abi_checker: {
- enabled: false,
- },
-}
-
-// ONLY USE IN TESTS.
-cc_library_static {
- name: "libstatspull_private",
- defaults: [
- "libstatspull_defaults",
- ],
- visibility: [
- "//frameworks/base/apex/statsd/tests/libstatspull",
- "//packages/modules/StatsD/apex/tests/libstatspull",
- ],
-}
-
-// Note: These unit tests only test PullAtomMetadata.
-// For full E2E tests of libstatspull, use LibStatsPullTests
-cc_test {
- name: "libstatspull_test",
- srcs: [
- "tests/pull_atom_metadata_test.cpp",
- ],
- shared_libs: [
- "libstatspull",
- "libstatssocket",
- ],
- test_suites: ["general-tests", "mts"],
- test_config: "libstatspull_test.xml",
-
- //TODO(b/153588990): Remove when the build system properly separates
- //32bit and 64bit architectures.
- compile_multilib: "both",
- multilib: {
- lib64: {
- suffix: "64",
- },
- lib32: {
- suffix: "32",
- },
- },
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-missing-field-initializers",
- "-Wno-unused-variable",
- "-Wno-unused-function",
- "-Wno-unused-parameter",
- ],
- require_root: true,
-}
diff --git a/libstats/pull/OWNERS b/libstats/pull/OWNERS
deleted file mode 100644
index 7855774..0000000
--- a/libstats/pull/OWNERS
+++ /dev/null
@@ -1,7 +0,0 @@
-joeo@google.com
-muhammadq@google.com
-ruchirr@google.com
-singhtejinder@google.com
-tsaichristine@google.com
-yaochen@google.com
-yro@google.com
diff --git a/libstats/pull/TEST_MAPPING b/libstats/pull/TEST_MAPPING
deleted file mode 100644
index 76f4f02..0000000
--- a/libstats/pull/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "libstatspull_test"
- }
- ]
-}
\ No newline at end of file
diff --git a/libstats/pull/include/stats_pull_atom_callback.h b/libstats/pull/include/stats_pull_atom_callback.h
deleted file mode 100644
index 17df584..0000000
--- a/libstats/pull/include/stats_pull_atom_callback.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2019, 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 <stats_event.h>
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Opaque struct representing the metadata for registering an AStatsManager_PullAtomCallback.
- */
-struct AStatsManager_PullAtomMetadata;
-typedef struct AStatsManager_PullAtomMetadata AStatsManager_PullAtomMetadata;
-
-/**
- * Allocate and initialize new PullAtomMetadata.
- *
- * Must call AStatsManager_PullAtomMetadata_release to free the memory.
- */
-AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain();
-
-/**
- * Frees the memory held by this PullAtomMetadata
- *
- * After calling this, the PullAtomMetadata must not be used or modified in any way.
- */
-void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the cool down time of the pull in milliseconds. If two successive pulls are issued
- * within the cool down, a cached version of the first will be used for the second. The minimum
- * allowed cool down is one second.
- */
-void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t cool_down_millis);
-
-/**
- * Get the cool down time of the pull in milliseconds.
- */
-int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the maximum time the pull can take in milliseconds.
- * The maximum allowed timeout is 10 seconds.
- */
-void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t timeout_millis);
-
-/**
- * Get the maximum time the pull can take in milliseconds.
- */
-int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Set the additive fields of this pulled atom.
- *
- * This is only applicable for atoms which have a uid field. When tasks are run in
- * isolated processes, the data will be attributed to the host uid. Additive fields
- * will be combined when the non-additive fields are the same.
- */
-void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* additive_fields, int32_t num_fields);
-
-/**
- * Get the number of additive fields for this pulled atom. This is intended to be called before
- * AStatsManager_PullAtomMetadata_getAdditiveFields to determine the size of the array.
- */
-int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
- AStatsManager_PullAtomMetadata* metadata);
-
-/**
- * Get the additive fields of this pulled atom.
- *
- * \param fields an output parameter containing the additive fields for this PullAtomMetadata.
- * Fields is an array and it is assumed that it is at least as large as the number of
- * additive fields, which can be obtained by calling
- * AStatsManager_PullAtomMetadata_getNumAdditiveFields.
- */
-void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* fields);
-
-/**
- * Return codes for the result of a pull.
- */
-typedef int32_t AStatsManager_PullAtomCallbackReturn;
-enum {
- // Value indicating that this pull was successful and that the result should be used.
- AStatsManager_PULL_SUCCESS = 0,
- // Value indicating that this pull was unsuccessful and that the result should not be used.
- AStatsManager_PULL_SKIP = 1,
-};
-
-/**
- * Opaque struct representing a list of AStatsEvent objects.
- */
-struct AStatsEventList;
-typedef struct AStatsEventList AStatsEventList;
-
-/**
- * Appends and returns an AStatsEvent to the end of the AStatsEventList.
- *
- * If an AStatsEvent is obtained in this manner, the memory is internally managed and
- * AStatsEvent_release does not need to be called. The lifetime of the AStatsEvent is that of the
- * AStatsEventList.
- *
- * The AStatsEvent does still need to be built by calling AStatsEvent_build.
- */
-AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data);
-
-/**
- * Callback interface for pulling atoms requested by the stats service.
- *
- * \param atom_tag the tag of the atom to pull.
- * \param data an output parameter in which the caller should fill the results of the pull. This
- * param cannot be NULL and it's lifetime is as long as the execution of the callback.
- * It must not be accessed or modified after returning from the callback.
- * \param cookie the opaque pointer passed in AStatsManager_registerPullAtomCallback.
- * \return AStatsManager_PULL_SUCCESS if the pull was successful, or AStatsManager_PULL_SKIP if not.
- */
-typedef AStatsManager_PullAtomCallbackReturn (*AStatsManager_PullAtomCallback)(
- int32_t atom_tag, AStatsEventList* data, void* cookie);
-/**
- * Sets a callback for an atom when that atom is to be pulled. The stats service will
- * invoke the callback when the stats service determines that this atom needs to be
- * pulled.
- *
- * Requires the REGISTER_STATS_PULL_ATOM permission.
- *
- * \param atom_tag The tag of the atom for this pull atom callback.
- * \param metadata Optional metadata specifying the timeout, cool down time, and
- * additive fields for mapping isolated to host uids.
- * This param is nullable, in which case defaults will be used.
- * \param callback The callback to be invoked when the stats service pulls the atom.
- * \param cookie A pointer that will be passed back to the callback.
- * It has no meaning to statsd.
- */
-void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
- AStatsManager_PullAtomCallback callback, void* cookie);
-
-/**
- * Clears a callback for an atom when that atom is to be pulled. Note that any ongoing
- * pulls will still occur.
- *
- * Requires the REGISTER_STATS_PULL_ATOM permission.
- *
- * \param atomTag The tag of the atom of which to unregister
- */
-void AStatsManager_clearPullAtomCallback(int32_t atom_tag);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/libstats/pull/libstatspull.map.txt b/libstats/pull/libstatspull.map.txt
deleted file mode 100644
index e0e851a..0000000
--- a/libstats/pull/libstatspull.map.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-LIBSTATSPULL {
- global:
- AStatsManager_PullAtomMetadata_obtain; # apex # introduced=30
- AStatsManager_PullAtomMetadata_release; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setCoolDownMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getCoolDownMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setTimeoutMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getTimeoutMillis; # apex # introduced=30
- AStatsManager_PullAtomMetadata_setAdditiveFields; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getNumAdditiveFields; # apex # introduced=30
- AStatsManager_PullAtomMetadata_getAdditiveFields; # apex # introduced=30
- AStatsEventList_addStatsEvent; # apex # introduced=30
- AStatsManager_setPullAtomCallback; # apex # introduced=30
- AStatsManager_clearPullAtomCallback; # apex # introduced=30
- local:
- *;
-};
diff --git a/libstats/pull/libstatspull_test.xml b/libstats/pull/libstatspull_test.xml
deleted file mode 100644
index 233fc1f..0000000
--- a/libstats/pull/libstatspull_test.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs libstatspull_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
- <option name="test-suite-tag" value="mts" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="libstatspull_test->/data/local/tmp/libstatspull_test" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libstatspull_test" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
- </object>
-</configuration>
diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp
deleted file mode 100644
index 478cae7..0000000
--- a/libstats/pull/stats_pull_atom_callback.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2019, 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.
- */
-
-#include <map>
-#include <thread>
-#include <vector>
-
-#include <stats_event.h>
-#include <stats_pull_atom_callback.h>
-
-#include <aidl/android/os/BnPullAtomCallback.h>
-#include <aidl/android/os/IPullAtomResultReceiver.h>
-#include <aidl/android/os/IStatsd.h>
-#include <aidl/android/util/StatsEventParcel.h>
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_manager.h>
-
-using Status = ::ndk::ScopedAStatus;
-using aidl::android::os::BnPullAtomCallback;
-using aidl::android::os::IPullAtomResultReceiver;
-using aidl::android::os::IStatsd;
-using aidl::android::util::StatsEventParcel;
-using ::ndk::SharedRefBase;
-
-struct AStatsEventList {
- std::vector<AStatsEvent*> data;
-};
-
-AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
- AStatsEvent* event = AStatsEvent_obtain();
- pull_data->data.push_back(event);
- return event;
-}
-
-static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
-
-struct AStatsManager_PullAtomMetadata {
- int64_t cool_down_millis;
- int64_t timeout_millis;
- std::vector<int32_t> additive_fields;
-};
-
-AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
- AStatsManager_PullAtomMetadata* metadata = new AStatsManager_PullAtomMetadata();
- metadata->cool_down_millis = DEFAULT_COOL_DOWN_MILLIS;
- metadata->timeout_millis = DEFAULT_TIMEOUT_MILLIS;
- metadata->additive_fields = std::vector<int32_t>();
- return metadata;
-}
-
-void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
- delete metadata;
-}
-
-void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t cool_down_millis) {
- metadata->cool_down_millis = cool_down_millis;
-}
-
-int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
- return metadata->cool_down_millis;
-}
-
-void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
- int64_t timeout_millis) {
- metadata->timeout_millis = timeout_millis;
-}
-
-int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
- return metadata->timeout_millis;
-}
-
-void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* additive_fields,
- int32_t num_fields) {
- metadata->additive_fields.assign(additive_fields, additive_fields + num_fields);
-}
-
-int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
- AStatsManager_PullAtomMetadata* metadata) {
- return metadata->additive_fields.size();
-}
-
-void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
- int32_t* fields) {
- std::copy(metadata->additive_fields.begin(), metadata->additive_fields.end(), fields);
-}
-
-class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
- public:
- StatsPullAtomCallbackInternal(const AStatsManager_PullAtomCallback callback, void* cookie,
- const int64_t coolDownMillis, const int64_t timeoutMillis,
- const std::vector<int32_t> additiveFields)
- : mCallback(callback),
- mCookie(cookie),
- mCoolDownMillis(coolDownMillis),
- mTimeoutMillis(timeoutMillis),
- mAdditiveFields(additiveFields) {}
-
- Status onPullAtom(int32_t atomTag,
- const std::shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
- AStatsEventList statsEventList;
- int successInt = mCallback(atomTag, &statsEventList, mCookie);
- bool success = successInt == AStatsManager_PULL_SUCCESS;
-
- // Convert stats_events into StatsEventParcels.
- std::vector<StatsEventParcel> parcels;
- for (int i = 0; i < statsEventList.data.size(); i++) {
- size_t size;
- uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
-
- StatsEventParcel p;
- // vector.assign() creates a copy, but this is inevitable unless
- // stats_event.h/c uses a vector as opposed to a buffer.
- p.buffer.assign(buffer, buffer + size);
- parcels.push_back(std::move(p));
- }
-
- Status status = resultReceiver->pullFinished(atomTag, success, parcels);
- if (!status.isOk()) {
- std::vector<StatsEventParcel> emptyParcels;
- resultReceiver->pullFinished(atomTag, /*success=*/false, emptyParcels);
- }
- for (int i = 0; i < statsEventList.data.size(); i++) {
- AStatsEvent_release(statsEventList.data[i]);
- }
- return Status::ok();
- }
-
- int64_t getCoolDownMillis() const { return mCoolDownMillis; }
- int64_t getTimeoutMillis() const { return mTimeoutMillis; }
- const std::vector<int32_t>& getAdditiveFields() const { return mAdditiveFields; }
-
- private:
- const AStatsManager_PullAtomCallback mCallback;
- void* mCookie;
- const int64_t mCoolDownMillis;
- const int64_t mTimeoutMillis;
- const std::vector<int32_t> mAdditiveFields;
-};
-
-static std::mutex pullAtomMutex;
-static std::shared_ptr<IStatsd> sStatsd = nullptr;
-
-static std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> mPullers;
-static std::shared_ptr<IStatsd> getStatsService();
-
-static void binderDied(void* /*cookie*/) {
- {
- std::lock_guard<std::mutex> lock(pullAtomMutex);
- sStatsd = nullptr;
- }
-
- std::shared_ptr<IStatsd> statsService = getStatsService();
- if (statsService == nullptr) {
- return;
- }
-
- // Since we do not want to make an IPC with the lock held, we first create a
- // copy of the data with the lock held before iterating through the map.
- std::map<int32_t, std::shared_ptr<StatsPullAtomCallbackInternal>> pullersCopy;
- {
- std::lock_guard<std::mutex> lock(pullAtomMutex);
- pullersCopy = mPullers;
- }
- for (const auto& it : pullersCopy) {
- statsService->registerNativePullAtomCallback(it.first, it.second->getCoolDownMillis(),
- it.second->getTimeoutMillis(),
- it.second->getAdditiveFields(), it.second);
- }
-}
-
-static ::ndk::ScopedAIBinder_DeathRecipient sDeathRecipient(
- AIBinder_DeathRecipient_new(binderDied));
-
-static std::shared_ptr<IStatsd> getStatsService() {
- std::lock_guard<std::mutex> lock(pullAtomMutex);
- if (!sStatsd) {
- // Fetch statsd
- ::ndk::SpAIBinder binder(AServiceManager_getService("stats"));
- sStatsd = IStatsd::fromBinder(binder);
- if (sStatsd) {
- AIBinder_linkToDeath(binder.get(), sDeathRecipient.get(), /*cookie=*/nullptr);
- }
- }
- return sStatsd;
-}
-
-void registerStatsPullAtomCallbackBlocking(int32_t atomTag,
- std::shared_ptr<StatsPullAtomCallbackInternal> cb) {
- const std::shared_ptr<IStatsd> statsService = getStatsService();
- if (statsService == nullptr) {
- // Statsd not available
- return;
- }
-
- statsService->registerNativePullAtomCallback(
- atomTag, cb->getCoolDownMillis(), cb->getTimeoutMillis(), cb->getAdditiveFields(), cb);
-}
-
-void unregisterStatsPullAtomCallbackBlocking(int32_t atomTag) {
- const std::shared_ptr<IStatsd> statsService = getStatsService();
- if (statsService == nullptr) {
- // Statsd not available
- return;
- }
-
- statsService->unregisterNativePullAtomCallback(atomTag);
-}
-
-void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
- AStatsManager_PullAtomCallback callback, void* cookie) {
- int64_t coolDownMillis =
- metadata == nullptr ? DEFAULT_COOL_DOWN_MILLIS : metadata->cool_down_millis;
- int64_t timeoutMillis = metadata == nullptr ? DEFAULT_TIMEOUT_MILLIS : metadata->timeout_millis;
-
- std::vector<int32_t> additiveFields;
- if (metadata != nullptr) {
- additiveFields = metadata->additive_fields;
- }
-
- std::shared_ptr<StatsPullAtomCallbackInternal> callbackBinder =
- SharedRefBase::make<StatsPullAtomCallbackInternal>(callback, cookie, coolDownMillis,
- timeoutMillis, additiveFields);
-
- {
- std::lock_guard<std::mutex> lg(pullAtomMutex);
- // Always add to the map. If statsd is dead, we will add them when it comes back.
- mPullers[atom_tag] = callbackBinder;
- }
-
- std::thread registerThread(registerStatsPullAtomCallbackBlocking, atom_tag, callbackBinder);
- registerThread.detach();
-}
-
-void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
- {
- std::lock_guard<std::mutex> lg(pullAtomMutex);
- // Always remove the puller from our map.
- // If statsd is down, we will not register it when it comes back.
- mPullers.erase(atom_tag);
- }
- std::thread unregisterThread(unregisterStatsPullAtomCallbackBlocking, atom_tag);
- unregisterThread.detach();
-}
diff --git a/libstats/pull/tests/pull_atom_metadata_test.cpp b/libstats/pull/tests/pull_atom_metadata_test.cpp
deleted file mode 100644
index abc8e47..0000000
--- a/libstats/pull/tests/pull_atom_metadata_test.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2020, 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.
- */
-
-#include <gtest/gtest.h>
-
-#include <stats_pull_atom_callback.h>
-
-namespace {
-
-static const int64_t DEFAULT_COOL_DOWN_MILLIS = 1000LL; // 1 second.
-static const int64_t DEFAULT_TIMEOUT_MILLIS = 2000LL; // 2 seconds.
-
-} // anonymous namespace
-
-TEST(AStatsManager_PullAtomMetadataTest, TestEmpty) {
- AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
- AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetTimeoutMillis) {
- int64_t timeoutMillis = 500;
- AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
- AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
- AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetCoolDownMillis) {
- int64_t coolDownMillis = 10000;
- AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
- AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), 0);
- AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetAdditiveFields) {
- const int numFields = 3;
- int inputFields[numFields] = {2, 4, 6};
- AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
- AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), DEFAULT_COOL_DOWN_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), DEFAULT_TIMEOUT_MILLIS);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
- int outputFields[numFields];
- AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
- for (int i = 0; i < numFields; i++) {
- EXPECT_EQ(inputFields[i], outputFields[i]);
- }
- AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-TEST(AStatsManager_PullAtomMetadataTest, TestSetAllElements) {
- int64_t timeoutMillis = 500;
- int64_t coolDownMillis = 10000;
- const int numFields = 3;
- int inputFields[numFields] = {2, 4, 6};
-
- AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
- AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, timeoutMillis);
- AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, coolDownMillis);
- AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, inputFields, numFields);
-
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), coolDownMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), timeoutMillis);
- EXPECT_EQ(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), numFields);
- int outputFields[numFields];
- AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, outputFields);
- for (int i = 0; i < numFields; i++) {
- EXPECT_EQ(inputFields[i], outputFields[i]);
- }
- AStatsManager_PullAtomMetadata_release(metadata);
-}
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
deleted file mode 100644
index a6ea185..0000000
--- a/libstats/socket/Android.bp
+++ /dev/null
@@ -1,127 +0,0 @@
-//
-// Copyright (C) 2018 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.
-//
-
-// =========================================================================
-// Native library to write stats log to statsd socket on Android R and later
-// =========================================================================
-cc_defaults {
- name: "libstatssocket_defaults",
- srcs: [
- "stats_buffer_writer.c",
- "stats_event.c",
- "stats_socket.c",
- "statsd_writer.c",
- ],
- export_include_dirs: ["include"],
- static_libs: [
- "libcutils", // does not expose a stable C API
- ],
- header_libs: ["liblog_headers"],
- cflags: [
- "-Wall",
- "-Werror",
- ],
-}
-
-cc_library {
- name: "libstatssocket",
- defaults: [
- "libstatssocket_defaults",
- ],
- host_supported: true,
- target: {
- // On android, libstatssocket should only be linked as a shared lib
- android: {
- static: {
- enabled: false,
- },
- },
- host: {
- shared: {
- enabled: false,
- },
- },
- },
- stl: "libc++_static",
-
- // enumerate stable entry points for APEX use
- stubs: {
- symbol_file: "libstatssocket.map.txt",
- versions: [
- "30",
- ],
- },
- apex_available: [
- "com.android.os.statsd",
- "test_com.android.os.statsd",
- ],
-}
-
-//TODO (b/149842105): Figure out if there is a better solution for this.
-cc_test_library {
- name: "libstatssocket_private",
- defaults: [
- "libstatssocket_defaults",
- ],
- visibility: [
- "//frameworks/base/apex/statsd/tests/libstatspull",
- "//frameworks/base/cmds/statsd",
- "//packages/modules/StatsD/apex/tests/libstatspull",
- "//packages/modules/StatsD/bin",
- ],
-}
-
-cc_library_headers {
- name: "libstatssocket_headers",
- export_include_dirs: ["include"],
- host_supported: true,
- apex_available: ["com.android.resolv"],
- min_sdk_version: "29",
-}
-
-cc_test {
- name: "libstatssocket_test",
- srcs: [
- "tests/stats_event_test.cpp",
- "tests/stats_writer_test.cpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- static_libs: [
- "libgmock",
- "libstatssocket_private",
- ],
- shared_libs: [
- "libcutils",
- "libutils",
- ],
- test_suites: ["device-tests", "mts"],
- test_config: "libstatssocket_test.xml",
- //TODO(b/153588990): Remove when the build system properly separates.
- //32bit and 64bit architectures.
- compile_multilib: "both",
- multilib: {
- lib64: {
- suffix: "64",
- },
- lib32: {
- suffix: "32",
- },
- },
- require_root: true,
-}
diff --git a/libstats/socket/TEST_MAPPING b/libstats/socket/TEST_MAPPING
deleted file mode 100644
index 0224998..0000000
--- a/libstats/socket/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "libstatssocket_test"
- }
- ]
-}
\ No newline at end of file
diff --git a/libstats/socket/include/stats_buffer_writer.h b/libstats/socket/include/stats_buffer_writer.h
deleted file mode 100644
index 5b41f0e..0000000
--- a/libstats/socket/include/stats_buffer_writer.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 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 <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __CPLUSPLUS
-void stats_log_close();
-int stats_log_is_closed();
-int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId);
-#ifdef __cplusplus
-}
-#endif // __CPLUSPLUS
diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h
deleted file mode 100644
index 3d3baf9..0000000
--- a/libstats/socket/include/stats_event.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#ifndef ANDROID_STATS_LOG_STATS_EVENT_H
-#define ANDROID_STATS_LOG_STATS_EVENT_H
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-/*
- * Functionality to build and store the buffer sent over the statsd socket.
- * This code defines and encapsulates the socket protocol.
- *
- * Usage:
- * AStatsEvent* event = AStatsEvent_obtain();
- *
- * AStatsEvent_setAtomId(event, atomId);
- * AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation
- * AStatsEvent_writeInt32(event, 24);
- * AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field
- * AStatsEvent_addInt32Annotation(event, 2, 128);
- * AStatsEvent_writeFloat(event, 2.0);
- *
- * AStatsEvent_write(event);
- * AStatsEvent_release(event);
- *
- * Note that calls to add atom fields and annotations should be made in the
- * order that they are defined in the atom.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __CPLUSPLUS
-
-/**
- * Opaque struct use to represent a StatsEvent. It builds and stores the data that is sent to
- * statsd.
- */
-struct AStatsEvent;
-typedef struct AStatsEvent AStatsEvent;
-
-/**
- * Returns a new AStatsEvent. If you call this function, you must call AStatsEvent_release to free
- * the allocated memory.
- */
-AStatsEvent* AStatsEvent_obtain();
-
-/**
- * Builds and finalizes the AStatsEvent for a pulled event.
- * This should only be called for pulled AStatsEvents.
- *
- * After this function, the StatsEvent must not be modified in any way other than calling release or
- * write.
- *
- * Build can be called multiple times without error.
- * If the event has been built before, this function is a no-op.
- */
-void AStatsEvent_build(AStatsEvent* event);
-
-/**
- * Writes the StatsEvent to the stats log.
- *
- * After calling this, AStatsEvent_release must be called,
- * and is the only function that can be safely called.
- */
-int AStatsEvent_write(AStatsEvent* event);
-
-/**
- * Frees the memory held by this StatsEvent.
- *
- * After calling this, the StatsEvent must not be used or modified in any way.
- */
-void AStatsEvent_release(AStatsEvent* event);
-
-/**
- * Sets the atom id for this StatsEvent.
- *
- * This function should be called immediately after AStatsEvent_obtain. It may
- * be called additional times as well, but subsequent calls will have no effect.
- **/
-void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId);
-
-/**
- * Writes an int32_t field to this StatsEvent.
- **/
-void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value);
-
-/**
- * Writes an int64_t field to this StatsEvent.
- **/
-void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value);
-
-/**
- * Writes a float field to this StatsEvent.
- **/
-void AStatsEvent_writeFloat(AStatsEvent* event, float value);
-
-/**
- * Write a bool field to this StatsEvent.
- **/
-void AStatsEvent_writeBool(AStatsEvent* event, bool value);
-
-/**
- * Write a byte array field to this StatsEvent.
- **/
-void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes);
-
-/**
- * Write a string field to this StatsEvent.
- *
- * The string must be null-terminated.
- **/
-void AStatsEvent_writeString(AStatsEvent* event, const char* value);
-
-/**
- * Write an attribution chain field to this StatsEvent.
- *
- * The sizes of uids and tags must be equal. The AttributionNode at position i is
- * made up of uids[i] and tags[i].
- *
- * \param uids array of uids in the attribution chain.
- * \param tags array of tags in the attribution chain. Each tag must be null-terminated.
- * \param numNodes the number of AttributionNodes in the attribution chain. This is the length of
- * the uids and the tags.
- **/
-void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
- const char* const* tags, uint8_t numNodes);
-
-/**
- * Write a bool annotation for the previous field written.
- **/
-void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value);
-
-/**
- * Write an integer annotation for the previous field written.
- **/
-void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value);
-
-// Internal/test APIs. Should not be exposed outside of the APEX.
-void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs);
-uint32_t AStatsEvent_getAtomId(AStatsEvent* event);
-// Size is an output parameter.
-uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size);
-uint32_t AStatsEvent_getErrors(AStatsEvent* event);
-
-#ifdef __cplusplus
-}
-#endif // __CPLUSPLUS
-
-#endif // ANDROID_STATS_LOG_STATS_EVENT_H
diff --git a/libstats/socket/include/stats_socket.h b/libstats/socket/include/stats_socket.h
deleted file mode 100644
index 5a75fc0..0000000
--- a/libstats/socket/include/stats_socket.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 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
-
-/**
- * Helpers to manage the statsd socket.
- **/
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __CPLUSPLUS
-
-/**
- * Closes the statsd socket file descriptor.
- **/
-void AStatsSocket_close();
-#ifdef __cplusplus
-}
-#endif // __CPLUSPLUS
diff --git a/libstats/socket/libstatssocket.map.txt b/libstats/socket/libstatssocket.map.txt
deleted file mode 100644
index 5c13904..0000000
--- a/libstats/socket/libstatssocket.map.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-LIBSTATSSOCKET {
- global:
- AStatsEvent_obtain; # apex # introduced=30
- AStatsEvent_build; # apex # introduced=30
- AStatsEvent_write; # apex # introduced=30
- AStatsEvent_release; # apex # introduced=30
- AStatsEvent_setAtomId; # apex # introduced=30
- AStatsEvent_writeInt32; # apex # introduced=30
- AStatsEvent_writeInt64; # apex # introduced=30
- AStatsEvent_writeFloat; # apex # introduced=30
- AStatsEvent_writeBool; # apex # introduced=30
- AStatsEvent_writeByteArray; # apex # introduced=30
- AStatsEvent_writeString; # apex # introduced=30
- AStatsEvent_writeAttributionChain; # apex # introduced=30
- AStatsEvent_addBoolAnnotation; # apex # introduced=30
- AStatsEvent_addInt32Annotation; # apex # introduced=30
- AStatsSocket_close; # apex # introduced=30
- local:
- *;
-};
diff --git a/libstats/socket/libstatssocket_test.xml b/libstats/socket/libstatssocket_test.xml
deleted file mode 100644
index d2694d1..0000000
--- a/libstats/socket/libstatssocket_test.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
--->
-<configuration description="Runs libstatssocket_test.">
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="apct-native" />
- <option name="test-suite-tag" value="mts" />
-
- <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="libstatssocket_test->/data/local/tmp/libstatssocket_test" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="libstatssocket_test" />
- </test>
-
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
- </object>
-</configuration>
-
diff --git a/libstats/socket/stats_buffer_writer.c b/libstats/socket/stats_buffer_writer.c
deleted file mode 100644
index 549acdc..0000000
--- a/libstats/socket/stats_buffer_writer.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "include/stats_buffer_writer.h"
-#ifdef __ANDROID__
-#include <cutils/properties.h>
-#endif
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/uio.h>
-#include "statsd_writer.h"
-
-static const uint32_t kStatsEventTag = 1937006964;
-
-extern struct android_log_transport_write statsdLoggerWrite;
-
-static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*__write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
-
-void note_log_drop(int error, int atomId) {
- statsdLoggerWrite.noteDrop(error, atomId);
-}
-
-void stats_log_close() {
- statsd_writer_init_lock();
- __write_to_statsd = __write_to_statsd_init;
- if (statsdLoggerWrite.close) {
- (*statsdLoggerWrite.close)();
- }
- statsd_writer_init_unlock();
-}
-
-int stats_log_is_closed() {
- return statsdLoggerWrite.isClosed && (*statsdLoggerWrite.isClosed)();
-}
-
-int write_buffer_to_statsd(void* buffer, size_t size, uint32_t atomId) {
- int ret = 1;
-
- struct iovec vecs[2];
- vecs[0].iov_base = (void*)&kStatsEventTag;
- vecs[0].iov_len = sizeof(kStatsEventTag);
- vecs[1].iov_base = buffer;
- vecs[1].iov_len = size;
-
- ret = __write_to_statsd(vecs, 2);
-
- if (ret < 0) {
- note_log_drop(ret, atomId);
- }
-
- return ret;
-}
-
-static int __write_to_stats_daemon(struct iovec* vec, size_t nr) {
- int save_errno;
- struct timespec ts;
- size_t len, i;
-
- for (len = i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
- if (!len) {
- return -EINVAL;
- }
-
- save_errno = errno;
-#if defined(__ANDROID__)
- clock_gettime(CLOCK_REALTIME, &ts);
-#else
- struct timeval tv;
- gettimeofday(&tv, NULL);
- ts.tv_sec = tv.tv_sec;
- ts.tv_nsec = tv.tv_usec * 1000;
-#endif
-
- int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);
- errno = save_errno;
- return ret;
-}
-
-static int __write_to_statsd_initialize_locked() {
- if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {
- if (statsdLoggerWrite.close) {
- (*statsdLoggerWrite.close)();
- return -ENODEV;
- }
- }
- return 1;
-}
-
-static int __write_to_statsd_init(struct iovec* vec, size_t nr) {
- int ret, save_errno = errno;
-
- statsd_writer_init_lock();
-
- if (__write_to_statsd == __write_to_statsd_init) {
- ret = __write_to_statsd_initialize_locked();
- if (ret < 0) {
- statsd_writer_init_unlock();
- errno = save_errno;
- return ret;
- }
-
- __write_to_statsd = __write_to_stats_daemon;
- }
-
- statsd_writer_init_unlock();
-
- ret = __write_to_statsd(vec, nr);
- errno = save_errno;
- return ret;
-}
diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c
deleted file mode 100644
index f3e8087..0000000
--- a/libstats/socket/stats_event.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "include/stats_event.h"
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include "stats_buffer_writer.h"
-
-#define LOGGER_ENTRY_MAX_PAYLOAD 4068
-// Max payload size is 4 bytes less as 4 bytes are reserved for stats_eventTag.
-// See android_util_Stats_Log.cpp
-#define MAX_PUSH_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - 4)
-
-#define MAX_PULL_EVENT_PAYLOAD (50 * 1024) // 50 KB
-
-/* POSITIONS */
-#define POS_NUM_ELEMENTS 1
-#define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t))
-#define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t))
-
-/* LIMITS */
-#define MAX_ANNOTATION_COUNT 15
-#define MAX_BYTE_VALUE 127 // parsing side requires that lengths fit in 7 bits
-
-/* ERRORS */
-#define ERROR_NO_TIMESTAMP 0x1
-#define ERROR_NO_ATOM_ID 0x2
-#define ERROR_OVERFLOW 0x4
-#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
-#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
-#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
-#define ERROR_INVALID_ANNOTATION_ID 0x40
-#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
-#define ERROR_TOO_MANY_ANNOTATIONS 0x100
-#define ERROR_TOO_MANY_FIELDS 0x200
-#define ERROR_INVALID_VALUE_TYPE 0x400
-#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
-
-/* TYPE IDS */
-#define INT32_TYPE 0x00
-#define INT64_TYPE 0x01
-#define STRING_TYPE 0x02
-#define LIST_TYPE 0x03
-#define FLOAT_TYPE 0x04
-#define BOOL_TYPE 0x05
-#define BYTE_ARRAY_TYPE 0x06
-#define OBJECT_TYPE 0x07
-#define KEY_VALUE_PAIRS_TYPE 0x08
-#define ATTRIBUTION_CHAIN_TYPE 0x09
-#define ERROR_TYPE 0x0F
-
-// The AStatsEvent struct holds the serialized encoding of an event
-// within a buf. Also includes other required fields.
-struct AStatsEvent {
- uint8_t* buf;
- // Location of last field within the buf. Here, field denotes either a
- // metadata field (e.g. timestamp) or an atom field.
- size_t lastFieldPos;
- // Number of valid bytes within the buffer.
- size_t numBytesWritten;
- uint32_t numElements;
- uint32_t atomId;
- uint32_t errors;
- bool built;
- size_t bufSize;
-};
-
-static int64_t get_elapsed_realtime_ns() {
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_BOOTTIME, &t);
- return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
-}
-
-AStatsEvent* AStatsEvent_obtain() {
- AStatsEvent* event = malloc(sizeof(AStatsEvent));
- event->lastFieldPos = 0;
- event->numBytesWritten = 2; // reserve first 2 bytes for root event type and number of elements
- event->numElements = 0;
- event->atomId = 0;
- event->errors = 0;
- event->built = false;
- event->bufSize = MAX_PUSH_EVENT_PAYLOAD;
- event->buf = (uint8_t*)calloc(event->bufSize, 1);
-
- event->buf[0] = OBJECT_TYPE;
- AStatsEvent_writeInt64(event, get_elapsed_realtime_ns()); // write the timestamp
-
- return event;
-}
-
-void AStatsEvent_release(AStatsEvent* event) {
- free(event->buf);
- free(event);
-}
-
-void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
- if (event->atomId != 0) return;
- if (event->numElements != 1) {
- event->errors |= ERROR_ATOM_ID_INVALID_POSITION;
- return;
- }
-
- event->atomId = atomId;
- AStatsEvent_writeInt32(event, atomId);
-}
-
-// Overwrites the timestamp populated in AStatsEvent_obtain with a custom
-// timestamp. Should only be called from test code.
-void AStatsEvent_overwriteTimestamp(AStatsEvent* event, uint64_t timestampNs) {
- memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], ×tampNs, sizeof(timestampNs));
- // Do not increment numElements because we already accounted for the timestamp
- // within AStatsEvent_obtain.
-}
-
-// Side-effect: modifies event->errors if the buffer would overflow
-static bool overflows(AStatsEvent* event, size_t size) {
- const size_t totalBytesNeeded = event->numBytesWritten + size;
- if (totalBytesNeeded > MAX_PULL_EVENT_PAYLOAD) {
- event->errors |= ERROR_OVERFLOW;
- return true;
- }
-
- // Expand buffer if needed.
- if (event->bufSize < MAX_PULL_EVENT_PAYLOAD && totalBytesNeeded > event->bufSize) {
- do {
- event->bufSize *= 2;
- } while (event->bufSize <= totalBytesNeeded);
-
- if (event->bufSize > MAX_PULL_EVENT_PAYLOAD) {
- event->bufSize = MAX_PULL_EVENT_PAYLOAD;
- }
-
- event->buf = (uint8_t*)realloc(event->buf, event->bufSize);
- }
- return false;
-}
-
-// Side-effect: all append functions increment event->numBytesWritten if there is
-// sufficient space within the buffer to place the value
-static void append_byte(AStatsEvent* event, uint8_t value) {
- if (!overflows(event, sizeof(value))) {
- event->buf[event->numBytesWritten] = value;
- event->numBytesWritten += sizeof(value);
- }
-}
-
-static void append_bool(AStatsEvent* event, bool value) {
- append_byte(event, (uint8_t)value);
-}
-
-static void append_int32(AStatsEvent* event, int32_t value) {
- if (!overflows(event, sizeof(value))) {
- memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
- event->numBytesWritten += sizeof(value);
- }
-}
-
-static void append_int64(AStatsEvent* event, int64_t value) {
- if (!overflows(event, sizeof(value))) {
- memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
- event->numBytesWritten += sizeof(value);
- }
-}
-
-static void append_float(AStatsEvent* event, float value) {
- if (!overflows(event, sizeof(value))) {
- memcpy(&event->buf[event->numBytesWritten], &value, sizeof(value));
- event->numBytesWritten += sizeof(float);
- }
-}
-
-static void append_byte_array(AStatsEvent* event, const uint8_t* buf, size_t size) {
- if (!overflows(event, size)) {
- memcpy(&event->buf[event->numBytesWritten], buf, size);
- event->numBytesWritten += size;
- }
-}
-
-// Side-effect: modifies event->errors if buf is not properly null-terminated
-static void append_string(AStatsEvent* event, const char* buf) {
- size_t size = strnlen(buf, MAX_PULL_EVENT_PAYLOAD);
- if (size == MAX_PULL_EVENT_PAYLOAD) {
- event->errors |= ERROR_STRING_NOT_NULL_TERMINATED;
- return;
- }
-
- append_int32(event, size);
- append_byte_array(event, (uint8_t*)buf, size);
-}
-
-static void start_field(AStatsEvent* event, uint8_t typeId) {
- event->lastFieldPos = event->numBytesWritten;
- append_byte(event, typeId);
- event->numElements++;
-}
-
-void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
- start_field(event, INT32_TYPE);
- append_int32(event, value);
-}
-
-void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
- start_field(event, INT64_TYPE);
- append_int64(event, value);
-}
-
-void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
- start_field(event, FLOAT_TYPE);
- append_float(event, value);
-}
-
-void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
- start_field(event, BOOL_TYPE);
- append_bool(event, value);
-}
-
-void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
- start_field(event, BYTE_ARRAY_TYPE);
- append_int32(event, numBytes);
- append_byte_array(event, buf, numBytes);
-}
-
-// Value is assumed to be encoded using UTF8
-void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
- start_field(event, STRING_TYPE);
- append_string(event, value);
-}
-
-// Tags are assumed to be encoded using UTF8
-void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
- const char* const* tags, uint8_t numNodes) {
- if (numNodes > MAX_BYTE_VALUE) {
- event->errors |= ERROR_ATTRIBUTION_CHAIN_TOO_LONG;
- return;
- }
-
- start_field(event, ATTRIBUTION_CHAIN_TYPE);
- append_byte(event, numNodes);
-
- for (uint8_t i = 0; i < numNodes; i++) {
- append_int32(event, uids[i]);
- append_string(event, tags[i]);
- }
-}
-
-// Side-effect: modifies event->errors if field has too many annotations
-static void increment_annotation_count(AStatsEvent* event) {
- uint8_t fieldType = event->buf[event->lastFieldPos] & 0x0F;
- uint32_t oldAnnotationCount = (event->buf[event->lastFieldPos] & 0xF0) >> 4;
- uint32_t newAnnotationCount = oldAnnotationCount + 1;
-
- if (newAnnotationCount > MAX_ANNOTATION_COUNT) {
- event->errors |= ERROR_TOO_MANY_ANNOTATIONS;
- return;
- }
-
- event->buf[event->lastFieldPos] = (((uint8_t)newAnnotationCount << 4) & 0xF0) | fieldType;
-}
-
-void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
- if (event->numElements < 2) {
- event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
- return;
- } else if (annotationId > MAX_BYTE_VALUE) {
- event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
- return;
- }
-
- append_byte(event, annotationId);
- append_byte(event, BOOL_TYPE);
- append_bool(event, value);
- increment_annotation_count(event);
-}
-
-void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
- if (event->numElements < 2) {
- event->errors |= ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD;
- return;
- } else if (annotationId > MAX_BYTE_VALUE) {
- event->errors |= ERROR_ANNOTATION_ID_TOO_LARGE;
- return;
- }
-
- append_byte(event, annotationId);
- append_byte(event, INT32_TYPE);
- append_int32(event, value);
- increment_annotation_count(event);
-}
-
-uint32_t AStatsEvent_getAtomId(AStatsEvent* event) {
- return event->atomId;
-}
-
-uint8_t* AStatsEvent_getBuffer(AStatsEvent* event, size_t* size) {
- if (size) *size = event->numBytesWritten;
- return event->buf;
-}
-
-uint32_t AStatsEvent_getErrors(AStatsEvent* event) {
- return event->errors;
-}
-
-static void build_internal(AStatsEvent* event, const bool push) {
- if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS;
- if (0 == event->atomId) event->errors |= ERROR_NO_ATOM_ID;
- if (push && event->numBytesWritten > MAX_PUSH_EVENT_PAYLOAD) event->errors |= ERROR_OVERFLOW;
-
- // If there are errors, rewrite buffer.
- if (event->errors) {
- // Discard everything after the atom id (including atom-level
- // annotations). This leaves only two elements (timestamp and atom id).
- event->numElements = 2;
- // Reset number of atom-level annotations to 0.
- event->buf[POS_ATOM_ID] = INT32_TYPE;
- // Now, write errors to the buffer immediately after the atom id.
- event->numBytesWritten = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t);
- start_field(event, ERROR_TYPE);
- append_int32(event, event->errors);
- }
-
- event->buf[POS_NUM_ELEMENTS] = event->numElements;
-}
-
-void AStatsEvent_build(AStatsEvent* event) {
- if (event->built) return;
-
- build_internal(event, false /* push */);
-
- event->built = true;
-}
-
-int AStatsEvent_write(AStatsEvent* event) {
- build_internal(event, true /* push */);
- return write_buffer_to_statsd(event->buf, event->numBytesWritten, event->atomId);
-}
diff --git a/libstats/socket/stats_socket.c b/libstats/socket/stats_socket.c
deleted file mode 100644
index 09f8967..0000000
--- a/libstats/socket/stats_socket.c
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include "include/stats_socket.h"
-#include "stats_buffer_writer.h"
-
-void AStatsSocket_close() {
- stats_log_close();
-}
diff --git a/libstats/socket/statsd_writer.c b/libstats/socket/statsd_writer.c
deleted file mode 100644
index 73b7a7e..0000000
--- a/libstats/socket/statsd_writer.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2018, 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.
- */
-#include "statsd_writer.h"
-
-#include <cutils/fs.h>
-#include <cutils/sockets.h>
-#include <cutils/threads.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <poll.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-#include <stdarg.h>
-#include <stdatomic.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-static atomic_int dropped = 0;
-static atomic_int log_error = 0;
-static atomic_int atom_tag = 0;
-
-void statsd_writer_init_lock() {
- /*
- * If we trigger a signal handler in the middle of locked activity and the
- * signal handler logs a message, we could get into a deadlock state.
- */
- pthread_mutex_lock(&log_init_lock);
-}
-
-int statd_writer_trylock() {
- return pthread_mutex_trylock(&log_init_lock);
-}
-
-void statsd_writer_init_unlock() {
- pthread_mutex_unlock(&log_init_lock);
-}
-
-static int statsdAvailable();
-static int statsdOpen();
-static void statsdClose();
-static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
-static int statsdIsClosed();
-
-struct android_log_transport_write statsdLoggerWrite = {
- .name = "statsd",
- .sock = -EBADF,
- .available = statsdAvailable,
- .open = statsdOpen,
- .close = statsdClose,
- .write = statsdWrite,
- .noteDrop = statsdNoteDrop,
- .isClosed = statsdIsClosed,
-};
-
-/* log_init_lock assumed */
-static int statsdOpen() {
- int i, ret = 0;
-
- i = atomic_load(&statsdLoggerWrite.sock);
- if (i < 0) {
- int flags = SOCK_DGRAM;
-#ifdef SOCK_CLOEXEC
- flags |= SOCK_CLOEXEC;
-#endif
-#ifdef SOCK_NONBLOCK
- flags |= SOCK_NONBLOCK;
-#endif
- int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));
- if (sock < 0) {
- ret = -errno;
- } else {
- int sndbuf = 1 * 1024 * 1024; // set max send buffer size 1MB
- socklen_t bufLen = sizeof(sndbuf);
- // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.
- // Proceed to connect even setsockopt fails.
- setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);
- struct sockaddr_un un;
- memset(&un, 0, sizeof(struct sockaddr_un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, "/dev/socket/statsdw");
-
- if (TEMP_FAILURE_RETRY(
- connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {
- ret = -errno;
- switch (ret) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- i = atomic_exchange(&statsdLoggerWrite.sock, ret);
- /* FALLTHRU */
- default:
- break;
- }
- close(sock);
- } else {
- ret = atomic_exchange(&statsdLoggerWrite.sock, sock);
- if ((ret >= 0) && (ret != sock)) {
- close(ret);
- }
- ret = 0;
- }
- }
- }
-
- return ret;
-}
-
-static void __statsdClose(int negative_errno) {
- int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);
- if (sock >= 0) {
- close(sock);
- }
-}
-
-static void statsdClose() {
- __statsdClose(-EBADF);
-}
-
-static int statsdAvailable() {
- if (atomic_load(&statsdLoggerWrite.sock) < 0) {
- if (access("/dev/socket/statsdw", W_OK) == 0) {
- return 0;
- }
- return -EBADF;
- }
- return 1;
-}
-
-static void statsdNoteDrop(int error, int tag) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
- atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
-}
-
-static int statsdIsClosed() {
- if (atomic_load(&statsdLoggerWrite.sock) < 0) {
- return 1;
- }
- return 0;
-}
-
-static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
- ssize_t ret;
- int sock;
- static const unsigned headerLength = 1;
- struct iovec newVec[nr + headerLength];
- android_log_header_t header;
- size_t i, payloadSize;
-
- sock = atomic_load(&statsdLoggerWrite.sock);
- if (sock < 0) switch (sock) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- break;
- default:
- return -EBADF;
- }
- /*
- * struct {
- * // what we provide to socket
- * android_log_header_t header;
- * // caller provides
- * union {
- * struct {
- * char prio;
- * char payload[];
- * } string;
- * struct {
- * uint32_t tag
- * char payload[];
- * } binary;
- * };
- * };
- */
-
- header.tid = gettid();
- header.realtime.tv_sec = ts->tv_sec;
- header.realtime.tv_nsec = ts->tv_nsec;
-
- newVec[0].iov_base = (unsigned char*)&header;
- newVec[0].iov_len = sizeof(header);
-
- // If we dropped events before, try to tell statsd.
- if (sock >= 0) {
- int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_long_t buffer;
- header.id = LOG_ID_STATS;
- // store the last log error in the tag field. This tag field is not used by statsd.
- buffer.header.tag = atomic_load(&log_error);
- buffer.payload.type = EVENT_TYPE_LONG;
- // format:
- // |atom_tag|dropped_count|
- int64_t composed_long = atomic_load(&atom_tag);
- // Send 2 int32's via an int64.
- composed_long = ((composed_long << 32) | ((int64_t)snapshot));
- buffer.payload.data = composed_long;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
- }
- }
- }
-
- header.id = LOG_ID_STATS;
-
- for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
- newVec[i].iov_base = vec[i - headerLength].iov_base;
- payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
-
- if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
- newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
- if (newVec[i].iov_len) {
- ++i;
- }
- break;
- }
- }
-
- /*
- * The write below could be lost, but will never block.
- *
- * ENOTCONN occurs if statsd has died.
- * ENOENT occurs if statsd is not running and socket is missing.
- * ECONNREFUSED occurs if we can not reconnect to statsd.
- * EAGAIN occurs if statsd is overloaded.
- */
- if (sock < 0) {
- ret = sock;
- } else {
- ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
- if (ret < 0) {
- ret = -errno;
- }
- }
- switch (ret) {
- case -ENOTCONN:
- case -ECONNREFUSED:
- case -ENOENT:
- if (statd_writer_trylock()) {
- return ret; /* in a signal handler? try again when less stressed
- */
- }
- __statsdClose(ret);
- ret = statsdOpen();
- statsd_writer_init_unlock();
-
- if (ret < 0) {
- return ret;
- }
-
- ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));
- if (ret < 0) {
- ret = -errno;
- }
- /* FALLTHRU */
- default:
- break;
- }
-
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- }
-
- return ret;
-}
diff --git a/libstats/socket/statsd_writer.h b/libstats/socket/statsd_writer.h
deleted file mode 100644
index 562bda5..0000000
--- a/libstats/socket/statsd_writer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018, 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.
- */
-
-#ifndef ANDROID_STATS_LOG_STATS_WRITER_H
-#define ANDROID_STATS_LOG_STATS_WRITER_H
-
-#include <pthread.h>
-#include <stdatomic.h>
-#include <sys/socket.h>
-
-/**
- * Internal lock should not be exposed. This is bad design.
- * TODO: rewrite it in c++ code and encapsulate the functionality in a
- * StatsdWriter class.
- */
-void statsd_writer_init_lock();
-int statsd_writer_init_trylock();
-void statsd_writer_init_unlock();
-
-struct android_log_transport_write {
- const char* name; /* human name to describe the transport */
- atomic_int sock;
- int (*available)(); /* Does not cause resources to be taken */
- int (*open)(); /* can be called multiple times, reusing current resources */
- void (*close)(); /* free up resources */
- /* write log to transport, returns number of bytes propagated, or -errno */
- int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
- /* note one log drop */
- void (*noteDrop)(int error, int tag);
- /* checks if the socket is closed */
- int (*isClosed)();
-};
-
-#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp
deleted file mode 100644
index 9a1fac8..0000000
--- a/libstats/socket/tests/stats_event_test.cpp
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "stats_event.h"
-#include <gtest/gtest.h>
-#include <utils/SystemClock.h>
-
-// Keep in sync with stats_event.c. Consider moving to separate header file to avoid duplication.
-/* ERRORS */
-#define ERROR_NO_TIMESTAMP 0x1
-#define ERROR_NO_ATOM_ID 0x2
-#define ERROR_OVERFLOW 0x4
-#define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
-#define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
-#define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
-#define ERROR_INVALID_ANNOTATION_ID 0x40
-#define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
-#define ERROR_TOO_MANY_ANNOTATIONS 0x100
-#define ERROR_TOO_MANY_FIELDS 0x200
-#define ERROR_INVALID_VALUE_TYPE 0x400
-#define ERROR_STRING_NOT_NULL_TERMINATED 0x800
-#define ERROR_ATOM_ID_INVALID_POSITION 0x2000
-
-/* TYPE IDS */
-#define INT32_TYPE 0x00
-#define INT64_TYPE 0x01
-#define STRING_TYPE 0x02
-#define LIST_TYPE 0x03
-#define FLOAT_TYPE 0x04
-#define BOOL_TYPE 0x05
-#define BYTE_ARRAY_TYPE 0x06
-#define OBJECT_TYPE 0x07
-#define KEY_VALUE_PAIRS_TYPE 0x08
-#define ATTRIBUTION_CHAIN_TYPE 0x09
-#define ERROR_TYPE 0x0F
-
-using std::string;
-using std::vector;
-
-// Side-effect: this function moves the start of the buffer past the read value
-template <class T>
-T readNext(uint8_t** buffer) {
- T value;
- if ((reinterpret_cast<uintptr_t>(*buffer) % alignof(T)) == 0) {
- value = *(T*)(*buffer);
- } else {
- memcpy(&value, *buffer, sizeof(T));
- }
- *buffer += sizeof(T);
- return value;
-}
-
-void checkTypeHeader(uint8_t** buffer, uint8_t typeId, uint8_t numAnnotations = 0) {
- uint8_t typeHeader = (numAnnotations << 4) | typeId;
- EXPECT_EQ(readNext<uint8_t>(buffer), typeHeader);
-}
-
-template <class T>
-void checkScalar(uint8_t** buffer, T expectedValue) {
- EXPECT_EQ(readNext<T>(buffer), expectedValue);
-}
-
-void checkString(uint8_t** buffer, const string& expectedString) {
- uint32_t size = readNext<uint32_t>(buffer);
- string parsedString((char*)(*buffer), size);
- EXPECT_EQ(parsedString, expectedString);
- *buffer += size; // move buffer past string we just read
-}
-
-void checkByteArray(uint8_t** buffer, const vector<uint8_t>& expectedByteArray) {
- uint32_t size = readNext<uint32_t>(buffer);
- vector<uint8_t> parsedByteArray(*buffer, *buffer + size);
- EXPECT_EQ(parsedByteArray, expectedByteArray);
- *buffer += size; // move buffer past byte array we just read
-}
-
-template <class T>
-void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T annotationValue) {
- EXPECT_EQ(readNext<uint8_t>(buffer), annotationId);
- EXPECT_EQ(readNext<uint8_t>(buffer), typeId);
- checkScalar<T>(buffer, annotationValue);
-}
-
-void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime,
- uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) {
- // All events start with OBJECT_TYPE id.
- checkTypeHeader(buffer, OBJECT_TYPE);
-
- // We increment by 2 because the number of elements listed in the
- // serialization accounts for the timestamp and atom id as well.
- checkScalar(buffer, static_cast<uint8_t>(numElements + 2));
-
- // Check timestamp
- checkTypeHeader(buffer, INT64_TYPE);
- int64_t timestamp = readNext<int64_t>(buffer);
- EXPECT_GE(timestamp, startTime);
- EXPECT_LE(timestamp, endTime);
-
- // Check atom id
- checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations);
- checkScalar(buffer, atomId);
-}
-
-TEST(StatsEventTest, TestScalars) {
- uint32_t atomId = 100;
- int32_t int32Value = -5;
- int64_t int64Value = -2 * android::elapsedRealtimeNano();
- float floatValue = 2.0;
- bool boolValue = false;
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeInt32(event, int32Value);
- AStatsEvent_writeInt64(event, int64Value);
- AStatsEvent_writeFloat(event, floatValue);
- AStatsEvent_writeBool(event, boolValue);
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/4, startTime, endTime, atomId);
-
- // check int32 element
- checkTypeHeader(&buffer, INT32_TYPE);
- checkScalar(&buffer, int32Value);
-
- // check int64 element
- checkTypeHeader(&buffer, INT64_TYPE);
- checkScalar(&buffer, int64Value);
-
- // check float element
- checkTypeHeader(&buffer, FLOAT_TYPE);
- checkScalar(&buffer, floatValue);
-
- // check bool element
- checkTypeHeader(&buffer, BOOL_TYPE);
- checkScalar(&buffer, boolValue);
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestStrings) {
- uint32_t atomId = 100;
- string str = "test_string";
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeString(event, str.c_str());
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
- checkTypeHeader(&buffer, STRING_TYPE);
- checkString(&buffer, str);
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestByteArrays) {
- uint32_t atomId = 100;
- vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeByteArray(event, message.data(), message.size());
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
- checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
- checkByteArray(&buffer, message);
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAttributionChains) {
- uint32_t atomId = 100;
-
- uint8_t numNodes = 50;
- uint32_t uids[numNodes];
- vector<string> tags(numNodes); // storage that cTag elements point to
- const char* cTags[numNodes];
- for (int i = 0; i < (int)numNodes; i++) {
- uids[i] = i;
- tags.push_back("test" + std::to_string(i));
- cTags[i] = tags[i].c_str();
- }
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeAttributionChain(event, uids, cTags, numNodes);
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
-
- checkTypeHeader(&buffer, ATTRIBUTION_CHAIN_TYPE);
- checkScalar(&buffer, numNodes);
- for (int i = 0; i < numNodes; i++) {
- checkScalar(&buffer, uids[i]);
- checkString(&buffer, tags[i]);
- }
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestFieldAnnotations) {
- uint32_t atomId = 100;
-
- // first element information
- bool boolValue = false;
- uint8_t boolAnnotation1Id = 1;
- uint8_t boolAnnotation2Id = 2;
- bool boolAnnotation1Value = true;
- int32_t boolAnnotation2Value = 3;
-
- // second element information
- float floatValue = -5.0;
- uint8_t floatAnnotation1Id = 3;
- uint8_t floatAnnotation2Id = 4;
- int32_t floatAnnotation1Value = 8;
- bool floatAnnotation2Value = false;
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_writeBool(event, boolValue);
- AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value);
- AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value);
- AStatsEvent_writeFloat(event, floatValue);
- AStatsEvent_addInt32Annotation(event, floatAnnotation1Id, floatAnnotation1Value);
- AStatsEvent_addBoolAnnotation(event, floatAnnotation2Id, floatAnnotation2Value);
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/2, startTime, endTime, atomId);
-
- // check first element
- checkTypeHeader(&buffer, BOOL_TYPE, /*numAnnotations=*/2);
- checkScalar(&buffer, boolValue);
- checkAnnotation(&buffer, boolAnnotation1Id, BOOL_TYPE, boolAnnotation1Value);
- checkAnnotation(&buffer, boolAnnotation2Id, INT32_TYPE, boolAnnotation2Value);
-
- // check second element
- checkTypeHeader(&buffer, FLOAT_TYPE, /*numAnnotations=*/2);
- checkScalar(&buffer, floatValue);
- checkAnnotation(&buffer, floatAnnotation1Id, INT32_TYPE, floatAnnotation1Value);
- checkAnnotation(&buffer, floatAnnotation2Id, BOOL_TYPE, floatAnnotation2Value);
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAtomLevelAnnotations) {
- uint32_t atomId = 100;
- // atom-level annotation information
- uint8_t boolAnnotationId = 1;
- uint8_t int32AnnotationId = 2;
- bool boolAnnotationValue = false;
- int32_t int32AnnotationValue = 5;
-
- float fieldValue = -3.5;
-
- int64_t startTime = android::elapsedRealtimeNano();
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue);
- AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue);
- AStatsEvent_writeFloat(event, fieldValue);
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId,
- /*numAtomLevelAnnotations=*/2);
-
- // check atom-level annotations
- checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue);
- checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue);
-
- // check first element
- checkTypeHeader(&buffer, FLOAT_TYPE);
- checkScalar(&buffer, fieldValue);
-
- EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestNoAtomIdError) {
- AStatsEvent* event = AStatsEvent_obtain();
- // Don't set the atom id in order to trigger the error.
- AStatsEvent_build(event);
-
- uint32_t errors = AStatsEvent_getErrors(event);
- EXPECT_EQ(errors & ERROR_NO_ATOM_ID, ERROR_NO_ATOM_ID);
-
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestPushOverflowError) {
- const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- const int writeCount = 120; // Number of times to write str in the event.
-
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
-
- // Add str to the event 120 times. Each str takes >35 bytes so this will
- // overflow the 4068 byte buffer.
- // We want to keep writeCount less than 127 to avoid hitting
- // ERROR_TOO_MANY_FIELDS.
- for (int i = 0; i < writeCount; i++) {
- AStatsEvent_writeString(event, str);
- }
- AStatsEvent_write(event);
-
- uint32_t errors = AStatsEvent_getErrors(event);
- EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
-
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestPullOverflowError) {
- const uint32_t atomId = 10100;
- const vector<uint8_t> bytes(430 /* number of elements */, 1 /* value of each element */);
- const int writeCount = 120; // Number of times to write bytes in the event.
-
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
-
- // Add bytes to the event 120 times. Size of bytes is 430 so this will
- // overflow the 50 KB pulled event buffer.
- // We want to keep writeCount less than 127 to avoid hitting
- // ERROR_TOO_MANY_FIELDS.
- for (int i = 0; i < writeCount; i++) {
- AStatsEvent_writeByteArray(event, bytes.data(), bytes.size());
- }
- AStatsEvent_build(event);
-
- uint32_t errors = AStatsEvent_getErrors(event);
- EXPECT_EQ(errors & ERROR_OVERFLOW, ERROR_OVERFLOW);
-
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestLargePull) {
- const uint32_t atomId = 100;
- const string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- const int writeCount = 120; // Number of times to write str in the event.
- const int64_t startTime = android::elapsedRealtimeNano();
-
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
-
- // Add str to the event 120 times.
- // We want to keep writeCount less than 127 to avoid hitting
- // ERROR_TOO_MANY_FIELDS.
- for (int i = 0; i < writeCount; i++) {
- AStatsEvent_writeString(event, str.c_str());
- }
- AStatsEvent_build(event);
- int64_t endTime = android::elapsedRealtimeNano();
-
- size_t bufferSize;
- uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
- uint8_t* bufferEnd = buffer + bufferSize;
-
- checkMetadata(&buffer, writeCount, startTime, endTime, atomId);
-
- // Check all instances of str have been written.
- for (int i = 0; i < writeCount; i++) {
- checkTypeHeader(&buffer, STRING_TYPE);
- checkString(&buffer, str);
- }
-
- EXPECT_EQ(buffer, bufferEnd); // Ensure that we have read the entire buffer.
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestAtomIdInvalidPositionError) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_writeInt32(event, 0);
- AStatsEvent_setAtomId(event, 100);
- AStatsEvent_writeBool(event, true);
- AStatsEvent_build(event);
-
- uint32_t errors = AStatsEvent_getErrors(event);
- EXPECT_EQ(errors & ERROR_ATOM_ID_INVALID_POSITION, ERROR_ATOM_ID_INVALID_POSITION);
-
- AStatsEvent_release(event);
-}
-
-TEST(StatsEventTest, TestOverwriteTimestamp) {
- uint32_t atomId = 100;
- int64_t expectedTimestamp = 0x123456789;
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, atomId);
- AStatsEvent_overwriteTimestamp(event, expectedTimestamp);
- AStatsEvent_build(event);
-
- uint8_t* buffer = AStatsEvent_getBuffer(event, NULL);
-
- // Make sure that the timestamp is being overwritten.
- checkMetadata(&buffer, /*numElements=*/0, /*startTime=*/expectedTimestamp,
- /*endTime=*/expectedTimestamp, atomId);
-
- EXPECT_EQ(AStatsEvent_getErrors(event), 0);
- AStatsEvent_release(event);
-}
diff --git a/libstats/socket/tests/stats_writer_test.cpp b/libstats/socket/tests/stats_writer_test.cpp
deleted file mode 100644
index 749599f..0000000
--- a/libstats/socket/tests/stats_writer_test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 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.
- */
-
-#include <gtest/gtest.h>
-#include "stats_buffer_writer.h"
-#include "stats_event.h"
-#include "stats_socket.h"
-
-TEST(StatsWriterTest, TestSocketClose) {
- AStatsEvent* event = AStatsEvent_obtain();
- AStatsEvent_setAtomId(event, 100);
- AStatsEvent_writeInt32(event, 5);
- int successResult = AStatsEvent_write(event);
- AStatsEvent_release(event);
-
- // In the case of a successful write, we return the number of bytes written.
- EXPECT_GT(successResult, 0);
- EXPECT_FALSE(stats_log_is_closed());
-
- AStatsSocket_close();
-
- EXPECT_TRUE(stats_log_is_closed());
-}
diff --git a/property_service/OWNERS b/property_service/OWNERS
index babbe4d..7529cb9 100644
--- a/property_service/OWNERS
+++ b/property_service/OWNERS
@@ -1 +1 @@
-tomcherry@google.com
+include platform/system/core:/janitors/OWNERS
diff --git a/qemu_pipe/OWNERS b/qemu_pipe/OWNERS
index dbc1bf6..d67a329 100644
--- a/qemu_pipe/OWNERS
+++ b/qemu_pipe/OWNERS
@@ -1 +1,3 @@
bohu@google.com
+lfy@google.com
+rkir@google.com
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 17d71f7..73d1101 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -69,7 +69,7 @@
EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
- EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
endif
# Put it here instead of in init.rc module definition,
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index ca22eb8..5d0d673 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,5 +1,7 @@
+bowgotsai@google.com
ccross@google.com
+dvander@google.com
+elsk@google.com
jeffv@google.com
jiyong@google.com
smoreland@google.com
-tomcherry@google.com
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
index 85d2786..8c7caf3 100644
--- a/rootdir/avb/Android.bp
+++ b/rootdir/avb/Android.bp
@@ -18,3 +18,10 @@
"s-gsi.avbpubkey",
],
}
+
+filegroup {
+ name: "qcar-gsi_avbpubkey",
+ srcs: [
+ "qcar-gsi.avbpubkey",
+ ],
+}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index c8fc1d6..f71f205 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -80,4 +80,15 @@
include $(BUILD_PREBUILT)
+#######################################
+# qcar-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := qcar-gsi.avbpubkey
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
my_gsi_avb_keys_path :=
diff --git a/rootdir/avb/qcar-gsi.avbpubkey b/rootdir/avb/qcar-gsi.avbpubkey
new file mode 100644
index 0000000..ce56646
--- /dev/null
+++ b/rootdir/avb/qcar-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/init.rc b/rootdir/init.rc
index de608b1..049301d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -592,6 +592,7 @@
mkdir /metadata/ota 0700 root system
mkdir /metadata/ota/snapshots 0700 root system
mkdir /metadata/userspacereboot 0770 root system
+ mkdir /metadata/watchdog 0770 root system
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
@@ -635,27 +636,28 @@
mkdir /data/bootchart 0755 shell shell encryption=Require
bootchart start
- # Make sure that apexd is started in the default namespace
- enter_default_mount_ns
-
mkdir /data/vendor 0771 root root encryption=Require
mkdir /data/vendor_ce 0771 root root encryption=None
mkdir /data/vendor_de 0771 root root encryption=None
mkdir /data/vendor/hardware 0771 root root
# 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/vendor/tombstones 0771 root root
mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
start tombstoned
+ # Make sure that apexd is started in the default namespace
+ enter_default_mount_ns
+
# /data/apex is now available. Start apexd to scan and activate APEXes.
mkdir /data/apex 0755 root system encryption=None
mkdir /data/apex/active 0755 root system
mkdir /data/apex/backup 0700 root system
mkdir /data/apex/hashtree 0700 root system
mkdir /data/apex/sessions 0700 root system
- mkdir /data/app-staging 0750 system system encryption=DeleteIfNecessary
+ mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary
start apexd
# Avoid predictable entropy pool. Carry over entropy from previous boot.
@@ -781,8 +783,6 @@
# the following directory.
mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require
- mkdir /data/anr 0775 system system encryption=Require
-
# NFC: create data/nfc for nv storage
mkdir /data/nfc 0770 nfc nfc encryption=Require
mkdir /data/nfc/param 0770 nfc nfc
@@ -934,6 +934,9 @@
write /proc/sys/vm/dirty_expire_centisecs 200
write /proc/sys/vm/dirty_background_ratio 5
+on property:sys.boot_completed=1 && property:init.mount_debugfs=1
+ umount /sys/kernel/debug
+
on boot
# basic network init
ifup lo
@@ -1072,6 +1075,7 @@
class_start main
class_start late_start
setprop service.bootanim.exit 0
+ setprop service.bootanim.progress 0
start bootanim
on property:vold.decrypt=trigger_shutdown_framework
@@ -1173,6 +1177,7 @@
setprop sys.user.0.ce_available ""
setprop sys.shutdown.requested ""
setprop service.bootanim.exit ""
+ setprop service.bootanim.progress ""
on userspace-reboot-fs-remount
# Make sure that vold is running.
@@ -1200,6 +1205,10 @@
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
setprop sys.init.userspace_reboot.in_progress ""
+on early-init && property:init.mount_debugfs=1
+ mount debugfs debugfs /sys/kernel/debug
+ chmod 0755 /sys/kernel/debug
+
# Migrate tasks again in case kernel threads are created during boot
on property:sys.boot_completed=1
copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 1994bdb..64a64d2 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -1,3 +1,7 @@
+import /vendor/ueventd.rc
+import /odm/ueventd.rc
+import /ueventd.${ro.hardware}.rc
+
firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/
uevent_socket_rcvbuf_size 16M
@@ -42,7 +46,9 @@
/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
-/dev/dma_heap/system 0666 system system
+/dev/dma_heap/system 0444 system system
+/dev/dma_heap/system-uncached 0444 system system
+/dev/dma_heap/system-secure 0444 system system
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 0819c21..635966f 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -16,4 +16,8 @@
name: "trusty_confirmationui_fuzzer",
defaults: ["trusty_fuzzer_defaults"],
srcs: ["fuzz.cpp"],
+
+ // The initial corpus for this fuzzer was derived by dumping bytes from
+ // ConfirmationUI VTS.
+ corpus: ["corpus/*"],
}
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
new file mode 100644
index 0000000..53fe0c9
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
new file mode 100644
index 0000000..d627b01
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
new file mode 100644
index 0000000..bda80fd
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
new file mode 100644
index 0000000..f5854f8
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
new file mode 100644
index 0000000..0dc6e91
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
new file mode 100644
index 0000000..927d64d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-globJV b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
new file mode 100644
index 0000000..0b274bf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
new file mode 100644
index 0000000..87870ca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
new file mode 100644
index 0000000..b4a1c49
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
new file mode 100644
index 0000000..5adf905
--- /dev/null
+++ b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
Binary files differ
diff --git a/trusty/confirmationui/fuzz/fuzz.cpp b/trusty/confirmationui/fuzz/fuzz.cpp
index d285116..9d3008b 100644
--- a/trusty/confirmationui/fuzz/fuzz.cpp
+++ b/trusty/confirmationui/fuzz/fuzz.cpp
@@ -19,16 +19,50 @@
#include <assert.h>
#include <log/log.h>
#include <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
#include <trusty/fuzz/utils.h>
#include <unistd.h>
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
using android::trusty::fuzz::TrustyApp;
#define TIPC_DEV "/dev/trusty-ipc-dev0"
#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
+static struct uuid confirmationui_uuid = {
+ 0x7dee2364,
+ 0xc036,
+ 0x425b,
+ {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},
+};
+
+/* The format of the packets is as following:
+ * 16 bits (uint16_t, header) + payload bytes
+ * The 16 bits header spicify the number of bytes of payload (header excluded).
+ */
+struct data_packet {
+ uint16_t header;
+ uint8_t payload[];
+};
+
+static CoverageRecord record(TIPC_DEV, &confirmationui_uuid);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+ auto ret = record.Open();
+ assert(ret.ok());
+ return 0;
+}
+
+/* Each corpus contains one or more data packets. */
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static uint8_t buf[TIPC_MAX_MSG_SIZE];
+ size_t data_idx = 0;
+
+ ExtraCounters counters(&record);
+ counters.Reset();
TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);
auto ret = ta.Connect();
@@ -36,16 +70,22 @@
android::trusty::fuzz::Abort();
}
- /* Send message to confirmationui server */
- ret = ta.Write(data, size);
- if (!ret.ok()) {
- return -1;
- }
+ while (data_idx < size) {
+ struct data_packet* data_packet_ptr = (struct data_packet*)&data[data_idx];
+ size_t payload_size = data_packet_ptr->header;
+ data_idx += data_packet_ptr->header + sizeof(data_packet_ptr->header);
- /* Read message from confirmationui server */
- ret = ta.Read(&buf, sizeof(buf));
- if (!ret.ok()) {
- return -1;
+ /* Write message to confirmationui server */
+ ret = ta.Write(data_packet_ptr->payload, payload_size);
+ if (!ret.ok()) {
+ return -1;
+ }
+
+ /* Read message from confirmationui server */
+ ret = ta.Read(&buf, sizeof(buf));
+ if (!ret.ok()) {
+ return -1;
+ }
}
return 0;
diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp
index a4adf81..daa6f03 100644
--- a/trusty/coverage/Android.bp
+++ b/trusty/coverage/Android.bp
@@ -14,6 +14,7 @@
cc_library {
name: "libtrusty_coverage",
+ vendor_available: true,
srcs: [
"coverage.cpp",
],
@@ -21,11 +22,13 @@
"include",
],
static_libs: [
- "libtrusty_test",
+ "libtrusty",
],
+
shared_libs: [
"libbase",
"liblog",
+ "libdmabufheap",
],
}
@@ -36,10 +39,12 @@
],
static_libs: [
"libtrusty_coverage",
- "libtrusty_test",
+ "libtrusty",
],
shared_libs: [
"libbase",
"liblog",
+ "libdmabufheap",
],
+ require_root: true,
}
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
index 1162f42..185abe5 100644
--- a/trusty/coverage/coverage.cpp
+++ b/trusty/coverage/coverage.cpp
@@ -16,12 +16,17 @@
#define LOG_TAG "coverage"
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
#include <sys/mman.h>
#include <sys/uio.h>
#include <trusty/coverage/coverage.h>
+#include <trusty/coverage/record.h>
#include <trusty/coverage/tipc.h>
#include <trusty/tipc.h>
@@ -34,6 +39,7 @@
using android::base::ErrnoError;
using android::base::Error;
using std::string;
+using std::unique_ptr;
static inline uintptr_t RoundPageUp(uintptr_t val) {
return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
@@ -43,12 +49,29 @@
: tipc_dev_(std::move(tipc_dev)),
coverage_srv_fd_(-1),
uuid_(*uuid),
+ sancov_filename_(),
+ record_len_(0),
+ shm_(NULL),
+ shm_len_(0) {}
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid, string sancov_filename)
+ : tipc_dev_(std::move(tipc_dev)),
+ coverage_srv_fd_(-1),
+ uuid_(*uuid),
+ sancov_filename_(sancov_filename),
record_len_(0),
shm_(NULL),
shm_len_(0) {}
CoverageRecord::~CoverageRecord() {
if (shm_) {
+ if (sancov_filename_) {
+ auto res = SaveSancovFile(*sancov_filename_);
+ if (!res.ok()) {
+ ALOGE("Could not write sancov file for module: %s\n", sancov_filename_->c_str());
+ }
+ }
+
munmap((void*)shm_, shm_len_);
}
}
@@ -111,24 +134,23 @@
record_len_ = resp.open_args.record_len;
shm_len_ = RoundPageUp(record_len_);
- fd = memfd_create("trusty-coverage", 0);
+ BufferAllocator allocator;
+
+ fd = allocator.Alloc("system", shm_len_);
if (fd < 0) {
- return ErrnoError() << "failed to create memfd: ";
+ return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+ << " err code: " << fd;
}
- unique_fd memfd(fd);
+ unique_fd dma_buf(fd);
- if (ftruncate(memfd, shm_len_) < 0) {
- return ErrnoError() << "failed to resize memfd: ";
- }
-
- void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+ void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
if (shm == MAP_FAILED) {
return ErrnoError() << "failed to map memfd: ";
}
req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD;
req.share_record_args.shm_len = shm_len_;
- ret = Rpc(&req, memfd, &resp);
+ ret = Rpc(&req, dma_buf, &resp);
if (!ret.ok()) {
return Error() << "failed to send shared memory: ";
}
@@ -137,12 +159,59 @@
return {};
}
-void CoverageRecord::Reset() {
- for (size_t i = 0; i < shm_len_; i++) {
+void CoverageRecord::ResetFullRecord() {
+ auto header_region = GetRegionBounds(COV_START);
+ if (!header_region.ok()) {
+ // If the header cannot be parsed, we can't reset the proper region yet.
+ return;
+ }
+
+ for (size_t i = header_region->second; i < shm_len_; i++) {
*((volatile uint8_t*)shm_ + i) = 0;
}
}
+void CoverageRecord::ResetCounts() {
+ volatile uint8_t* begin = nullptr;
+ volatile uint8_t* end = nullptr;
+ GetRawCounts(&begin, &end);
+
+ for (volatile uint8_t* x = begin; x < end; x++) {
+ *x = 0;
+ }
+}
+
+void CoverageRecord::ResetPCs() {
+ volatile uintptr_t* begin = nullptr;
+ volatile uintptr_t* end = nullptr;
+ GetRawPCs(&begin, &end);
+
+ for (volatile uintptr_t* x = begin; x < end; x++) {
+ *x = 0;
+ }
+}
+
+Result<std::pair<size_t, size_t>> CoverageRecord::GetRegionBounds(uint32_t region_type) {
+ assert(shm_);
+
+ auto header = (volatile struct coverage_record_header*)shm_;
+
+ if (header->type != COV_START) {
+ return Error() << "Header not yet valid";
+ }
+
+ for (++header; header->type != COV_TOTAL_LENGTH; ++header) {
+ if (header->type == region_type) {
+ // Coverage record must end with a COV_TOTAL_LENGTH header entry, so
+ // it is always safe to read the next entry since we don't iterate
+ // over the COV_TOTAL_LENGTH entry.
+ return {{header->offset, (header + 1)->offset}};
+ }
+ }
+
+ return Error() << "Could not find coverage region type: " << region_type;
+}
+
void CoverageRecord::GetRawData(volatile void** begin, volatile void** end) {
assert(shm_);
@@ -150,7 +219,35 @@
*end = (uint8_t*)(*begin) + record_len_;
}
-uint64_t CoverageRecord::CountEdges() {
+void CoverageRecord::GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end) {
+ auto region = GetRegionBounds(COV_8BIT_COUNTERS);
+ if (!region.ok()) {
+ *begin = 0;
+ *end = 0;
+ return;
+ }
+
+ assert(region->second <= record_len_);
+
+ *begin = (volatile uint8_t*)shm_ + region->first;
+ *end = (volatile uint8_t*)shm_ + region->second;
+}
+
+void CoverageRecord::GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end) {
+ auto region = GetRegionBounds(COV_INSTR_PCS);
+ if (!region.ok()) {
+ *begin = 0;
+ *end = 0;
+ return;
+ }
+
+ assert(region->second <= record_len_);
+
+ *begin = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->first);
+ *end = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->second);
+}
+
+uint64_t CoverageRecord::TotalEdgeCounts() {
assert(shm_);
uint64_t counter = 0;
@@ -158,7 +255,7 @@
volatile uint8_t* begin = NULL;
volatile uint8_t* end = NULL;
- GetRawData((volatile void**)&begin, (volatile void**)&end);
+ GetRawCounts(&begin, &end);
for (volatile uint8_t* x = begin; x < end; x++) {
counter += *x;
@@ -167,6 +264,35 @@
return counter;
}
+Result<void> CoverageRecord::SaveSancovFile(const std::string& filename) {
+ android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+ if (!output_fd.ok()) {
+ return ErrnoError() << "Could not open sancov file";
+ }
+
+ uint64_t magic;
+ if (sizeof(uintptr_t) == 8) {
+ magic = 0xC0BFFFFFFFFFFF64;
+ } else if (sizeof(uintptr_t) == 4) {
+ magic = 0xC0BFFFFFFFFFFF32;
+ }
+ WriteFully(output_fd, &magic, sizeof(magic));
+
+ volatile uintptr_t* begin = nullptr;
+ volatile uintptr_t* end = nullptr;
+
+ GetRawPCs(&begin, &end);
+
+ for (volatile uintptr_t* pc_ptr = begin; pc_ptr < end; pc_ptr++) {
+ uintptr_t pc = *pc_ptr;
+ if (pc) {
+ WriteFully(output_fd, &pc, sizeof(pc));
+ }
+ }
+
+ return {};
+}
+
} // namespace coverage
} // namespace trusty
} // namespace android
diff --git a/trusty/coverage/coverage_test.cpp b/trusty/coverage/coverage_test.cpp
index d8df7a4..c1efca6 100644
--- a/trusty/coverage/coverage_test.cpp
+++ b/trusty/coverage/coverage_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
#include <trusty/coverage/coverage.h>
#include <trusty/tipc.h>
@@ -27,6 +28,7 @@
#define TIPC_DEV "/dev/trusty-ipc-dev0"
#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
namespace android {
namespace trusty {
@@ -54,8 +56,8 @@
};
TEST_F(CoverageTest, CoverageReset) {
- record_->Reset();
- auto counter = record_->CountEdges();
+ record_->ResetFullRecord();
+ auto counter = record_->TotalEdgeCounts();
ASSERT_EQ(counter, 0);
}
@@ -69,7 +71,7 @@
for (size_t i = 1; i < sizeof(magic) * 8; i++) {
/* Reset coverage */
- record_->Reset();
+ record_->ResetCounts();
/* Send message to test server */
uint32_t msg = magic & ~(mask << i);
@@ -81,10 +83,15 @@
ASSERT_EQ(rc, sizeof(msg));
/* Count number of non-unique blocks executed */
- auto counter = record_->CountEdges();
+ auto counter = record_->TotalEdgeCounts();
/* Each consecutive input should exercise more or same blocks */
ASSERT_GE(counter, high_watermark);
high_watermark = counter;
+
+ auto sancov_filename = android::base::StringPrintf(
+ "/data/local/tmp/" TEST_SRV_MODULE ".%d.sancov", getpid());
+ auto res = record_->SaveSancovFile(sancov_filename);
+ ASSERT_TRUE(res.ok());
}
ASSERT_GT(high_watermark, 0);
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h
index b61b959..ed723f6 100644
--- a/trusty/coverage/include/trusty/coverage/coverage.h
+++ b/trusty/coverage/include/trusty/coverage/coverage.h
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <string>
#include <android-base/result.h>
@@ -32,19 +33,44 @@
class CoverageRecord {
public:
+ /**
+ * Create a coverage record interface. Coverage will not be written to a
+ * sancov output file on completion.
+ */
CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+ /**
+ * Create a coverage record interface. On destruction, write this coverage
+ * to the given sancov filename.
+ */
+ CoverageRecord(std::string tipc_dev, struct uuid* uuid, std::string sancov_filename);
+
~CoverageRecord();
Result<void> Open();
- void Reset();
+ void ResetFullRecord();
+ void ResetCounts();
+ void ResetPCs();
void GetRawData(volatile void** begin, volatile void** end);
- uint64_t CountEdges();
+ void GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end);
+ void GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end);
+ uint64_t TotalEdgeCounts();
+
+ /**
+ * Save the current set of observed PCs to the given filename.
+ * The resulting .sancov file can be parsed via the LLVM sancov tool to see
+ * coverage statistics and visualize coverage.
+ */
+ Result<void> SaveSancovFile(const std::string& filename);
private:
Result<void> Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp);
+ Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
std::string tipc_dev_;
unique_fd coverage_srv_fd_;
struct uuid uuid_;
+ std::optional<std::string> sancov_filename_;
size_t record_len_;
volatile void* shm_;
size_t shm_len_;
diff --git a/trusty/coverage/include/trusty/coverage/record.h b/trusty/coverage/include/trusty/coverage/record.h
new file mode 100644
index 0000000..bfe06f3
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/record.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side:
+ * trusty/user/base/lib/coverage/common/include/lib/coverage/common/record.h */
+
+#pragma once
+
+#include <stdint.h>
+
+/**
+ * enum coverage_record_type - Coverage region header type
+ * @COV_START: Magic header start marker
+ * @COV_8BIT_COUNTERS: 8bit counter for each instrumentation point
+ * @COV_INSTR_PCS: Pointer length offset of each instrumentation point from the
+ * start of the binary
+ * @COV_TOTAL_LENGTH: Total length of the entire coverage record, must be the
+ * last header item.
+ *
+ * Describes the type of a region of the coverage record. See &struct
+ * coverage_record_header.
+ */
+enum coverage_record_type {
+ COV_START = 0x434f5652,
+ COV_8BIT_COUNTERS = 1,
+ COV_INSTR_PCS = 2,
+ COV_TOTAL_LENGTH = 0,
+};
+
+/**
+ * struct coverage_record_header - Header entry describing a region of the
+ * coverage record.
+ * @type: type of the region, must be one of @enum coverage_record_type
+ * @offset: offset from the beginning of the header to the start of the region
+ *
+ * Coverage records start with a header which is a list of struct
+ * coverage_record_header, beginning with an entry with type COV_START and
+ * terminated with an entry with type COV_TOTAL_LENGTH. Each of these header
+ * entries corresponds to a region of the record, with the offset indicating the
+ * offset of the start of that region from the beginning of the record (i.e. the
+ * beginning of the header). Each record type and offset is 32-bit field with
+ * native endianness. The first header item must be COV_START with a 0 offset.
+ * The COV_START entry should be initialized when the coverage header is
+ * complete and ready for consumption by the client, because coverage record
+ * initialization happens asynchronously. The final header item,
+ * COV_TOTAL_LENGTH, which must always be present, indicates the total length of
+ * the coverage record, including the header.
+ *
+ * Coverage regions should be contiguous, so the end of one region is the start
+ * of the next, and the coverage header must be in the same order as the regions
+ * in the record body. Thus we can compute the length of a region by subtracting
+ * the region's offset from the offset of the next header item.
+ */
+struct coverage_record_header {
+ uint32_t type;
+ uint32_t offset;
+};
diff --git a/trusty/fuzz/Android.bp b/trusty/fuzz/Android.bp
index 22d834d..ad13816 100644
--- a/trusty/fuzz/Android.bp
+++ b/trusty/fuzz/Android.bp
@@ -39,10 +39,10 @@
export_include_dirs: ["include"],
static_libs: [
"libFuzzer",
+ "libtrusty",
],
shared_libs: [
"libtrusty_coverage",
- "libtrusty_test",
"libbase",
"liblog",
],
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
index 3fc9f48..1e863ac 100644
--- a/trusty/fuzz/counters.cpp
+++ b/trusty/fuzz/counters.cpp
@@ -21,6 +21,7 @@
#include <trusty/fuzz/counters.h>
#include <android-base/logging.h>
+#include <log/log.h>
#include <trusty/coverage/coverage.h>
#include <trusty/coverage/tipc.h>
@@ -32,7 +33,8 @@
* We don't know how many counters the coverage record will contain. So, eyeball
* the size of this section.
*/
-__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[PAGE_SIZE];
+static const size_t kMaxNumCounters = 0x4000;
+__attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
namespace android {
namespace trusty {
@@ -42,9 +44,9 @@
assert(fuzzer::ExtraCountersBegin());
assert(fuzzer::ExtraCountersEnd());
- uint8_t* begin = NULL;
- uint8_t* end = NULL;
- record_->GetRawData((volatile void**)&begin, (volatile void**)&end);
+ volatile uint8_t* begin = NULL;
+ volatile uint8_t* end = NULL;
+ record_->GetRawCounts(&begin, &end);
assert(end - begin <= sizeof(counters));
}
@@ -53,7 +55,7 @@
}
void ExtraCounters::Reset() {
- record_->Reset();
+ record_->ResetCounts();
fuzzer::ClearExtraCounters();
}
@@ -61,9 +63,17 @@
volatile uint8_t* begin = NULL;
volatile uint8_t* end = NULL;
- record_->GetRawData((volatile void**)&begin, (volatile void**)&end);
+ record_->GetRawCounts(&begin, &end);
+ if (!begin || !end) {
+ ALOGE("Could not get raw counts from coverage record\n");
+ return;
+ }
size_t num_counters = end - begin;
+ if (num_counters > kMaxNumCounters) {
+ ALOGE("Too many counters (%zu) to fit in the extra counters section!\n", num_counters);
+ num_counters = kMaxNumCounters;
+ }
for (size_t i = 0; i < num_counters; i++) {
*(counters + i) = *(begin + i);
}
diff --git a/trusty/gatekeeper/fuzz/fuzz.cpp b/trusty/gatekeeper/fuzz/fuzz.cpp
index c0e8abb..4d885ce 100644
--- a/trusty/gatekeeper/fuzz/fuzz.cpp
+++ b/trusty/gatekeeper/fuzz/fuzz.cpp
@@ -30,6 +30,7 @@
#define TIPC_DEV "/dev/trusty-ipc-dev0"
#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MODULE_NAME "gatekeeper.syms.elf"
/* Gatekeeper TA's UUID is 38ba0cdc-df0e-11e4-9869-233fb6ae4795 */
static struct uuid gatekeeper_uuid = {
@@ -39,7 +40,7 @@
{0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95},
};
-static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid);
+static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid, GATEKEEPER_MODULE_NAME);
extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
auto ret = record.Open();
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 98cbcc3..7184e4d 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -221,10 +221,10 @@
Return<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
if (data.size() == 0) return ErrorCode::OK;
- AddEntropyRequest request;
+ AddEntropyRequest request(impl_->message_version());
request.random_data.Reinitialize(data.data(), data.size());
- AddEntropyResponse response;
+ AddEntropyResponse response(impl_->message_version());
impl_->AddRngEntropy(request, &response);
return legacy_enum_conversion(response.error);
@@ -232,10 +232,10 @@
Return<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
generateKey_cb _hidl_cb) {
- GenerateKeyRequest request;
+ GenerateKeyRequest request(impl_->message_version());
request.key_description.Reinitialize(KmParamSet(keyParams));
- GenerateKeyResponse response;
+ GenerateKeyResponse response(impl_->message_version());
impl_->GenerateKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -253,11 +253,11 @@
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
getKeyCharacteristics_cb _hidl_cb) {
- GetKeyCharacteristicsRequest request;
+ GetKeyCharacteristicsRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- GetKeyCharacteristicsResponse response;
+ GetKeyCharacteristicsResponse response(impl_->message_version());
impl_->GetKeyCharacteristics(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -273,12 +273,12 @@
KeyFormat keyFormat,
const hidl_vec<uint8_t>& keyData,
importKey_cb _hidl_cb) {
- ImportKeyRequest request;
+ ImportKeyRequest request(impl_->message_version());
request.key_description.Reinitialize(KmParamSet(params));
request.key_format = legacy_enum_conversion(keyFormat);
request.SetKeyMaterial(keyData.data(), keyData.size());
- ImportKeyResponse response;
+ ImportKeyResponse response(impl_->message_version());
impl_->ImportKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -297,12 +297,12 @@
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
exportKey_cb _hidl_cb) {
- ExportKeyRequest request;
+ ExportKeyRequest request(impl_->message_version());
request.key_format = legacy_enum_conversion(exportFormat);
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- ExportKeyResponse response;
+ ExportKeyResponse response(impl_->message_version());
impl_->ExportKey(request, &response);
hidl_vec<uint8_t> resultKeyBlob;
@@ -316,11 +316,11 @@
Return<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
const hidl_vec<KeyParameter>& attestParams,
attestKey_cb _hidl_cb) {
- AttestKeyRequest request;
+ AttestKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
request.attest_params.Reinitialize(KmParamSet(attestParams));
- AttestKeyResponse response;
+ AttestKeyResponse response(impl_->message_version());
impl_->AttestKey(request, &response);
hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -334,11 +334,11 @@
Return<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
const hidl_vec<KeyParameter>& upgradeParams,
upgradeKey_cb _hidl_cb) {
- UpgradeKeyRequest request;
+ UpgradeKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
- UpgradeKeyResponse response;
+ UpgradeKeyResponse response(impl_->message_version());
impl_->UpgradeKey(request, &response);
if (response.error == KM_ERROR_OK) {
@@ -350,18 +350,18 @@
}
Return<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
- DeleteKeyRequest request;
+ DeleteKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- DeleteKeyResponse response;
+ DeleteKeyResponse response(impl_->message_version());
impl_->DeleteKey(request, &response);
return legacy_enum_conversion(response.error);
}
Return<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {
- DeleteAllKeysRequest request;
- DeleteAllKeysResponse response;
+ DeleteAllKeysRequest request(impl_->message_version());
+ DeleteAllKeysResponse response(impl_->message_version());
impl_->DeleteAllKeys(request, &response);
return legacy_enum_conversion(response.error);
@@ -374,15 +374,15 @@
Return<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,
const hidl_vec<KeyParameter>& inParams,
begin_cb _hidl_cb) {
- BeginOperationRequest request;
+ BeginOperationRequest request(impl_->message_version());
request.purpose = legacy_enum_conversion(purpose);
request.SetKeyMaterial(key.data(), key.size());
request.additional_params.Reinitialize(KmParamSet(inParams));
- BeginOperationResponse response;
+ BeginOperationResponse response(impl_->message_version());
impl_->BeginOperation(request, &response);
- hidl_vec<KeyParameter> resultParams;
+ hidl_vec<KeyParameter> resultParams(impl_->message_version());
if (response.error == KM_ERROR_OK) {
resultParams = kmParamSet2Hidl(response.output_params);
}
@@ -394,8 +394,8 @@
Return<void> TrustyKeymaster3Device::update(uint64_t operationHandle,
const hidl_vec<KeyParameter>& inParams,
const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {
- UpdateOperationRequest request;
- UpdateOperationResponse response;
+ UpdateOperationRequest request(impl_->message_version());
+ UpdateOperationResponse response(impl_->message_version());
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
uint32_t resultConsumed = 0;
@@ -431,13 +431,13 @@
const hidl_vec<uint8_t>& input,
const hidl_vec<uint8_t>& signature,
finish_cb _hidl_cb) {
- FinishOperationRequest request;
+ FinishOperationRequest request(impl_->message_version());
request.op_handle = operationHandle;
request.input.Reinitialize(input.data(), input.size());
request.signature.Reinitialize(signature.data(), signature.size());
request.additional_params.Reinitialize(KmParamSet(inParams));
- FinishOperationResponse response;
+ FinishOperationResponse response(impl_->message_version());
impl_->FinishOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -451,10 +451,10 @@
}
Return<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {
- AbortOperationRequest request;
+ AbortOperationRequest request(impl_->message_version());
request.op_handle = operationHandle;
- AbortOperationResponse response;
+ AbortOperationResponse response(impl_->message_version());
impl_->AbortOperation(request, &response);
return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index ec2ba12..73ad6ae 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -284,7 +284,7 @@
Return<void> TrustyKeymaster4Device::computeSharedHmac(
const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {
- ComputeSharedHmacRequest request;
+ ComputeSharedHmacRequest request(impl_->message_version());
request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
request.params_array.num_params = params.size();
for (size_t i = 0; i < params.size(); ++i) {
@@ -309,7 +309,7 @@
Return<void> TrustyKeymaster4Device::verifyAuthorization(
uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,
const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {
- VerifyAuthorizationRequest request;
+ VerifyAuthorizationRequest request(impl_->message_version());
request.challenge = challenge;
request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));
request.auth_token.challenge = authToken.challenge;
@@ -336,10 +336,10 @@
Return<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {
if (data.size() == 0) return ErrorCode::OK;
- AddEntropyRequest request;
+ AddEntropyRequest request(impl_->message_version());
request.random_data.Reinitialize(data.data(), data.size());
- AddEntropyResponse response;
+ AddEntropyResponse response(impl_->message_version());
impl_->AddRngEntropy(request, &response);
return legacy_enum_conversion(response.error);
@@ -347,10 +347,10 @@
Return<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,
generateKey_cb _hidl_cb) {
- GenerateKeyRequest request;
+ GenerateKeyRequest request(impl_->message_version());
request.key_description.Reinitialize(KmParamSet(keyParams));
- GenerateKeyResponse response;
+ GenerateKeyResponse response(impl_->message_version());
impl_->GenerateKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -368,11 +368,11 @@
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
getKeyCharacteristics_cb _hidl_cb) {
- GetKeyCharacteristicsRequest request;
+ GetKeyCharacteristicsRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- GetKeyCharacteristicsResponse response;
+ GetKeyCharacteristicsResponse response(impl_->message_version());
impl_->GetKeyCharacteristics(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -388,12 +388,12 @@
KeyFormat keyFormat,
const hidl_vec<uint8_t>& keyData,
importKey_cb _hidl_cb) {
- ImportKeyRequest request;
+ ImportKeyRequest request(impl_->message_version());
request.key_description.Reinitialize(KmParamSet(params));
request.key_format = legacy_enum_conversion(keyFormat);
request.SetKeyMaterial(keyData.data(), keyData.size());
- ImportKeyResponse response;
+ ImportKeyResponse response(impl_->message_version());
impl_->ImportKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -411,7 +411,7 @@
const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,
const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,
uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {
- ImportWrappedKeyRequest request;
+ ImportWrappedKeyRequest request(impl_->message_version());
request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
@@ -419,7 +419,7 @@
request.password_sid = passwordSid;
request.biometric_sid = biometricSid;
- ImportWrappedKeyResponse response;
+ ImportWrappedKeyResponse response(impl_->message_version());
impl_->ImportWrappedKey(request, &response);
KeyCharacteristics resultCharacteristics;
@@ -438,12 +438,12 @@
const hidl_vec<uint8_t>& clientId,
const hidl_vec<uint8_t>& appData,
exportKey_cb _hidl_cb) {
- ExportKeyRequest request;
+ ExportKeyRequest request(impl_->message_version());
request.key_format = legacy_enum_conversion(exportFormat);
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
addClientAndAppData(clientId, appData, &request.additional_params);
- ExportKeyResponse response;
+ ExportKeyResponse response(impl_->message_version());
impl_->ExportKey(request, &response);
hidl_vec<uint8_t> resultKeyBlob;
@@ -457,11 +457,11 @@
Return<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,
const hidl_vec<KeyParameter>& attestParams,
attestKey_cb _hidl_cb) {
- AttestKeyRequest request;
+ AttestKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());
request.attest_params.Reinitialize(KmParamSet(attestParams));
- AttestKeyResponse response;
+ AttestKeyResponse response(impl_->message_version());
impl_->AttestKey(request, &response);
hidl_vec<hidl_vec<uint8_t>> resultCertChain;
@@ -475,11 +475,11 @@
Return<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,
const hidl_vec<KeyParameter>& upgradeParams,
upgradeKey_cb _hidl_cb) {
- UpgradeKeyRequest request;
+ UpgradeKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
- UpgradeKeyResponse response;
+ UpgradeKeyResponse response(impl_->message_version());
impl_->UpgradeKey(request, &response);
if (response.error == KM_ERROR_OK) {
@@ -491,18 +491,18 @@
}
Return<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {
- DeleteKeyRequest request;
+ DeleteKeyRequest request(impl_->message_version());
request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
- DeleteKeyResponse response;
+ DeleteKeyResponse response(impl_->message_version());
impl_->DeleteKey(request, &response);
return legacy_enum_conversion(response.error);
}
Return<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {
- DeleteAllKeysRequest request;
- DeleteAllKeysResponse response;
+ DeleteAllKeysRequest request(impl_->message_version());
+ DeleteAllKeysResponse response(impl_->message_version());
impl_->DeleteAllKeys(request, &response);
return legacy_enum_conversion(response.error);
@@ -516,12 +516,12 @@
const hidl_vec<KeyParameter>& inParams,
const HardwareAuthToken& authToken, begin_cb _hidl_cb) {
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
- BeginOperationRequest request;
+ BeginOperationRequest request(impl_->message_version());
request.purpose = legacy_enum_conversion(purpose);
request.SetKeyMaterial(key.data(), key.size());
request.additional_params.Reinitialize(KmParamSet(extendedParams));
- BeginOperationResponse response;
+ BeginOperationResponse response(impl_->message_version());
impl_->BeginOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -540,8 +540,8 @@
const VerificationToken& verificationToken,
update_cb _hidl_cb) {
(void)verificationToken;
- UpdateOperationRequest request;
- UpdateOperationResponse response;
+ UpdateOperationRequest request(impl_->message_version());
+ UpdateOperationResponse response(impl_->message_version());
hidl_vec<KeyParameter> resultParams;
hidl_vec<uint8_t> resultBlob;
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
@@ -581,14 +581,14 @@
const VerificationToken& verificationToken,
finish_cb _hidl_cb) {
(void)verificationToken;
- FinishOperationRequest request;
+ FinishOperationRequest request(impl_->message_version());
hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);
request.op_handle = operationHandle;
request.input.Reinitialize(input.data(), input.size());
request.signature.Reinitialize(signature.data(), signature.size());
request.additional_params.Reinitialize(KmParamSet(extendedParams));
- FinishOperationResponse response;
+ FinishOperationResponse response(impl_->message_version());
impl_->FinishOperation(request, &response);
hidl_vec<KeyParameter> resultParams;
@@ -602,10 +602,10 @@
}
Return<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {
- AbortOperationRequest request;
+ AbortOperationRequest request(impl_->message_version());
request.op_handle = operationHandle;
- AbortOperationResponse response;
+ AbortOperationResponse response(impl_->message_version());
impl_->AbortOperation(request, &response);
return legacy_enum_conversion(response.error);
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 750a9d7..5690031 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -31,11 +31,35 @@
return err;
}
- ConfigureRequest req;
+ // Try GetVersion2 first.
+ GetVersion2Request versionReq;
+ GetVersion2Response versionRsp = GetVersion2(versionReq);
+ if (versionRsp.error != KM_ERROR_OK) {
+ ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
+
+ GetVersionRequest versionReq;
+ GetVersionResponse versionRsp;
+ GetVersion(versionReq, &versionRsp);
+ if (versionRsp.error != KM_ERROR_OK) {
+ ALOGE("Failed to get TA version %d", versionRsp.error);
+ return -1;
+ } else {
+ keymaster_error_t error;
+ message_version_ = NegotiateMessageVersion(versionRsp, &error);
+ if (error != KM_ERROR_OK) {
+ ALOGE("Failed to negotiate message version %d", error);
+ return -1;
+ }
+ }
+ } else {
+ message_version_ = NegotiateMessageVersion(versionReq, versionRsp);
+ }
+
+ ConfigureRequest req(message_version());
req.os_version = GetOsVersion();
req.os_patchlevel = GetOsPatchlevel();
- ConfigureResponse rsp;
+ ConfigureResponse rsp(message_version());
Configure(req, &rsp);
if (rsp.error != KM_ERROR_OK) {
@@ -52,7 +76,7 @@
trusty_keymaster_disconnect();
}
-static void ForwardCommand(enum keymaster_command command, const Serializable& req,
+static void ForwardCommand(enum keymaster_command command, const KeymasterMessage& req,
KeymasterResponse* rsp) {
keymaster_error_t err;
err = trusty_keymaster_send(command, req, rsp);
@@ -173,25 +197,30 @@
}
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
- // Empty buffer to allow ForwardCommand to have something to serialize
- Buffer request;
- GetHmacSharingParametersResponse response;
+ GetHmacSharingParametersRequest request(message_version());
+ GetHmacSharingParametersResponse response(message_version());
ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
return response;
}
ComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(
const ComputeSharedHmacRequest& request) {
- ComputeSharedHmacResponse response;
+ ComputeSharedHmacResponse response(message_version());
ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);
return response;
}
VerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(
const VerifyAuthorizationRequest& request) {
- VerifyAuthorizationResponse response;
+ VerifyAuthorizationResponse response(message_version());
ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);
return response;
}
+GetVersion2Response TrustyKeymaster::GetVersion2(const GetVersion2Request& request) {
+ GetVersion2Response response(message_version());
+ ForwardCommand(KM_GET_VERSION_2, request, &response);
+ return response;
+}
+
} // namespace keymaster
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
new file mode 100644
index 0000000..da9f9ec
--- /dev/null
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -0,0 +1,19 @@
+// Copyright (C) 2020 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.
+
+cc_fuzz {
+ name: "trusty_keymaster_fuzzer",
+ defaults: ["trusty_fuzzer_defaults"],
+ srcs: ["fuzz.cpp"],
+}
diff --git a/trusty/keymaster/fuzz/fuzz.cpp b/trusty/keymaster/fuzz/fuzz.cpp
new file mode 100644
index 0000000..70e19e6
--- /dev/null
+++ b/trusty/keymaster/fuzz/fuzz.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#undef NDEBUG
+
+#include <assert.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <unistd.h>
+#include <iostream>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define KEYMASTER_PORT "com.android.trusty.keymaster"
+#define KEYMASTER_MODULE_FILENAME "keymaster.syms.elf"
+
+/* Keymaster TA's UUID is 5f902ace-5e5c-4cd8-ae54-87b88c22ddaf */
+static struct uuid keymaster_uuid = {
+ 0x5f902ace,
+ 0x5e5c,
+ 0x4cd8,
+ {0xae, 0x54, 0x87, 0xb8, 0x8c, 0x22, 0xdd, 0xaf},
+};
+
+static CoverageRecord record(TIPC_DEV, &keymaster_uuid, KEYMASTER_MODULE_FILENAME);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+ auto ret = record.Open();
+ if (!ret.ok()) {
+ std::cerr << ret.error() << std::endl;
+ exit(-1);
+ }
+ return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static uint8_t buf[TIPC_MAX_MSG_SIZE];
+
+ ExtraCounters counters(&record);
+ counters.Reset();
+
+ android::trusty::fuzz::TrustyApp ta(TIPC_DEV, KEYMASTER_PORT);
+ auto ret = ta.Connect();
+ if (!ret.ok()) {
+ android::trusty::fuzz::Abort();
+ }
+
+ /* Send message to test server */
+ ret = ta.Write(data, size);
+ if (!ret.ok()) {
+ return -1;
+ }
+
+ /* Read message from test server */
+ ret = ta.Read(&buf, sizeof(buf));
+ if (!ret.ok()) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 030b645..bec2a2a 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -59,6 +59,12 @@
GetHmacSharingParametersResponse GetHmacSharingParameters();
ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
+ GetVersion2Response GetVersion2(const GetVersion2Request& request);
+
+ uint32_t message_version() const { return message_version_; }
+
+ private:
+ uint32_t message_version_;
};
} // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index ce2cc2e..419c96f 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -53,6 +53,7 @@
KM_DELETE_ALL_KEYS = (23 << KEYMASTER_REQ_SHIFT),
KM_DESTROY_ATTESTATION_IDS = (24 << KEYMASTER_REQ_SHIFT),
KM_IMPORT_WRAPPED_KEY = (25 << KEYMASTER_REQ_SHIFT),
+ KM_GET_VERSION_2 = (28 << KEYMASTER_REQ_SHIFT),
// Bootloader/provisioning calls.
KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index 6f74833..df6b0f8 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -70,7 +70,7 @@
}
struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
- explicit SetAttestationKeyRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ explicit SetAttestationKeyRequest(int32_t ver = keymaster::kDefaultMessageVersion)
: KeymasterMessage(ver) {}
size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
@@ -88,7 +88,7 @@
};
struct KeymasterNoResponse : public keymaster::KeymasterResponse {
- explicit KeymasterNoResponse(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)
: keymaster::KeymasterResponse(ver) {}
size_t NonErrorSerializedSize() const override { return 0; }
@@ -99,7 +99,7 @@
struct SetAttestationKeyResponse : public KeymasterNoResponse {};
struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
- explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::kDefaultMessageVersion)
: KeymasterMessage(ver) {}
size_t SerializedSize() const override { return sizeof(uint32_t); }
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 708fdbd..e0161a5 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -26,13 +26,8 @@
cc_library {
name: "libtrusty",
- vendor: true,
- defaults: ["libtrusty_defaults"],
-}
-
-// TODO(b/170753563): cc_fuzz can't deal with vendor components. Build libtrusty
-// for system.
-cc_test_library {
- name: "libtrusty_test",
+ // TODO(b/170753563): cc_fuzz can't deal with vendor components. Build
+ // libtrusty for system and vendor.
+ vendor_available: true,
defaults: ["libtrusty_defaults"],
}
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 9676b79..5e60d28 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -19,6 +19,7 @@
srcs: ["tipc_test.c"],
shared_libs: [
"libc",
+ "libdmabufheap",
"liblog",
"libtrusty",
],
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index ca581dc..94aedd7 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -25,6 +25,8 @@
#include <sys/mman.h>
#include <sys/uio.h>
+#include <BufferAllocator/BufferAllocatorWrapper.h>
+
#include <trusty/tipc.h>
#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
@@ -86,7 +88,7 @@
" ta-access - test ta-access flags\n"
" writev - writev test\n"
" readv - readv test\n"
- " send-fd - transmit memfd to trusty, use as shm\n"
+ " send-fd - transmit dma_buf to trusty, use as shm\n"
"\n";
static uint opt_repeat = 1;
@@ -890,9 +892,12 @@
static int send_fd_test(void) {
int ret;
- int memfd = -1;
+ int dma_buf = -1;
int fd = -1;
volatile char* buf = MAP_FAILED;
+ BufferAllocator* allocator = NULL;
+
+ const size_t num_pages = 10;
fd = tipc_connect(dev_name, receiver_name);
if (fd < 0) {
@@ -901,22 +906,24 @@
goto cleanup;
}
- memfd = memfd_create("tipc-send-fd", 0);
- if (memfd < 0) {
- fprintf(stderr, "Failed to create memfd: %s\n", strerror(errno));
+ allocator = CreateDmabufHeapBufferAllocator();
+ if (!allocator) {
+ fprintf(stderr, "Failed to create dma-buf allocator.\n");
ret = -1;
goto cleanup;
}
- if (ftruncate(memfd, PAGE_SIZE) < 0) {
- fprintf(stderr, "Failed to resize memfd: %s\n", strerror(errno));
- ret = -1;
+ size_t buf_size = PAGE_SIZE * num_pages;
+ dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0);
+ if (dma_buf < 0) {
+ ret = dma_buf;
+ fprintf(stderr, "Failed to create dma-buf fd of size %zu err (%d)\n", buf_size, ret);
goto cleanup;
}
- buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+ buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
if (buf == MAP_FAILED) {
- fprintf(stderr, "Failed to map memfd: %s\n", strerror(errno));
+ fprintf(stderr, "Failed to map dma-buf: %s\n", strerror(errno));
ret = -1;
goto cleanup;
}
@@ -924,13 +931,13 @@
strcpy((char*)buf, "From NS");
struct trusty_shm shm = {
- .fd = memfd,
+ .fd = dma_buf,
.transfer = TRUSTY_SHARE,
};
ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
if (rc < 0) {
- fprintf(stderr, "tipc_send failed\n");
+ fprintf(stderr, "tipc_send failed: %zd\n", rc);
ret = rc;
goto cleanup;
}
@@ -938,13 +945,19 @@
read(fd, &c, 1);
tipc_close(fd);
- ret = strcmp("Hello from Trusty!", (const char*)buf) ? (-1) : 0;
+ ret = 0;
+ for (size_t skip = 0; skip < num_pages; skip++) {
+ ret |= strcmp("Hello from Trusty!", (const char*)&buf[skip * PAGE_SIZE]) ? (-1) : 0;
+ }
cleanup:
if (buf != MAP_FAILED) {
munmap((char*)buf, PAGE_SIZE);
}
- close(memfd);
+ close(dma_buf);
+ if (allocator) {
+ FreeDmabufHeapBufferAllocator(allocator);
+ }
tipc_close(fd);
return ret;
}