Merge "[trusty][apploader] Handle APPLOADER_ERR_POLICY_VIOLATION"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
[Builtin Hooks]
clang_format = true
+rustfmt = true
[Builtin Hooks Options]
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+rustfmt = --config-path=rustfmt.toml
[Hook Scripts]
aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index edcea44..ad0231d 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -24,6 +24,10 @@
export_include_dirs: ["common/include"],
recovery_available: true,
vendor_ramdisk_available: true,
+ apex_available: [
+ "com.android.virt",
+ "//apex_available:platform",
+ ],
}
cc_library_shared {
@@ -44,6 +48,10 @@
"libbase",
"libcutils",
],
+ apex_available: [
+ "com.android.virt",
+ "//apex_available:platform",
+ ],
export_header_lib_headers: ["libdebuggerd_common_headers"],
export_include_dirs: ["tombstoned/include"],
@@ -282,7 +290,6 @@
"libdebuggerd/test/elf_fake.cpp",
"libdebuggerd/test/log_fake.cpp",
"libdebuggerd/test/open_files_list_test.cpp",
- "libdebuggerd/test/tombstone_test.cpp",
"libdebuggerd/test/utility_test.cpp",
],
@@ -391,6 +398,9 @@
apex_available: [
"com.android.runtime",
],
+
+ // Required for tests.
+ required: ["crash_dump.policy"],
}
cc_binary {
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index d5327db..efc13df 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "debuggerd_test"
+ },
+ {
+ "name": "libtombstoned_client_rust_test"
}
]
}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 0737612..a5e2413 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1409,6 +1409,16 @@
return true;
}
+extern "C" void foo() {
+ LOG(INFO) << "foo";
+ std::this_thread::sleep_for(1s);
+}
+
+extern "C" void bar() {
+ LOG(INFO) << "bar";
+ std::this_thread::sleep_for(1s);
+}
+
TEST_F(CrasherTest, seccomp_tombstone) {
int intercept_result;
unique_fd output_fd;
@@ -1416,6 +1426,11 @@
static const auto dump_type = kDebuggerdTombstone;
StartProcess(
[]() {
+ std::thread a(foo);
+ std::thread b(bar);
+
+ std::this_thread::sleep_for(100ms);
+
raise_debugger_signal(dump_type);
_exit(0);
},
@@ -1430,16 +1445,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
-}
-
-extern "C" void foo() {
- LOG(INFO) << "foo";
- std::this_thread::sleep_for(1s);
-}
-
-extern "C" void bar() {
- LOG(INFO) << "bar";
- std::this_thread::sleep_for(1s);
+ ASSERT_BACKTRACE_FRAME(result, "foo");
+ ASSERT_BACKTRACE_FRAME(result, "bar");
}
TEST_F(CrasherTest, seccomp_backtrace) {
@@ -1818,9 +1825,9 @@
TEST_F(CrasherTest, unreadable_elf) {
int intercept_result;
unique_fd output_fd;
- StartProcess([]() {
+ std::string tmp_so_name;
+ StartProcess([&tmp_so_name]() {
TemporaryDir td;
- std::string tmp_so_name;
if (!CopySharedLibrary(td.path, &tmp_so_name)) {
_exit(1);
}
@@ -1850,6 +1857,8 @@
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(NOTE: Function names and BuildId information is missing )");
+ std::string match_str = "NOTE: " + tmp_so_name;
+ ASSERT_MATCH(result, match_str);
}
TEST(tombstoned, proto) {
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index baf994f..4c1f9eb 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -98,32 +98,6 @@
__linker_disable_fallback_allocator();
}
-static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
- pid_t current_tid = gettid();
- char buf[BUFSIZ];
- snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
- DIR* dir = opendir(buf);
-
- if (!dir) {
- async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
- return;
- }
-
- struct dirent* ent;
- while ((ent = readdir(dir))) {
- char* end;
- long tid = strtol(ent->d_name, &end, 10);
- if (end == ent->d_name || *end != '\0') {
- continue;
- }
-
- if (tid != current_tid) {
- callback(tid, output_fd);
- }
- }
- closedir(dir);
-}
-
static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
// Make sure the thread actually got the signal.
struct pollfd pfd = {
@@ -216,21 +190,21 @@
}
// Only allow one thread to perform a trace at a time.
- static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_trylock(&trace_mutex);
- if (ret != 0) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
- strerror(ret));
+ static std::mutex trace_mutex;
+ if (!trace_mutex.try_lock()) {
+ async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
return;
}
+ std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
+
// Fetch output fd from tombstoned.
unique_fd tombstone_socket, output_fd;
if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
kDebuggerdNativeBacktrace)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"missing crash_dump_fallback() in selinux policy?");
- goto exit;
+ return;
}
dump_backtrace_header(output_fd.get());
@@ -239,15 +213,15 @@
debuggerd_fallback_trace(output_fd.get(), ucontext);
// Send a signal to all of our siblings, asking them to dump their stack.
- iterate_siblings(
- [](pid_t tid, int output_fd) {
+ pid_t current_tid = gettid();
+ if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
// Use a pipe, to be able to detect situations where the thread gracefully exits before
// receiving our signal.
unique_fd pipe_read, pipe_write;
if (!Pipe(&pipe_read, &pipe_write)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
strerror(errno));
- return false;
+ return;
}
uint64_t expected = pack_thread_fd(-1, -1);
@@ -257,7 +231,7 @@
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"thread %d is already outputting to fd %d?", tid, fd);
close(sent_fd);
- return false;
+ return;
}
siginfo_t siginfo = {};
@@ -269,10 +243,10 @@
if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
tid, strerror(errno));
- return false;
+ return;
}
- bool success = forward_output(pipe_read.get(), output_fd, tid);
+ bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
if (!success) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"timeout expired while waiting for thread %d to dump", tid);
@@ -288,15 +262,14 @@
}
}
- return true;
- },
- output_fd.get());
+ return;
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
+ current_tid, strerror(errno));
+ }
dump_backtrace_footer(output_fd.get());
tombstoned_notify_completion(tombstone_socket.get());
-
-exit:
- pthread_mutex_unlock(&trace_mutex);
}
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index ed2b974..b2077ba 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -161,112 +161,3 @@
set_human_readable_cause(cause, crash_address_);
}
-
-void GwpAsanCrashData::DumpCause(log_t* log) const {
- if (!CrashIsMine()) {
- ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
- return;
- }
-
- if (error_ == gwp_asan::Error::UNKNOWN) {
- _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
- crash_address_);
- return;
- }
-
- if (!responsible_allocation_) {
- _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
- crash_address_);
- return;
- }
-
- uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
- size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
-
- uintptr_t diff;
- const char* location_str;
-
- if (crash_address_ < alloc_address) {
- // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
- location_str = "left of";
- diff = alloc_address - crash_address_;
- } else if (crash_address_ - alloc_address < alloc_size) {
- // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
- location_str = "into";
- diff = crash_address_ - alloc_address;
- } else {
- // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
- // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
- location_str = "right of";
- diff = crash_address_ - alloc_address;
- if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
- diff -= alloc_size;
- }
- }
-
- // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
- const char* byte_suffix = "s";
- if (diff == 1) {
- byte_suffix = "";
- }
- _LOG(log, logtype::HEADER,
- "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
- error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
-}
-
-bool GwpAsanCrashData::HasDeallocationTrace() const {
- assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
- if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
- return false;
- }
- return true;
-}
-
-void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
- assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
- uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
-
- std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
- size_t num_frames =
- __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
-
- if (thread_id == gwp_asan::kInvalidThreadID) {
- _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
- } else {
- _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
- }
-
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
-}
-
-bool GwpAsanCrashData::HasAllocationTrace() const {
- assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
- return responsible_allocation_ != nullptr;
-}
-
-void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
- assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
- uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
-
- std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
- size_t num_frames =
- __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
-
- if (thread_id == gwp_asan::kInvalidThreadID) {
- _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
- } else {
- _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
- }
-
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < num_frames; ++i) {
- unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
-}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index f9c2481..a979370 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -52,26 +52,6 @@
// allocator crash state.
uintptr_t GetFaultAddress() const;
- // Dump the GWP-ASan stringified cause of this crash. May only be called if
- // CrashIsMine() returns true.
- void DumpCause(log_t* log) const;
-
- // Returns whether this crash has a deallocation trace. May only be called if
- // CrashIsMine() returns true.
- bool HasDeallocationTrace() const;
-
- // Dump the GWP-ASan deallocation trace for this crash. May only be called if
- // HasDeallocationTrace() returns true.
- void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
-
- // Returns whether this crash has a allocation trace. May only be called if
- // CrashIsMine() returns true.
- bool HasAllocationTrace() const;
-
- // Dump the GWP-ASan allocation trace for this crash. May only be called if
- // HasAllocationTrace() returns true.
- void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
-
void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
protected:
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index c3b95d6..172ffe9 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,16 +34,12 @@
bool CrashIsMine() const;
- void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
private:
scudo_error_info error_info_ = {};
uintptr_t untagged_fault_addr_;
- void DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const;
-
void FillInCause(Cause* cause, const scudo_error_report* report,
unwindstack::Unwinder* unwinder) const;
};
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a2933f2..a4836d7 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -130,87 +130,3 @@
FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);
}
}
-
-void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
- if (error_info_.reports[1].error_type != UNKNOWN) {
- _LOG(log, logtype::HEADER,
- "\nNote: multiple potential causes for this crash were detected, listing them in "
- "decreasing order of likelihood.\n");
- }
-
- size_t report_num = 0;
- while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
- error_info_.reports[report_num].error_type != UNKNOWN) {
- DumpReport(&error_info_.reports[report_num++], log, unwinder);
- }
-}
-
-void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
- unwindstack::Unwinder* unwinder) const {
- const char *error_type_str;
- switch (report->error_type) {
- case USE_AFTER_FREE:
- error_type_str = "Use After Free";
- break;
- case BUFFER_OVERFLOW:
- error_type_str = "Buffer Overflow";
- break;
- case BUFFER_UNDERFLOW:
- error_type_str = "Buffer Underflow";
- break;
- default:
- error_type_str = "Unknown";
- break;
- }
-
- uintptr_t diff;
- const char* location_str;
-
- if (untagged_fault_addr_ < report->allocation_address) {
- // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
- location_str = "left of";
- diff = report->allocation_address - untagged_fault_addr_;
- } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
- // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
- location_str = "into";
- diff = untagged_fault_addr_ - report->allocation_address;
- } else {
- // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
- location_str = "right of";
- diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
- }
-
- // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
- const char* byte_suffix = "s";
- if (diff == 1) {
- byte_suffix = "";
- }
- _LOG(log, logtype::HEADER,
- "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
- error_type_str, diff, byte_suffix, location_str, report->allocation_size,
- report->allocation_address);
-
- if (report->allocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-
- if (report->deallocation_trace[0]) {
- _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
- ++i) {
- unwindstack::FrameData frame_data =
- unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
- frame_data.num = i;
- _LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
- }
- }
-}
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
deleted file mode 100644
index 1cbfb56..0000000
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2015 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 <stdlib.h>
-#include <sys/mman.h>
-#include <time.h>
-
-#include <memory>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#include "libdebuggerd/utility.h"
-
-#include "UnwinderMock.h"
-#include "host_signal_fixup.h"
-#include "log_fake.h"
-
-#include "gwp_asan.cpp"
-
-using ::testing::MatchesRegex;
-
-class TombstoneTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- unwinder_mock_.reset(new UnwinderMock());
-
- char tmp_file[256];
- const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, data_template, sizeof(data_template));
- int tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
- memcpy(tmp_file, tmp_template, sizeof(tmp_template));
- tombstone_fd = mkstemp(tmp_file);
- if (tombstone_fd == -1) {
- abort();
- }
- }
- if (unlink(tmp_file) == -1) {
- abort();
- }
-
- log_.tfd = tombstone_fd;
- amfd_data_.clear();
- log_.amfd_data = &amfd_data_;
- log_.crashed_tid = 12;
- log_.current_tid = 12;
- log_.should_retrieve_logcat = false;
-
- resetLogs();
- }
-
- virtual void TearDown() {
- if (log_.tfd >= 0) {
- close(log_.tfd);
- }
- }
-
- std::unique_ptr<UnwinderMock> unwinder_mock_;
-
- log_t log_;
- std::string amfd_data_;
-};
-
-class GwpAsanCrashDataTest : public GwpAsanCrashData {
-public:
- GwpAsanCrashDataTest(
- gwp_asan::Error error,
- const gwp_asan::AllocationMetadata *responsible_allocation) :
- GwpAsanCrashData(nullptr, ProcessInfo{}, ThreadInfo{}) {
- is_gwp_asan_responsible_ = true;
- error_ = error;
- responsible_allocation_ = responsible_allocation;
- error_string_ = gwp_asan::ErrorToString(error_);
- }
-
- void SetCrashAddress(uintptr_t crash_address) {
- crash_address_ = crash_address;
- }
-};
-
-TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
- crash_data.SetCrashAddress(0x1000);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free, 0 bytes "
- "into a 32-byte allocation at 0x[a-fA-F0-9]+\n"));
-}
-
-TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
- crash_data.SetCrashAddress(0x1000);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free, 0 bytes into a "
- "32-byte allocation at 0x[a-fA-F0-9]+\n"));
-}
-
-TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
- crash_data.SetCrashAddress(0x1025);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(
- tombstone_contents,
- MatchesRegex(
- "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
- "allocation at 0x[a-fA-F0-9]+\n"));
-}
-
-TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
- crash_data.SetCrashAddress(0xffe);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(
- tombstone_contents,
- MatchesRegex(
- "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
- "allocation at 0x[a-fA-F0-9]+\n"));
-}
-
-TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
- crash_data.SetCrashAddress(0x1001);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(
- tombstone_contents,
- MatchesRegex(
- "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
- "allocation at 0x[a-fA-F0-9]+\n"));
-}
-
-TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
- gwp_asan::AllocationMetadata meta;
- meta.Addr = 0x1000;
- meta.RequestedSize = 32;
-
- GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
- crash_data.SetCrashAddress(0x1021);
-
- crash_data.DumpCause(&log_);
- ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
- std::string tombstone_contents;
- ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
- ASSERT_THAT(
- tombstone_contents,
- MatchesRegex(
- "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
- "allocation at 0x[a-fA-F0-9]+\n"));
-}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index f21a203..14caaf6 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
@@ -73,22 +74,40 @@
std::map<pid_t, ThreadInfo> threads;
threads[tid] = ThreadInfo{
- .registers = std::move(regs),
- .uid = uid,
- .tid = tid,
- .thread_name = std::move(thread_name),
- .pid = pid,
- .command_line = std::move(command_line),
- .selinux_label = std::move(selinux_label),
- .siginfo = siginfo,
+ .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
+ .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
+ .siginfo = siginfo,
+#if defined(__aarch64__)
+ // Only supported on aarch64 for now.
+ .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
+ .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
+#endif
};
+ if (pid == tid) {
+ const ThreadInfo& thread = threads[pid];
+ if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
+ threads[tid] = ThreadInfo{
+ .uid = thread.uid,
+ .tid = tid,
+ .pid = thread.pid,
+ .command_line = thread.command_line,
+ .thread_name = get_thread_name(tid),
+ .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+ .pac_enabled_keys = thread.pac_enabled_keys,
+ };
+ })) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+ strerror(errno));
+ }
+ }
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
auto process_memory =
unwindstack::Memory::CreateProcessMemoryCached(getpid());
unwinder.SetProcessMemory(process_memory);
if (!unwinder.Init()) {
- async_safe_fatal("failed to init unwinder object");
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+ return;
}
ProcessInfo process_info;
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index b7d5bc4..bee4a67 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -35,6 +35,7 @@
#include <memory>
#include <optional>
+#include <set>
#include <string>
#include <async_safe/log.h>
@@ -48,6 +49,7 @@
#include <android/log.h>
#include <bionic/macros.h>
+#include <bionic/reserved_signals.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -346,6 +348,104 @@
f->set_build_id(frame.map_info->GetPrintableBuildID());
}
+static void dump_registers(unwindstack::Unwinder* unwinder,
+ const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
+ bool memory_dump) {
+ if (regs == nullptr) {
+ return;
+ }
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+ regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
+ Register r;
+ r.set_name(name);
+ r.set_u64(value);
+ *thread.add_registers() = r;
+
+ if (memory_dump) {
+ MemoryDump dump;
+
+ dump.set_register_name(name);
+ std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
+ if (map_info) {
+ dump.set_mapping_name(map_info->name());
+ }
+
+ constexpr size_t kNumBytesAroundRegister = 256;
+ constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
+ char buf[kNumBytesAroundRegister];
+ uint8_t tags[kNumTagsAroundRegister];
+ ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+ if (bytes == -1) {
+ return;
+ }
+ dump.set_begin_address(value);
+ dump.set_memory(buf, bytes);
+
+ bool has_tags = false;
+#if defined(__aarch64__)
+ for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
+ if (tags[i] != 0) {
+ has_tags = true;
+ }
+ }
+#endif // defined(__aarch64__)
+
+ if (has_tags) {
+ dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
+ }
+
+ *thread.add_memory_dump() = std::move(dump);
+ }
+ });
+}
+
+static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
+ if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
+ return;
+ }
+
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
+ unwinder->LastErrorCodeString());
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
+ unwinder->LastErrorAddress());
+}
+
+static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
+ if (unwinder->NumFrames() == 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
+ log_unwinder_error(unwinder);
+ return;
+ }
+
+ unwinder->SetDisplayBuildID(true);
+ std::set<std::string> unreadable_elf_files;
+ for (const auto& frame : unwinder->frames()) {
+ BacktraceFrame* f = thread.add_current_backtrace();
+ fill_in_backtrace_frame(f, frame);
+ if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+ unreadable_elf_files.emplace(frame.map_info->name());
+ }
+ }
+
+ if (!unreadable_elf_files.empty()) {
+ auto unreadable_elf_files_proto = thread.mutable_unreadable_elf_files();
+ auto backtrace_note = thread.mutable_backtrace_note();
+ *backtrace_note->Add() =
+ "Function names and BuildId information is missing for some frames due";
+ *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
+ *backtrace_note->Add() = "found under the lib/ directory are readable.";
+ *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+ *backtrace_note->Add() = "Unreadable libraries:";
+ for (auto& name : unreadable_elf_files) {
+ *backtrace_note->Add() = " " + name;
+ *unreadable_elf_files_proto->Add() = name;
+ }
+ }
+}
+
static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
const ThreadInfo& thread_info, bool memory_dump = false) {
Thread thread;
@@ -355,97 +455,32 @@
thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
- unwindstack::Maps* maps = unwinder->GetMaps();
- unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
- thread_info.registers->IterateRegisters(
- [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
- Register r;
- r.set_name(name);
- r.set_u64(value);
- *thread.add_registers() = r;
-
- if (memory_dump) {
- MemoryDump dump;
-
- dump.set_register_name(name);
- std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
- if (map_info) {
- dump.set_mapping_name(map_info->name());
- }
-
- constexpr size_t kNumBytesAroundRegister = 256;
- constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
- char buf[kNumBytesAroundRegister];
- uint8_t tags[kNumTagsAroundRegister];
- size_t start_offset = 0;
- ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
- if (bytes == -1) {
- return;
- }
- dump.set_begin_address(value);
-
- if (start_offset + bytes > sizeof(buf)) {
- async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
- start_offset, bytes);
- }
-
- dump.set_memory(buf, bytes);
-
- bool has_tags = false;
-#if defined(__aarch64__)
- for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
- if (tags[i] != 0) {
- has_tags = true;
- }
- }
-#endif // defined(__aarch64__)
-
- if (has_tags) {
- dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
- }
-
- *thread.add_memory_dump() = std::move(dump);
- }
- });
-
- std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
- unwinder->SetRegs(regs_copy.get());
- unwinder->Unwind();
- if (unwinder->NumFrames() == 0) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
- if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error code: %s",
- unwinder->LastErrorCodeString());
- async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, " error address: 0x%" PRIx64,
- unwinder->LastErrorAddress());
+ if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
+ // Fallback path for non-main thread, doing unwind from running process.
+ unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
+ if (!thread_unwinder.Init()) {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unable to initialize ThreadUnwinder object.");
+ log_unwinder_error(&thread_unwinder);
+ return;
}
+
+ std::unique_ptr<unwindstack::Regs> initial_regs;
+ thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
+ dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
+ dump_thread_backtrace(&thread_unwinder, thread);
} else {
- if (unwinder->elf_from_memory_not_file()) {
- auto backtrace_note = thread.mutable_backtrace_note();
- *backtrace_note->Add() =
- "Function names and BuildId information is missing for some frames due";
- *backtrace_note->Add() =
- "to unreadable libraries. For unwinds of apps, only shared libraries";
- *backtrace_note->Add() = "found under the lib/ directory are readable.";
- *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame);
- }
+ dump_registers(unwinder, thread_info.registers, thread, memory_dump);
+ std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+ unwinder->SetRegs(regs_copy.get());
+ unwinder->Unwind();
+ dump_thread_backtrace(unwinder, thread);
}
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
}
-static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
- const ThreadInfo& thread_info) {
- dump_thread(tombstone, unwinder, thread_info, true);
-}
-
static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
unwindstack::Maps* maps = unwinder->GetMaps();
std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
@@ -663,7 +698,8 @@
dump_abort_message(&result, unwinder, process_info);
- dump_main_thread(&result, unwinder, main_thread);
+ // Dump the main thread, but save the memory around the registers.
+ dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
for (const auto& [tid, thread_info] : threads) {
if (tid != target_thread) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 543a67c..ecd98a4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -28,6 +28,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <set>
#include <string>
#include <android-base/properties.h>
@@ -483,7 +484,16 @@
}
void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
- if (unwinder->elf_from_memory_not_file()) {
+ std::set<std::string> unreadable_elf_files;
+ unwinder->SetDisplayBuildID(true);
+ for (const auto& frame : unwinder->frames()) {
+ if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+ unreadable_elf_files.emplace(frame.map_info->name());
+ }
+ }
+
+ // Put the preamble ahead of the backtrace.
+ if (!unreadable_elf_files.empty()) {
_LOG(log, logtype::BACKTRACE,
"%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
_LOG(log, logtype::BACKTRACE,
@@ -493,10 +503,13 @@
_LOG(log, logtype::BACKTRACE,
"%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
#endif
+ _LOG(log, logtype::BACKTRACE, "%sNOTE: Unreadable libraries:\n", prefix);
+ for (auto& name : unreadable_elf_files) {
+ _LOG(log, logtype::BACKTRACE, "%sNOTE: %s\n", prefix, name.c_str());
+ }
}
- unwinder->SetDisplayBuildID(true);
- for (size_t i = 0; i < unwinder->NumFrames(); i++) {
- _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
+ for (const auto& frame : unwinder->frames()) {
+ _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
}
}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 40a942e..a0f2f82 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -123,12 +123,13 @@
string name = 2;
repeated Register registers = 3;
repeated string backtrace_note = 7;
+ repeated string unreadable_elf_files = 9;
repeated BacktraceFrame current_backtrace = 4;
repeated MemoryDump memory_dump = 5;
int64 tagged_addr_ctrl = 6;
int64 pac_enabled_keys = 8;
- reserved 9 to 999;
+ reserved 10 to 999;
}
message BacktraceFrame {
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
new file mode 100644
index 0000000..2007f39
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/Android.bp
@@ -0,0 +1,59 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_static {
+ name: "libtombstoned_client_wrapper",
+ srcs: [
+ "wrapper.cpp",
+ ],
+ generated_sources: [
+ "libtombstoned_client_rust_bridge_code"
+ ],
+ header_libs: [
+ "libbase_headers",
+ "libdebuggerd_common_headers",
+ ],
+ shared_libs: [
+ "libtombstoned_client",
+ ],
+ apex_available: ["com.android.virt"],
+}
+
+rust_defaults {
+ name: "libtombstoned_client_rust_defaults",
+ crate_name: "tombstoned_client",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libcxx",
+ "libthiserror",
+ ],
+ static_libs: [
+ "libtombstoned_client_wrapper",
+ ],
+ shared_libs: [
+ "libtombstoned_client",
+ ],
+}
+
+rust_library {
+ name: "libtombstoned_client_rust",
+ defaults: ["libtombstoned_client_rust_defaults"],
+ apex_available: ["com.android.virt"],
+}
+
+rust_test {
+ name: "libtombstoned_client_rust_test",
+ defaults: ["libtombstoned_client_rust_defaults"],
+ require_root: true,
+ test_suites: ["device-tests"],
+}
+
+genrule {
+ name: "libtombstoned_client_rust_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["libtombstoned_client_cxx_generated.cc"],
+}
diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs
new file mode 100644
index 0000000..5c8abef
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/src/lib.rs
@@ -0,0 +1,153 @@
+// Copyright 2022, 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.
+
+//! Rust wrapper for tombstoned client.
+
+pub use ffi::DebuggerdDumpType;
+use std::fs::File;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use thiserror::Error;
+
+/// Error communicating with tombstoned.
+#[derive(Clone, Debug, Error, Eq, PartialEq)]
+#[error("Error communicating with tombstoned")]
+pub struct Error;
+
+/// File descriptors for communicating with tombstoned.
+pub struct TombstonedConnection {
+ /// The socket connection to tombstoned.
+ ///
+ /// This is actually a Unix SOCK_SEQPACKET socket not a file, but the Rust standard library
+ /// doesn't have an appropriate type and it's not really worth bringing in a dependency on `uds`
+ /// or something when all we do is pass it back to C++ or close it.
+ tombstoned_socket: File,
+ /// The file descriptor for text output.
+ pub text_output: Option<File>,
+ /// The file descriptor for proto output.
+ pub proto_output: Option<File>,
+}
+
+impl TombstonedConnection {
+ unsafe fn from_raw_fds(
+ tombstoned_socket: RawFd,
+ text_output_fd: RawFd,
+ proto_output_fd: RawFd,
+ ) -> Self {
+ Self {
+ tombstoned_socket: File::from_raw_fd(tombstoned_socket),
+ text_output: if text_output_fd >= 0 {
+ Some(File::from_raw_fd(text_output_fd))
+ } else {
+ None
+ },
+ proto_output: if proto_output_fd >= 0 {
+ Some(File::from_raw_fd(proto_output_fd))
+ } else {
+ None
+ },
+ }
+ }
+
+ /// Connects to tombstoned.
+ pub fn connect(pid: i32, dump_type: DebuggerdDumpType) -> Result<Self, Error> {
+ let mut tombstoned_socket = -1;
+ let mut text_output_fd = -1;
+ let mut proto_output_fd = -1;
+ if ffi::tombstoned_connect_files(
+ pid,
+ &mut tombstoned_socket,
+ &mut text_output_fd,
+ &mut proto_output_fd,
+ dump_type,
+ ) {
+ Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
+ } else {
+ Err(Error)
+ }
+ }
+
+ /// Notifies tombstoned that the dump is complete.
+ pub fn notify_completion(&self) -> Result<(), Error> {
+ if ffi::tombstoned_notify_completion(self.tombstoned_socket.as_raw_fd()) {
+ Ok(())
+ } else {
+ Err(Error)
+ }
+ }
+}
+
+#[cxx::bridge]
+mod ffi {
+ /// The type of dump.
+ enum DebuggerdDumpType {
+ /// A native backtrace.
+ #[cxx_name = "kDebuggerdNativeBacktrace"]
+ NativeBacktrace,
+ /// A tombstone.
+ #[cxx_name = "kDebuggerdTombstone"]
+ Tombstone,
+ /// A Java backtrace.
+ #[cxx_name = "kDebuggerdJavaBacktrace"]
+ JavaBacktrace,
+ /// Any intercept.
+ #[cxx_name = "kDebuggerdAnyIntercept"]
+ AnyIntercept,
+ /// A tombstone proto.
+ #[cxx_name = "kDebuggerdTombstoneProto"]
+ TombstoneProto,
+ }
+
+ unsafe extern "C++" {
+ include!("wrapper.hpp");
+
+ type DebuggerdDumpType;
+
+ fn tombstoned_connect_files(
+ pid: i32,
+ tombstoned_socket: &mut i32,
+ text_output_fd: &mut i32,
+ proto_output_fd: &mut i32,
+ dump_type: DebuggerdDumpType,
+ ) -> bool;
+
+ fn tombstoned_notify_completion(tombstoned_socket: i32) -> bool;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::{io::Write, process};
+
+ // Verify that we can connect to tombstoned, write something to the file descriptor it returns,
+ // and notify completion, without any errors.
+ #[test]
+ fn test() {
+ let connection =
+ TombstonedConnection::connect(process::id() as i32, DebuggerdDumpType::Tombstone)
+ .expect("Failed to connect to tombstoned.");
+
+ assert!(connection.proto_output.is_none());
+ connection
+ .text_output
+ .as_ref()
+ .expect("No text output FD returned.")
+ .write_all(b"test data")
+ .expect("Failed to write to text output FD.");
+
+ connection
+ .notify_completion()
+ .expect("Failed to notify completion.");
+ }
+}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.cpp b/debuggerd/rust/tombstoned_client/wrapper.cpp
new file mode 100644
index 0000000..7492329
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/wrapper.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022, 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 "wrapper.hpp"
+
+#include <android-base/unique_fd.h>
+
+#include "tombstoned/tombstoned.h"
+
+using android::base::unique_fd;
+
+bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
+ int& proto_output_fd, DebuggerdDumpType dump_type) {
+ unique_fd tombstoned_socket_unique, text_output_unique, proto_output_unique;
+
+ bool result = tombstoned_connect(pid, &tombstoned_socket_unique, &text_output_unique,
+ &proto_output_unique, dump_type);
+ if (result) {
+ tombstoned_socket = tombstoned_socket_unique.release();
+ text_output_fd = text_output_unique.release();
+ proto_output_fd = proto_output_unique.release();
+ }
+
+ return result;
+}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.hpp b/debuggerd/rust/tombstoned_client/wrapper.hpp
new file mode 100644
index 0000000..95d3865
--- /dev/null
+++ b/debuggerd/rust/tombstoned_client/wrapper.hpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2022, 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 "tombstoned/tombstoned.h"
+
+bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
+ int& proto_output_fd, DebuggerdDumpType dump_type);
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 858a338..8241f0e 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,7 +25,7 @@
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 152697c..0cb8e08 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -34,10 +34,18 @@
rt_sigaction: 1
rt_tgsigqueueinfo: 1
+// this is referenced from mainline modules running on Q devices, where not all
+// of the constants used here are defined in headers, so minijail rejects them.
+// we define them here to avoid those errors.
+ // constants introduced in R
#define PR_SET_VMA 0x53564d41
+#define PR_GET_TAGGED_ADDR_CTRL 56
+ // constants introduced in S
+#define PR_PAC_GET_ENABLED_KEYS 61
+
#if defined(__aarch64__)
// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
#else
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
#endif
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index ce0fd30..5c6abc9 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,6 +18,7 @@
#include <time.h>
+#include <functional>
#include <string>
#include <utility>
@@ -74,3 +75,24 @@
n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
return buf;
}
+
+bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
+ char buf[BUFSIZ];
+ snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
+ std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
+ if (dir == nullptr) {
+ return false;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ pid_t tid = atoi(entry->d_name);
+ if (tid == 0) {
+ continue;
+ }
+ if (pid != tid) {
+ callback(tid);
+ }
+ }
+ return true;
+}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index ec2862a..4375870 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <string>
#include <vector>
@@ -27,3 +28,4 @@
std::string get_thread_name(pid_t tid);
std::string get_timestamp();
+bool iterate_tids(pid_t, std::function<void(pid_t)>);
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 44dc81f..06ffe0f 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -76,7 +76,7 @@
} // namespace
-int FlashRawDataChunk(int fd, const char* data, size_t len) {
+int FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {
size_t ret = 0;
const size_t max_write_size = 1048576;
void* aligned_buffer;
@@ -91,7 +91,15 @@
while (ret < len) {
int this_len = std::min(max_write_size, len - ret);
memcpy(aligned_buffer_unique_ptr.get(), data, this_len);
- int this_ret = write(fd, aligned_buffer_unique_ptr.get(), this_len);
+ // In case of non 4KB aligned writes, reopen without O_DIRECT flag
+ if (this_len & 0xFFF) {
+ if (handle->Reset(O_WRONLY) != true) {
+ PLOG(ERROR) << "Failed to reset file descriptor";
+ return -1;
+ }
+ }
+
+ int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);
if (this_ret < 0) {
PLOG(ERROR) << "Failed to flash data of len " << len;
return -1;
@@ -102,8 +110,8 @@
return 0;
}
-int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
- int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+int FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {
+ int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());
if (ret < 0) {
return -errno;
}
@@ -111,30 +119,30 @@
}
int WriteCallback(void* priv, const void* data, size_t len) {
- int fd = reinterpret_cast<long long>(priv);
+ PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
if (!data) {
- return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+ return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
}
- return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+ return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
}
-int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+int FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {
struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),
downloaded_data.size(), true, false);
if (!file) {
// Invalid sparse format
return -EINVAL;
}
- return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+ return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
}
-int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
- lseek64(fd, 0, SEEK_SET);
+int FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {
+ lseek64(handle->fd(), 0, SEEK_SET);
if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
*reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
- return FlashSparseData(fd, downloaded_data);
+ return FlashSparseData(handle, downloaded_data);
} else {
- return FlashRawData(fd, downloaded_data);
+ return FlashRawData(handle, downloaded_data);
}
}
@@ -181,11 +189,16 @@
if (android::base::GetProperty("ro.system.build.type", "") != "user") {
WipeOverlayfsForPartition(device, partition_name);
}
- int result = FlashBlockDevice(handle.fd(), data);
+ int result = FlashBlockDevice(&handle, data);
sync();
return result;
}
+static void RemoveScratchPartition() {
+ AutoMountMetadata mount_metadata;
+ android::fs_mgr::TeardownAllOverlayForMountPoint();
+}
+
bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
std::vector<char> data = std::move(device->download_data());
if (data.empty()) {
@@ -218,7 +231,7 @@
if (!FlashPartitionTable(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to flash new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully flashed partition table");
}
@@ -262,7 +275,7 @@
if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
- android::fs_mgr::TeardownAllOverlayForMountPoint();
+ RemoveScratchPartition();
sync();
return device->WriteOkay("Successfully updated partition table");
}
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 97b5ad4..3302c43 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -90,14 +90,7 @@
return false;
}
- flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
- unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
- if (fd < 0) {
- PLOG(ERROR) << "Failed to open block device: " << handle->path();
- return false;
- }
- handle->set_fd(std::move(fd));
- return true;
+ return handle->Open(flags);
}
std::optional<std::string> FindPhysicalPartition(const std::string& name) {
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 1d81b7a..6e1453f 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -18,6 +18,8 @@
#include <optional>
#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <fstab/fstab.h>
@@ -44,11 +46,51 @@
}
const std::string& path() const { return path_; }
int fd() const { return fd_.get(); }
- void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+ bool Open(int flags) {
+ flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
+ // Attempts to open a second device can fail with EBUSY if the device is already open.
+ // Explicitly close any previously opened devices as unique_fd won't close them until
+ // after the attempt to open.
+ fd_.reset();
+
+ fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "Failed to open block device: " << path_;
+ return false;
+ }
+ flags_ = flags;
+
+ return true;
+ }
+ bool Reset(int flags) {
+ if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {
+ return true;
+ }
+
+ off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;
+ if (offset < 0) {
+ PLOG(ERROR) << "Failed lseek on block device: " << path_;
+ return false;
+ }
+
+ sync();
+
+ if (Open(flags) == false) {
+ return false;
+ }
+
+ if (lseek(fd_.get(), offset, SEEK_SET) != offset) {
+ PLOG(ERROR) << "Failed lseek on block device: " << path_;
+ return false;
+ }
+
+ return true;
+ }
private:
std::string path_;
android::base::unique_fd fd_;
+ int flags_;
std::function<void()> closer_;
};
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 76e9889..0cf4699 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -332,8 +332,8 @@
auto fastboot_hal = device->fastboot_hal();
if (!fastboot_hal) {
- *message = "Fastboot HAL not found";
- return false;
+ *message = "raw";
+ return true;
}
FileSystemType type;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index fde1dab..39d86f9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1084,7 +1084,11 @@
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if (g_disable_verity || g_disable_verification) {
- if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
+ // The vbmeta partition might have additional prefix if running in virtual machine
+ // e.g., guest_vbmeta_a.
+ if (android::base::EndsWith(partition, "vbmeta") ||
+ android::base::EndsWith(partition, "vbmeta_a") ||
+ android::base::EndsWith(partition, "vbmeta_b")) {
rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
} else if (!has_vbmeta_partition() &&
(partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index fcd3bd6..1b59e4a 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -15,6 +15,11 @@
*
*/
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "fastboot_fuzzer",
host_supported: true,
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8ce961b..6863894 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -364,8 +364,8 @@
const struct ext4_super_block* sb, int* fs_stat) {
bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
bool want_quota = entry.fs_mgr_flags.quota;
- bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
-
+ // Enable projid support by default
+ bool want_projid = true;
if (has_quota == want_quota) {
return;
}
@@ -2208,8 +2208,10 @@
// Devices upgrading to dynamic partitions are allowed to specify a super
// partition name. This includes cuttlefish, which is a non-A/B device.
std::string super_partition;
- if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
- fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ if (fs_mgr_get_boot_config("force_super_partition", &super_partition)) {
+ return super_partition;
+ }
+ if (fs_mgr_get_boot_config("super_partition", &super_partition)) {
if (fs_mgr_get_slot_suffix().empty()) {
return super_partition;
}
@@ -2256,7 +2258,8 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0
// Allowlist the mount point if user build.
static const std::vector<const std::string> kAllowedPaths = {
- "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+ "/odm", "/odm_dlkm", "/oem", "/product",
+ "/system_dlkm", "/system_ext", "/vendor", "/vendor_dlkm",
};
static const std::vector<const std::string> kAllowedPrefixes = {
"/mnt/product/",
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index bb49873..6f59ed3 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -156,11 +156,10 @@
LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
bool needs_casefold = false;
- bool needs_projid = false;
+ bool needs_projid = true;
if (entry.mount_point == "/data") {
needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
}
if (entry.fs_type == "f2fs") {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 0ca1946..b8b9262 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -287,11 +287,16 @@
entry->fs_mgr_flags.avb = true;
entry->vbmeta_partition = arg;
} else if (StartsWith(flag, "keydirectory=")) {
- // The metadata flag is followed by an = and the directory for the keys.
+ // The keydirectory flag enables metadata encryption. It is
+ // followed by an = and the directory containing the metadata
+ // encryption key.
entry->metadata_key_dir = arg;
} else if (StartsWith(flag, "metadata_encryption=")) {
- // Specify the cipher and flags to use for metadata encryption
- entry->metadata_encryption = arg;
+ // The metadata_encryption flag specifies the cipher and flags to
+ // use for metadata encryption, if the defaults aren't sufficient.
+ // It doesn't actually enable metadata encryption; that is done by
+ // "keydirectory".
+ entry->metadata_encryption_options = arg;
} else if (StartsWith(flag, "sysfs_path=")) {
// The path to trigger device gc by idle-maint of vold.
entry->sysfs_path = arg;
@@ -657,7 +662,12 @@
if (partition_ext4 == fstab->end()) {
auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
new_entry.fs_type = "ext4";
- fstab->emplace_back(new_entry);
+ auto it = std::find_if(fstab->rbegin(), fstab->rend(),
+ [&mount_point](const auto& entry) {
+ return entry.mount_point == mount_point;
+ });
+ auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
+ fstab->insert(end_of_mount_point_group, new_entry);
}
}
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2da5b0f..82b5275 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,6 +33,7 @@
#include <algorithm>
#include <memory>
+#include <optional>
#include <string>
#include <vector>
@@ -125,8 +126,12 @@
bool fs_mgr_in_recovery() {
// Check the existence of recovery binary instead of using the compile time
- // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
- // defined, albeit not in recovery. More details: system/core/init/README.md
+ // __ANDROID_RECOVERY__ macro.
+ // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+ // mode would use the same init binary, which would mean during normal boot
+ // the '/init' binary is actually a symlink pointing to
+ // init_second_stage.recovery, which would be compiled with
+ // __ANDROID_RECOVERY__ defined.
return fs_mgr_access("/system/bin/recovery");
}
@@ -1121,6 +1126,23 @@
return true;
}
+static inline uint64_t GetIdealDataScratchSize() {
+ BlockDeviceInfo super_info;
+ PartitionOpener opener;
+ if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+ LERROR << "could not get block device info for super";
+ return 0;
+ }
+
+ struct statvfs s;
+ if (statvfs("/data", &s) < 0) {
+ PERROR << "could not statfs /data";
+ return 0;
+ }
+
+ return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+}
+
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
if (change) *change = false;
@@ -1136,13 +1158,6 @@
return true;
}
- BlockDeviceInfo info;
- PartitionOpener opener;
- if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
- LERROR << "could not get block device info for super";
- return false;
- }
-
if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
@@ -1152,10 +1167,11 @@
return false;
}
if (!images->BackingImageExists(partition_name)) {
- static constexpr uint64_t kMinimumSize = 64_MiB;
- static constexpr uint64_t kMaximumSize = 2_GiB;
+ uint64_t size = GetIdealDataScratchSize();
+ if (!size) {
+ size = 2_GiB;
+ }
- uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
if (!images->CreateBackingImage(partition_name, size, flags)) {
@@ -1396,18 +1412,35 @@
return ret;
}
+struct MapInfo {
+ // If set, partition is owned by ImageManager.
+ std::unique_ptr<IImageManager> images;
+ // If set, and images is null, this is a DAP partition.
+ std::string name;
+ // If set, and images and name are empty, this is a non-dynamic partition.
+ std::string device;
+
+ MapInfo() = default;
+ MapInfo(MapInfo&&) = default;
+ ~MapInfo() {
+ if (images) {
+ images->UnmapImageDevice(name);
+ } else if (!name.empty()) {
+ DestroyLogicalPartition(name);
+ }
+ }
+};
+
// Note: This function never returns the DSU scratch device in recovery or fastbootd,
// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static bool EnsureScratchMapped(std::string* device, bool* mapped) {
- *mapped = false;
- *device = GetBootScratchDevice();
- if (!device->empty()) {
- return true;
+static std::optional<MapInfo> EnsureScratchMapped() {
+ MapInfo info;
+ info.device = GetBootScratchDevice();
+ if (!info.device.empty()) {
+ return {std::move(info)};
}
-
if (!fs_mgr_in_recovery()) {
- errno = EINVAL;
- return false;
+ return {};
}
auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1417,11 +1450,15 @@
// would otherwise always be mapped.
auto images = IImageManager::Open("remount", 10s);
if (images && images->BackingImageExists(partition_name)) {
- if (!images->MapImageDevice(partition_name, 10s, device)) {
- return false;
+ if (images->IsImageDisabled(partition_name)) {
+ return {};
}
- *mapped = true;
- return true;
+ if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+ return {};
+ }
+ info.name = partition_name;
+ info.images = std::move(images);
+ return {std::move(info)};
}
// Avoid uart spam by first checking for a scratch partition.
@@ -1429,12 +1466,12 @@
auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
auto metadata = ReadCurrentMetadata(super_device);
if (!metadata) {
- return false;
+ return {};
}
auto partition = FindPartition(*metadata.get(), partition_name);
if (!partition) {
- return false;
+ return {};
}
CreateLogicalPartitionParams params = {
@@ -1444,11 +1481,11 @@
.force_writable = true,
.timeout_ms = 10s,
};
- if (!CreateLogicalPartition(params, device)) {
- return false;
+ if (!CreateLogicalPartition(params, &info.device)) {
+ return {};
}
- *mapped = true;
- return true;
+ info.name = partition_name;
+ return {std::move(info)};
}
// This should only be reachable in recovery, where DSU scratch is not
@@ -1602,26 +1639,35 @@
fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
}
- // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
- bool mapped = false;
- std::string scratch_device;
- if (EnsureScratchMapped(&scratch_device, &mapped)) {
+ if (mount_point.empty()) {
+ // Throw away the entire partition.
+ auto partition_name = android::base::Basename(kScratchMountPoint);
+ auto images = IImageManager::Open("remount", 10s);
+ if (images && images->BackingImageExists(partition_name)) {
+ if (images->DisableImage(partition_name)) {
+ LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+ } else {
+ LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+ }
+ }
+ }
+
+ if (auto info = EnsureScratchMapped(); info.has_value()) {
+ // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
fs_mgr_overlayfs_umount_scratch();
- if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+ if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
bool should_destroy_scratch = false;
fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
&should_destroy_scratch);
+ fs_mgr_overlayfs_umount_scratch();
if (should_destroy_scratch) {
fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
}
- fs_mgr_overlayfs_umount_scratch();
- }
- if (mapped) {
- DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
}
}
// Teardown DSU overlay if present.
+ std::string scratch_device;
if (MapDsuScratchDevice(&scratch_device)) {
fs_mgr_overlayfs_umount_scratch();
if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
index f0afd28..b2b9be8 100644
--- a/fs_mgr/fuzz/Android.bp
+++ b/fs_mgr/fuzz/Android.bp
@@ -14,6 +14,11 @@
// limitations under the License.
//
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
cc_fuzz {
name: "libfstab_fuzzer",
srcs: [
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b831d12..f26fb24 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,7 @@
std::string fs_options;
std::string fs_checkpoint_opts;
std::string metadata_key_dir;
- std::string metadata_encryption;
+ std::string metadata_encryption_options;
off64_t length = 0;
std::string label;
int partnum = -1;
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a8..003e6ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,6 +66,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages() override;
@@ -219,6 +220,17 @@
return !device->empty();
}
+bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
+ bool retval;
+ auto status = manager_->isImageDisabled(name, &retval);
+ if (!status.isOk()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__
+ << " binder returned: " << status.exceptionMessage().string();
+ return false;
+ }
+ return retval;
+}
+
bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
return false;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index e3f5716..c416f4d 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -79,7 +79,7 @@
partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
// Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RECOVERY__
+#ifdef __ANDROID_RAMDISK__
device_info_.is_recovery = {true};
#else
if (!device_info_.is_recovery.has_value()) {
@@ -523,7 +523,7 @@
auto image_header = GetImageHeaderPath(name);
-#if !defined __ANDROID_RECOVERY__
+#ifndef __ANDROID_RAMDISK__
// If there is a device-mapper node wrapping the block device, then we're
// able to create another node around it; the dm layer does not carry the
// exclusion lock down the stack when a mount occurs.
@@ -854,6 +854,24 @@
return true;
}
+bool ImageManager::IsImageDisabled(const std::string& name) {
+ if (!MetadataExists(metadata_dir_)) {
+ return true;
+ }
+
+ auto metadata = OpenMetadata(metadata_dir_);
+ if (!metadata) {
+ return false;
+ }
+
+ auto partition = FindPartition(*metadata.get(), name);
+ if (!partition) {
+ return false;
+ }
+
+ return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
+}
+
std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
const std::chrono::milliseconds& timeout_ms,
const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d09751..7472949 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,6 +119,7 @@
ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
ASSERT_TRUE(manager_->BackingImageExists(base_name_));
ASSERT_TRUE(manager_->DisableImage(base_name_));
+ ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
ASSERT_TRUE(manager_->RemoveDisabledImages());
ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
}
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index b23a7f7..00dd661 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,6 +131,9 @@
virtual bool RemoveAllImages() = 0;
virtual bool UnmapImageIfExists(const std::string& name);
+
+ // Returns whether DisableImage() was called.
+ virtual bool IsImageDisabled(const std::string& name) = 0;
};
class ImageManager final : public IImageManager {
@@ -162,6 +165,7 @@
bool RemoveDisabledImages() override;
bool GetMappedImageDevice(const std::string& name, std::string* device) override;
bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+ bool IsImageDisabled(const std::string& name) override;
std::vector<std::string> GetAllBackingImages();
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 04bcbda..875ccb0 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "liblp_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "liblp_test"
+ }
]
}
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 8b269cd..6db8f13 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -275,6 +275,24 @@
],
}
+cc_test {
+ name: "vts_ota_config_test",
+ srcs: [
+ "vts_ota_config_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ test_suites: [
+ "vts",
+ ],
+ test_options: {
+ min_shipping_api_level: 33,
+ },
+ auto_gen_config: true,
+ require_root: true,
+}
+
cc_binary {
name: "snapshotctl",
srcs: [
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 37319fe..9d2b877 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -2,3 +2,4 @@
balsini@google.com
dvander@google.com
elsk@google.com
+akailash@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 532f66d..5daa84d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -197,6 +197,9 @@
// user-space snapshots
bool userspace_snapshots = 9;
+
+ // io_uring support
+ bool io_uring_enabled = 10;
}
// Next: 10
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 9b5fd2a..746feeb 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -550,6 +550,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::vector<CowOperation>::iterator op_iter_;
@@ -560,6 +563,15 @@
op_iter_ = ops_->begin();
}
+bool CowOpIter::RDone() {
+ return op_iter_ == ops_->begin();
+}
+
+void CowOpIter::Prev() {
+ CHECK(!RDone());
+ op_iter_--;
+}
+
bool CowOpIter::Done() {
return op_iter_ == ops_->end();
}
@@ -585,6 +597,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -603,6 +618,9 @@
const CowOperation& Get() override;
void Next() override;
+ void Prev() override;
+ bool RDone() override;
+
private:
std::shared_ptr<std::vector<CowOperation>> ops_;
std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
@@ -623,6 +641,15 @@
block_iter_ = merge_op_blocks->begin() + start;
}
+bool CowMergeOpIter::RDone() {
+ return block_iter_ == merge_op_blocks_->begin();
+}
+
+void CowMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_iter_--;
+}
+
bool CowMergeOpIter::Done() {
return block_iter_ == merge_op_blocks_->end();
}
@@ -649,6 +676,15 @@
block_riter_ = merge_op_blocks->rbegin();
}
+bool CowRevMergeOpIter::RDone() {
+ return block_riter_ == merge_op_blocks_->rbegin();
+}
+
+void CowRevMergeOpIter::Prev() {
+ CHECK(!RDone());
+ block_riter_--;
+}
+
bool CowRevMergeOpIter::Done() {
return block_riter_ == merge_op_blocks_->rend() - start_;
}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index d5b4335..8e6bbd9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -92,7 +92,7 @@
public:
virtual ~ICowOpIter() {}
- // True if there are more items to read, false otherwise.
+ // True if there are no more items to read forward, false otherwise.
virtual bool Done() = 0;
// Read the current operation.
@@ -100,6 +100,12 @@
// Advance to the next item.
virtual void Next() = 0;
+
+ // Advance to the previous item.
+ virtual void Prev() = 0;
+
+ // True if there are no more items to read backwards, false otherwise
+ virtual bool RDone() = 0;
};
class CowReader final : public ICowReader {
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 120f95b..38b47d5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -809,6 +809,9 @@
// userspace snapshots.
bool UpdateUsesUserSnapshots(LockedFile* lock);
+ // Check if io_uring API's need to be used
+ bool UpdateUsesIouring(LockedFile* lock);
+
// Wrapper around libdm, with diagnostics.
bool DeleteDeviceIfExists(const std::string& name,
const std::chrono::milliseconds& timeout_ms = {});
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 548ba00..167ff8c 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -155,6 +155,7 @@
}
StringSink sink;
bool success = true;
+ uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
while (!iter->Done()) {
const CowOperation& op = iter->Get();
@@ -187,9 +188,26 @@
}
}
+ if (op.type == kCowCopyOp) {
+ copy_ops++;
+ } else if (op.type == kCowReplaceOp) {
+ replace_ops++;
+ } else if (op.type == kCowZeroOp) {
+ zero_ops++;
+ } else if (op.type == kCowXorOp) {
+ xor_ops++;
+ }
+
iter->Next();
}
+ if (!opt.silent) {
+ auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
+ std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
+ << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
+ << " Xor_ops: " << xor_ops << std::endl;
+ }
+
return success;
}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index f3de2b4..d086f29 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1685,6 +1685,9 @@
if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {
snapuserd_argv->emplace_back("-user_snapshot");
+ if (UpdateUsesIouring(lock.get())) {
+ snapuserd_argv->emplace_back("-io_uring");
+ }
}
size_t num_cows = 0;
@@ -2062,6 +2065,11 @@
return update_status.compression_enabled();
}
+bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
+ SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+ return update_status.io_uring_enabled();
+}
+
bool SnapshotManager::UpdateUsesUserSnapshots() {
// This and the following function is constantly
// invoked during snapshot merge. We want to avoid
@@ -2877,6 +2885,7 @@
status.set_source_build_fingerprint(old_status.source_build_fingerprint());
status.set_merge_phase(old_status.merge_phase());
status.set_userspace_snapshots(old_status.userspace_snapshots());
+ status.set_io_uring_enabled(old_status.io_uring_enabled());
}
return WriteSnapshotUpdateStatus(lock, status);
}
@@ -3196,14 +3205,27 @@
status.set_compression_enabled(cow_creator.compression_enabled);
if (cow_creator.compression_enabled) {
if (!device()->IsTestDevice()) {
+ bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release = android::base::GetProperty(
+ "ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ userSnapshotsEnabled = false;
+ }
+
// Userspace snapshots is enabled only if compression is enabled
- status.set_userspace_snapshots(IsUserspaceSnapshotsEnabled());
- if (IsUserspaceSnapshotsEnabled()) {
+ status.set_userspace_snapshots(userSnapshotsEnabled);
+ if (userSnapshotsEnabled) {
is_snapshot_userspace_ = true;
- LOG(INFO) << "User-space snapshots enabled";
+ status.set_io_uring_enabled(IsIouringEnabled());
+ LOG(INFO) << "Userspace snapshots enabled";
} else {
is_snapshot_userspace_ = false;
- LOG(INFO) << "User-space snapshots disabled";
+ LOG(INFO) << "Userspace snapshots disabled";
}
// Terminate stale daemon if any
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index c1a5af7..a648384 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -199,6 +199,7 @@
bool UnmapImageIfExists(const std::string& name) override {
return impl_->UnmapImageIfExists(name);
}
+ bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
private:
std::unique_ptr<android::fiemap::IImageManager> impl_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index d76558b..36abf71 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -46,8 +46,6 @@
#include "partition_cow_creator.h"
#include "utility.h"
-#include <android-base/properties.h>
-
// Mock classes are not used. Header included to ensure mocked class definition aligns with the
// class itself.
#include <libsnapshot/mock_device_info.h>
@@ -2413,8 +2411,15 @@
// fit in super, but not |prd|.
constexpr uint64_t partition_size = 3788_KiB;
SetSize(sys_, partition_size);
+ SetSize(vnd_, partition_size);
+ SetSize(prd_, 18_MiB);
- AddOperationForPartitions({sys_});
+ // Make sure |prd| does not fit in super at all. On VABC, this means we
+ // fake an extra large COW for |vnd| to fill up super.
+ vnd_->set_estimate_cow_size(30_MiB);
+ prd_->set_estimate_cow_size(30_MiB);
+
+ AddOperationForPartitions();
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -2424,8 +2429,25 @@
GTEST_SKIP() << "Test does not apply to userspace snapshots";
}
- ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+ // Test that partitions prioritize using space in super.
+ auto tgt = MetadataBuilder::New(*opener_, "super", 1);
+ ASSERT_NE(tgt, nullptr);
+ ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
+ ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+ ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
+
+ // Write some data to target partitions.
+ for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+ ASSERT_TRUE(WriteSnapshotAndHash(name));
+ }
+
+ // Assert that source partitions aren't affected.
+ for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+ ASSERT_TRUE(IsPartitionUnchanged(name));
+ }
+
ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
ASSERT_TRUE(UnmapAll());
class DmStatusFailure final : public DeviceMapperWrapper {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
index b86a802..484a9c4 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
@@ -932,7 +932,6 @@
ASSERT_EQ(area_sz, 2);
- size_t new_chunk = 263;
// Verify the partially filled area
void* buffer = snapuserd_->GetExceptionBuffer(1);
loff_t offset = 0;
@@ -941,7 +940,6 @@
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
ASSERT_EQ(de->old_chunk, i);
offset += sizeof(struct disk_exception);
- new_chunk += 1;
}
de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index a082742..2f7775c 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -28,11 +28,23 @@
DEFINE_bool(socket_handoff, false,
"If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
+DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
namespace android {
namespace snapshot {
bool Daemon::IsUserspaceSnapshotsEnabled() {
+ const std::string UNKNOWN = "unknown";
+ const std::string vendor_release =
+ android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+ // No user-space snapshots if vendor partition is on Android 12
+ if (vendor_release.find("12") != std::string::npos) {
+ LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+ << vendor_release;
+ return false;
+ }
+
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
@@ -51,7 +63,12 @@
// is applied will check for the property. This is ok as the system
// properties are valid at this point. We can't do this during first
// stage init and hence use the command line flags to get the information.
- if (!IsDmSnapshotTestingEnabled() && (FLAGS_user_snapshot || IsUserspaceSnapshotsEnabled())) {
+ bool user_snapshots = FLAGS_user_snapshot;
+ if (!user_snapshots) {
+ user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
+ }
+
+ if (user_snapshots) {
LOG(INFO) << "Starting daemon for user-space snapshots.....";
return StartServerForUserspaceSnapshots(arg_start, argc, argv);
} else {
@@ -75,6 +92,11 @@
MaskAllSignalsExceptIntAndTerm();
+ user_server_.SetServerRunning();
+ if (FLAGS_io_uring) {
+ user_server_.SetIouringEnabled();
+ }
+
if (FLAGS_socket_handoff) {
return user_server_.RunForSocketHandoff();
}
@@ -165,7 +187,10 @@
}
void Daemon::Interrupt() {
- if (IsUserspaceSnapshotsEnabled()) {
+ // TODO: We cannot access system property during first stage init.
+ // Until we remove the dm-snapshot code, we will have this check
+ // and verify it through a temp variable.
+ if (user_server_.IsServerRunning()) {
user_server_.Interrupt();
} else {
server_.Interrupt();
@@ -173,7 +198,7 @@
}
void Daemon::ReceivedSocketSignal() {
- if (IsUserspaceSnapshotsEnabled()) {
+ if (user_server_.IsServerRunning()) {
user_server_.ReceivedSocketSignal();
} else {
server_.ReceivedSocketSignal();
@@ -209,8 +234,6 @@
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
- LOG(INFO) << "snapuserd daemon about to start";
-
android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
if (!daemon.StartDaemon(argc, argv)) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 5109d82..692cb74 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -39,7 +39,21 @@
}
bool SnapshotHandler::InitializeWorkers() {
- for (int i = 0; i < kNumWorkerThreads; i++) {
+ int num_worker_threads = kNumWorkerThreads;
+
+ // We will need multiple worker threads only during
+ // device boot after OTA. For all other purposes,
+ // one thread is sufficient. We don't want to consume
+ // unnecessary memory especially during OTA install phase
+ // when daemon will be up during entire post install phase.
+ //
+ // During boot up, we need multiple threads primarily for
+ // update-verification.
+ if (is_socket_present_) {
+ num_worker_threads = 1;
+ }
+
+ for (int i = 0; i < num_worker_threads; i++) {
std::unique_ptr<Worker> wt =
std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
misc_name_, base_path_merge_, GetSharedPtr());
@@ -478,22 +492,17 @@
return;
}
- if (IsIouringSupported()) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksAsync, this, dm_block_device,
- partition_name, dev_sz);
- } else {
- int num_threads = 2;
- size_t num_blocks = dev_sz >> BLOCK_SHIFT;
- size_t num_blocks_per_thread = num_blocks / num_threads;
- size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
- off_t offset = 0;
+ int num_threads = 2;
+ size_t num_blocks = dev_sz >> BLOCK_SHIFT;
+ size_t num_blocks_per_thread = num_blocks / num_threads;
+ size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
+ off_t offset = 0;
- for (int i = 0; i < num_threads; i++) {
- std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this,
- dm_block_device, partition_name, offset, read_sz_per_thread);
+ for (int i = 0; i < num_threads; i++) {
+ std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
+ partition_name, offset, read_sz_per_thread);
- offset += read_sz_per_thread;
- }
+ offset += read_sz_per_thread;
}
}
@@ -677,6 +686,14 @@
return false;
}
+ // During selinux init transition, libsnapshot will propagate the
+ // status of io_uring enablement. As properties are not initialized,
+ // we cannot query system property.
+ if (is_io_uring_enabled_) {
+ return true;
+ }
+
+ // Finally check the system property
return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index b0f2d65..83d40f6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -99,6 +99,7 @@
void InitializeRAIter();
bool RAIterDone();
void RAIterNext();
+ void RAResetIter(uint64_t num_blocks);
const CowOperation* GetRAOpIter();
void InitializeBuffer();
@@ -151,12 +152,16 @@
std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
BufferSink bufsink_;
+ uint64_t total_ra_blocks_completed_ = 0;
bool read_ahead_async_ = false;
- // Queue depth of 32 seems optimal. We don't want
+ // Queue depth of 8 seems optimal. We don't want
// to have a huge depth as it may put more memory pressure
// on the kernel worker threads given that we use
- // IOSQE_ASYNC flag.
- int queue_depth_ = 32;
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
std::unique_ptr<struct io_uring> ring_;
};
@@ -210,11 +215,12 @@
// Merge related ops
bool Merge();
- bool MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
- bool MergeOrderedOpsAsync(const std::unique_ptr<ICowOpIter>& cowop_iter);
- bool MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter);
+ bool AsyncMerge();
+ bool SyncMerge();
+ bool MergeOrderedOps();
+ bool MergeOrderedOpsAsync();
+ bool MergeReplaceZeroOps();
int PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec = nullptr);
sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
@@ -238,12 +244,18 @@
unique_fd base_path_merge_fd_;
unique_fd ctrl_fd_;
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ size_t ra_block_index_ = 0;
+ uint64_t blocks_merged_in_group_ = 0;
bool merge_async_ = false;
- // Queue depth of 32 seems optimal. We don't want
+ // Queue depth of 8 seems optimal. We don't want
// to have a huge depth as it may put more memory pressure
// on the kernel worker threads given that we use
- // IOSQE_ASYNC flag.
- int queue_depth_ = 32;
+ // IOSQE_ASYNC flag - ASYNC flags can potentially
+ // result in EINTR; Since we don't restart
+ // syscalls and fallback to synchronous I/O, we
+ // don't want huge queue depth
+ int queue_depth_ = 8;
std::unique_ptr<struct io_uring> ring_;
std::shared_ptr<SnapshotHandler> snapuserd_;
@@ -319,6 +331,7 @@
void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
+ void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
bool MergeInitiated() { return merge_initiated_; }
double GetMergePercentage() { return merge_completion_percentage_; }
@@ -396,6 +409,7 @@
bool merge_initiated_ = false;
bool attached_ = false;
bool is_socket_present_;
+ bool is_io_uring_enabled_ = false;
bool scratch_space_ = false;
std::unique_ptr<struct io_uring> ring_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index d4d4efe..c26a2cd 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -24,15 +24,14 @@
using android::base::unique_fd;
int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
- const std::unique_ptr<ICowOpIter>& cowop_iter,
std::vector<const CowOperation*>* replace_zero_vec) {
int num_ops = *pending_ops;
int nr_consecutive = 0;
bool checkOrderedOp = (replace_zero_vec == nullptr);
do {
- if (!cowop_iter->Done() && num_ops) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ if (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
break;
}
@@ -42,12 +41,12 @@
replace_zero_vec->push_back(cow_op);
}
- cowop_iter->Next();
+ cowop_iter_->Next();
num_ops -= 1;
nr_consecutive = 1;
- while (!cowop_iter->Done() && num_ops) {
- const CowOperation* op = &cowop_iter->Get();
+ while (!cowop_iter_->Done() && num_ops) {
+ const CowOperation* op = &cowop_iter_->Get();
if (checkOrderedOp && !IsOrderedOp(*op)) {
break;
}
@@ -63,7 +62,7 @@
nr_consecutive += 1;
num_ops -= 1;
- cowop_iter->Next();
+ cowop_iter_->Next();
}
}
} while (0);
@@ -71,7 +70,7 @@
return nr_consecutive;
}
-bool Worker::MergeReplaceZeroOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeReplaceZeroOps() {
// Flush every 8192 ops. Since all ops are independent and there is no
// dependency between COW ops, we will flush the data and the number
// of ops merged in COW file for every 8192 ops. If there is a crash,
@@ -84,15 +83,17 @@
int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
int num_ops_merged = 0;
- while (!cowop_iter->Done()) {
+ SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
+
+ while (!cowop_iter_->Done()) {
int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
std::vector<const CowOperation*> replace_zero_vec;
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter, &replace_zero_vec);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
if (linear_blocks == 0) {
// Merge complete
- CHECK(cowop_iter->Done());
+ CHECK(cowop_iter_->Done());
break;
}
@@ -117,8 +118,8 @@
size_t io_size = linear_blocks * BLOCK_SZ;
// Merge - Write the contents back to base device
- int ret = pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(), io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(),
+ io_size, source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR)
<< "Merge: ReplaceZeroOps: Failed to write to backing device while merging "
@@ -172,16 +173,15 @@
return true;
}
-bool Worker::MergeOrderedOpsAsync(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeOrderedOpsAsync() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- size_t block_index = 0;
SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -190,11 +190,10 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
- snapuserd_->SetMergeFailed(block_index);
return false;
}
- snapuserd_->SetMergeInProgress(block_index);
+ snapuserd_->SetMergeInProgress(ra_block_index_);
loff_t offset = 0;
int num_ops = snapuserd_->GetTotalBlocksToMerge();
@@ -202,12 +201,13 @@
int pending_sqe = queue_depth_;
int pending_ios_to_submit = 0;
bool flush_required = false;
+ blocks_merged_in_group_ = 0;
SNAP_LOG(DEBUG) << "Merging copy-ops of size: " << num_ops;
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
if (linear_blocks != 0) {
size_t io_size = (linear_blocks * BLOCK_SZ);
@@ -216,7 +216,6 @@
struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
if (!sqe) {
SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
@@ -225,10 +224,18 @@
offset += io_size;
num_ops -= linear_blocks;
+ blocks_merged_in_group_ += linear_blocks;
pending_sqe -= 1;
pending_ios_to_submit += 1;
- sqe->flags |= IOSQE_ASYNC;
+ // These flags are important - We need to make sure that the
+ // blocks are linked and are written in the same order as
+ // populated. This is because of overlapping block writes.
+ //
+ // If there are no dependency, we can optimize this further by
+ // allowing parallel writes; but for now, just link all the SQ
+ // entries.
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
}
// Ring is full or no more COW ops to be merged in this batch
@@ -256,7 +263,7 @@
pending_sqe -= 1;
flush_required = false;
pending_ios_to_submit += 1;
- sqe->flags |= IOSQE_ASYNC;
+ sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
}
} else {
flush_required = true;
@@ -269,35 +276,44 @@
SNAP_PLOG(ERROR)
<< "io_uring_submit failed for read-ahead: "
<< " io submit: " << ret << " expected: " << pending_ios_to_submit;
- snapuserd_->SetMergeFailed(block_index);
return false;
}
int pending_ios_to_complete = pending_ios_to_submit;
pending_ios_to_submit = 0;
+ bool status = true;
+
// Reap I/O completions
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
- SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
- snapuserd_->SetMergeFailed(block_index);
- return false;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+ status = false;
+ break;
}
if (cqe->res < 0) {
- SNAP_LOG(ERROR)
- << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
- snapuserd_->SetMergeFailed(block_index);
- return false;
+ SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed with res: " << cqe->res;
+ status = false;
+ break;
}
io_uring_cqe_seen(ring_.get(), cqe);
pending_ios_to_complete -= 1;
}
+ if (!status) {
+ return false;
+ }
+
pending_sqe = queue_depth_;
}
@@ -312,7 +328,6 @@
// Flush the data
if (flush_required && (fsync(base_path_merge_fd_.get()) < 0)) {
SNAP_LOG(ERROR) << " Failed to fsync merged data";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
@@ -320,35 +335,34 @@
// the merge completion
if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
- snapuserd_->SetMergeFailed(block_index);
return false;
}
SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
+
// Mark the block as merge complete
- snapuserd_->SetMergeCompleted(block_index);
+ snapuserd_->SetMergeCompleted(ra_block_index_);
// Notify RA thread that the merge thread is ready to merge the next
// window
snapuserd_->NotifyRAForMergeReady();
// Get the next block
- block_index += 1;
+ ra_block_index_ += 1;
}
return true;
}
-bool Worker::MergeOrderedOps(const std::unique_ptr<ICowOpIter>& cowop_iter) {
+bool Worker::MergeOrderedOps() {
void* mapped_addr = snapuserd_->GetMappedAddr();
void* read_ahead_buffer =
static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
- size_t block_index = 0;
SNAP_LOG(INFO) << "MergeOrderedOps started....";
- while (!cowop_iter->Done()) {
- const CowOperation* cow_op = &cowop_iter->Get();
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
if (!IsOrderedOp(*cow_op)) {
break;
}
@@ -357,11 +371,11 @@
// Wait for RA thread to notify that the merge window
// is ready for merging.
if (!snapuserd_->WaitForMergeBegin()) {
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
- snapuserd_->SetMergeInProgress(block_index);
+ snapuserd_->SetMergeInProgress(ra_block_index_);
loff_t offset = 0;
int num_ops = snapuserd_->GetTotalBlocksToMerge();
@@ -369,7 +383,7 @@
while (num_ops) {
uint64_t source_offset;
- int linear_blocks = PrepareMerge(&source_offset, &num_ops, cowop_iter);
+ int linear_blocks = PrepareMerge(&source_offset, &num_ops);
if (linear_blocks == 0) {
break;
}
@@ -378,12 +392,13 @@
// Write to the base device. Data is already in the RA buffer. Note
// that XOR ops is already handled by the RA thread. We just write
// the contents out.
- int ret = pwrite(base_path_merge_fd_.get(), (char*)read_ahead_buffer + offset, io_size,
- source_offset);
+ int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(),
+ (char*)read_ahead_buffer + offset, io_size,
+ source_offset));
if (ret < 0 || ret != io_size) {
SNAP_LOG(ERROR) << "Failed to write to backing device while merging "
<< " at offset: " << source_offset << " io_size: " << io_size;
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -397,7 +412,7 @@
// Flush the data
if (fsync(base_path_merge_fd_.get()) < 0) {
SNAP_LOG(ERROR) << " Failed to fsync merged data";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
@@ -405,47 +420,87 @@
// the merge completion
if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
- snapuserd_->SetMergeFailed(block_index);
+ snapuserd_->SetMergeFailed(ra_block_index_);
return false;
}
SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
// Mark the block as merge complete
- snapuserd_->SetMergeCompleted(block_index);
+ snapuserd_->SetMergeCompleted(ra_block_index_);
// Notify RA thread that the merge thread is ready to merge the next
// window
snapuserd_->NotifyRAForMergeReady();
// Get the next block
- block_index += 1;
+ ra_block_index_ += 1;
}
return true;
}
-bool Worker::Merge() {
- std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
+bool Worker::AsyncMerge() {
+ if (!MergeOrderedOpsAsync()) {
+ SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
+ // Reset the iter so that we retry the merge
+ while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ blocks_merged_in_group_ -= 1;
+ }
+ return false;
+ }
+
+ SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed";
+ return true;
+}
+
+bool Worker::SyncMerge() {
+ if (!MergeOrderedOps()) {
+ SNAP_LOG(ERROR) << "Merge failed for ordered ops";
+ return false;
+ }
+
+ SNAP_LOG(INFO) << "MergeOrderedOps completed";
+ return true;
+}
+
+bool Worker::Merge() {
+ cowop_iter_ = reader_->GetMergeOpIter();
+
+ bool retry = false;
+ bool ordered_ops_merge_status;
+
+ // Start Async Merge
if (merge_async_) {
- if (!MergeOrderedOpsAsync(cowop_iter)) {
+ ordered_ops_merge_status = AsyncMerge();
+ if (!ordered_ops_merge_status) {
+ FinalizeIouring();
+ retry = true;
+ merge_async_ = false;
+ }
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async merge, we
+ // will directly enter here (aka devices with 4.x kernels)
+ const bool sync_merge_required = (retry || !merge_async_);
+
+ if (sync_merge_required) {
+ ordered_ops_merge_status = SyncMerge();
+ if (!ordered_ops_merge_status) {
+ // Merge failed. Device will continue to be mounted
+ // off snapshots; merge will be retried during
+ // next reboot
SNAP_LOG(ERROR) << "Merge failed for ordered ops";
snapuserd_->MergeFailed();
return false;
}
- SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed.....";
- } else {
- // Start with Copy and Xor ops
- if (!MergeOrderedOps(cowop_iter)) {
- SNAP_LOG(ERROR) << "Merge failed for ordered ops";
- snapuserd_->MergeFailed();
- return false;
- }
- SNAP_LOG(INFO) << "MergeOrderedOps completed.....";
}
// Replace and Zero ops
- if (!MergeReplaceZeroOps(cowop_iter)) {
+ if (!MergeReplaceZeroOps()) {
SNAP_LOG(ERROR) << "Merge failed for replace/zero ops";
snapuserd_->MergeFailed();
return false;
@@ -506,7 +561,7 @@
CloseFds();
reader_->CloseCowFd();
- SNAP_LOG(INFO) << "Merge finish";
+ SNAP_LOG(INFO) << "Snapshot-Merge completed";
return true;
}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 26c5f19..fa2866f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -279,7 +279,6 @@
sqe = io_uring_get_sqe(ring_.get());
if (!sqe) {
SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during read-ahead";
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -309,7 +308,6 @@
if (ret != pending_ios_to_submit) {
SNAP_PLOG(ERROR) << "io_uring_submit failed for read-ahead: "
<< " io submit: " << ret << " expected: " << pending_ios_to_submit;
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -321,14 +319,12 @@
// Read XOR data from COW file in parallel when I/O's are in-flight
if (xor_processing_required && !ReadXorData(block_index, xor_op_index, xor_op_vec)) {
SNAP_LOG(ERROR) << "ReadXorData failed";
- snapuserd_->ReadAheadIOFailed();
return false;
}
// Fetch I/O completions
if (!ReapIoCompletions(pending_ios_to_complete)) {
SNAP_LOG(ERROR) << "ReapIoCompletions failed";
- snapuserd_->ReadAheadIOFailed();
return false;
}
@@ -393,26 +389,35 @@
}
bool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) {
+ bool status = true;
+
// Reap I/O completions
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
+ // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
+ // these error codes are not truly I/O errors; we can retry them
+ // by re-populating the SQE entries and submitting the I/O
+ // request back. However, we don't do that now; instead we
+ // will fallback to synchronous I/O.
int ret = io_uring_wait_cqe(ring_.get(), &cqe);
if (ret) {
SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
- return false;
+ status = false;
+ break;
}
if (cqe->res < 0) {
SNAP_LOG(ERROR) << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
- return false;
+ status = false;
+ break;
}
io_uring_cqe_seen(ring_.get(), cqe);
pending_ios_to_complete -= 1;
}
- return true;
+ return status;
}
void ReadAhead::ProcessXorData(size_t& block_xor_index, size_t& xor_index,
@@ -610,18 +615,38 @@
return ReconstructDataFromCow();
}
+ bool retry = false;
+ bool ra_status;
+
+ // Start Async read-ahead
if (read_ahead_async_) {
- if (!ReadAheadAsyncIO()) {
- SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - io_uring processing failure.";
- return false;
+ ra_status = ReadAheadAsyncIO();
+ if (!ra_status) {
+ SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - Falling back synchronous I/O";
+ FinalizeIouring();
+ RAResetIter(total_blocks_merged_);
+ retry = true;
+ read_ahead_async_ = false;
}
- } else {
- if (!ReadAheadSyncIO()) {
+ }
+
+ // Check if we need to fallback and retry the merge
+ //
+ // If the device doesn't support async operations, we
+ // will directly enter here (aka devices with 4.x kernels)
+
+ const bool ra_sync_required = (retry || !read_ahead_async_);
+
+ if (ra_sync_required) {
+ ra_status = ReadAheadSyncIO();
+ if (!ra_status) {
SNAP_LOG(ERROR) << "ReadAheadSyncIO failed";
return false;
}
}
+ SNAP_LOG(DEBUG) << "Read-ahead: total_ra_blocks_merged: " << total_ra_blocks_completed_;
+
// Wait for the merge to finish for the previous RA window. We shouldn't
// be touching the scratch space until merge is complete of previous RA
// window. If there is a crash during this time frame, merge should resume
@@ -646,6 +671,7 @@
offset += BLOCK_SZ;
}
+ total_ra_blocks_completed_ += total_blocks_merged_;
snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
// Flush the data only if we have a overlapping blocks in the region
@@ -763,6 +789,13 @@
cowop_iter_->Next();
}
+void ReadAhead::RAResetIter(uint64_t num_blocks) {
+ while (num_blocks && !cowop_iter_->RDone()) {
+ cowop_iter_->Prev();
+ num_blocks -= 1;
+ }
+}
+
const CowOperation* ReadAhead::GetRAOpIter() {
const CowOperation* cow_op = &cowop_iter_->Get();
return cow_op;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index eb64704..82b2b25 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -293,7 +293,6 @@
void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
- handler->snapuserd()->SetSocketPresent(is_socket_present_);
if (!handler->snapuserd()->Start()) {
LOG(ERROR) << " Failed to launch all worker threads";
}
@@ -471,6 +470,9 @@
return nullptr;
}
+ snapuserd->SetSocketPresent(is_socket_present_);
+ snapuserd->SetIouringEnabled(io_uring_enabled_);
+
if (!snapuserd->InitializeWorkers()) {
LOG(ERROR) << "Failed to initialize workers";
return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index c645456..34e7941 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -84,6 +84,8 @@
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
int num_partitions_merge_complete_ = 0;
+ bool is_server_running_ = false;
+ bool io_uring_enabled_ = false;
std::mutex lock_;
@@ -136,6 +138,10 @@
void SetTerminating() { terminating_ = true; }
void ReceivedSocketSignal() { received_socket_signal_ = true; }
+ void SetServerRunning() { is_server_running_ = true; }
+ bool IsServerRunning() { return is_server_running_; }
+ void SetIouringEnabled() { io_uring_enabled_ = true; }
+ bool IsIouringEnabled() { return io_uring_enabled_; }
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 6dec1e2..d4e1d7c 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -531,6 +531,13 @@
{
std::unique_lock<std::mutex> lock(blk_state->m_lock);
+ // We may have fallback from Async-merge to synchronous merging
+ // on the existing block. There is no need to reset as the
+ // merge is already in progress.
+ if (blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS) {
+ return;
+ }
+
CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_PENDING);
// First set the state to RA_READY so that in-flight I/O will drain
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index e3e3af8..71fe124 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -267,8 +267,8 @@
return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
<< kUserDataDevice;
}
- uint64_t next_consume =
- std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+ uint64_t next_consume = std::min(available_space_ - max_free_space,
+ (uint64_t)std::numeric_limits<off_t>::max());
off_t allocated = 0;
while (next_consume > 0 && free_space_ > max_free_space) {
int status = fallocate(big_file_->fd, 0, allocated, next_consume);
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 89d6145..f01bec9 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -192,6 +192,10 @@
return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
}
+bool IsIouringEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
std::string GetOtherPartitionName(const std::string& name) {
auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index a032b68..0ef3234 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -135,6 +135,8 @@
bool IsDmSnapshotTestingEnabled();
+bool IsIouringEnabled();
+
// Swap the suffix of a partition name.
std::string GetOtherPartitionName(const std::string& name);
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
new file mode 100644
index 0000000..afc2d81
--- /dev/null
+++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2022 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 <android-base/properties.h>
+#include <gtest/gtest.h>
+
+TEST(VAB, Enabled) {
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
+ ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 335da9e..d82d566 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -88,3 +88,32 @@
test_suites: ["general-tests"],
}
+
+cc_test {
+ name: "vts_fs_test",
+ test_suites: [
+ "vts",
+ "device-tests",
+ ],
+ test_options: {
+ min_shipping_api_level: 29,
+ },
+ require_root: true,
+ auto_gen_config: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ srcs: [
+ "vts_fs_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libfs_mgr",
+ "libfstab",
+ "libgmock",
+ "libgtest",
+ ],
+}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index eccb902..6c881c0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -943,7 +943,7 @@
ASSERT_LE(1U, fstab.size());
auto entry = fstab.begin();
- EXPECT_EQ("adiantum", entry->metadata_encryption);
+ EXPECT_EQ("adiantum", entry->metadata_encryption_options);
}
TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
@@ -960,8 +960,8 @@
ASSERT_LE(1U, fstab.size());
auto entry = fstab.begin();
- EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
- auto parts = android::base::Split(entry->metadata_encryption, ":");
+ EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption_options);
+ auto parts = android::base::Split(entry->metadata_encryption_options, ":");
EXPECT_EQ(2U, parts.size());
EXPECT_EQ("aes-256-xts", parts[0]);
EXPECT_EQ("wrappedkey_v0", parts[1]);
@@ -1104,3 +1104,76 @@
EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
EXPECT_EQ(0, entry->readahead_size_kb);
}
+
+TEST(fs_mgr, TransformFstabForDsu) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+system /system ext4 ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ std::string fstab_contents = R"fs(
+system /system erofs ro wait,logical,first_stage_mount
+vendor /vendor ext4 ro wait,logical,first_stage_mount
+data /data f2fs noatime wait
+)fs";
+
+ ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+ Fstab fstab;
+ EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+ TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+ ASSERT_EQ(4U, fstab.size());
+
+ auto entry = fstab.begin();
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("erofs", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/system", entry->mount_point);
+ EXPECT_EQ("system_gsi", entry->blk_device);
+ EXPECT_EQ("ext4", entry->fs_type);
+ entry++;
+
+ EXPECT_EQ("/vendor", entry->mount_point);
+ EXPECT_EQ("vendor", entry->blk_device);
+ entry++;
+
+ EXPECT_EQ("/data", entry->mount_point);
+ EXPECT_EQ("userdata_gsi", entry->blk_device);
+ entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
new file mode 100644
index 0000000..b5fac53
--- /dev/null
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -0,0 +1,111 @@
+// 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 <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+static int GetVsrLevel() {
+ return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
+TEST(fs, ErofsSupported) {
+ // S and higher for this test.
+ if (GetVsrLevel() < __ANDROID_API_S__) {
+ GTEST_SKIP();
+ }
+
+ struct utsname uts;
+ ASSERT_EQ(uname(&uts), 0);
+
+ unsigned int major, minor;
+ ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+
+ // EROFS support only required in 5.10+
+ if (major < 5 || (major == 5 && minor < 10)) {
+ GTEST_SKIP();
+ }
+
+ std::string fs;
+ ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
+ EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
+}
+
+TEST(fs, PartitionTypes) {
+ android::fs_mgr::Fstab fstab;
+ ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+ auto& dm = android::dm::DeviceMapper::Instance();
+
+ std::string super_bdev, userdata_bdev;
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
+ ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
+
+ int vsr_level = GetVsrLevel();
+
+ for (const auto& entry : fstab) {
+ std::string parent_bdev = entry.blk_device;
+ while (true) {
+ auto basename = android::base::Basename(parent_bdev);
+ if (!android::base::StartsWith(basename, "dm-")) {
+ break;
+ }
+
+ auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);
+ if (!parent || *parent == parent_bdev) {
+ break;
+ }
+ parent_bdev = *parent;
+ }
+
+ if (parent_bdev == userdata_bdev ||
+ android::base::StartsWith(parent_bdev, "/dev/block/loop")) {
+ if (entry.flags & MS_RDONLY) {
+ // APEXes should not be F2FS.
+ EXPECT_NE(entry.fs_type, "f2fs");
+ }
+ continue;
+ }
+
+ if (vsr_level < __ANDROID_API_T__) {
+ continue;
+ }
+ if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
+ // Only check for dynamic partitions at this VSR level.
+ continue;
+ }
+
+ if (entry.flags & MS_RDONLY) {
+ EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+ } else {
+ EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
+ }
+ }
+}
+
+TEST(fs, NoDtFstab) {
+ if (GetVsrLevel() < __ANDROID_API_Q__) {
+ GTEST_SKIP();
+ }
+
+ android::fs_mgr::Fstab fstab;
+ EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 24777c8..f180006 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -22,9 +22,15 @@
"libutils",
"libbase",
- // Need latest HealthInfo definition from headers of this shared
- // library. Clients don't need to link to this.
+ // Need HealthInfo definition from headers of these shared
+ // libraries. Clients don't need to link to these.
"android.hardware.health@2.1",
+ "android.hardware.health-V1-ndk",
+ ],
+ whole_static_libs: [
+ // Need to translate HIDL to AIDL to support legacy APIs in
+ // BatteryMonitor.
+ "android.hardware.health-translate-ndk",
],
header_libs: ["libhealthd_headers"],
export_header_lib_headers: ["libhealthd_headers"],
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 5890f9a..a7571a2 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -31,10 +31,12 @@
#include <memory>
#include <optional>
+#include <aidl/android/hardware/health/HealthInfo.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/hardware/health/2.1/types.h>
+#include <android/hardware/health/translate-ndk.h>
#include <batteryservice/BatteryService.h>
#include <cutils/klog.h>
#include <cutils/properties.h>
@@ -52,10 +54,54 @@
using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-using android::hardware::health::V1_0::BatteryHealth;
-using android::hardware::health::V1_0::BatteryStatus;
-using android::hardware::health::V2_1::BatteryCapacityLevel;
-using android::hardware::health::V2_1::Constants;
+using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryStatus;
+using aidl::android::hardware::health::HealthInfo;
+
+namespace {
+
+// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
+// Skips storageInfo and diskStats.
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V1_0::HealthInfo* out) {
+ out->chargerAcOnline = in.chargerAcOnline;
+ out->chargerUsbOnline = in.chargerUsbOnline;
+ out->chargerWirelessOnline = in.chargerWirelessOnline;
+ out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
+ out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
+ out->batteryStatus =
+ static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
+ out->batteryHealth =
+ static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
+ out->batteryPresent = in.batteryPresent;
+ out->batteryLevel = in.batteryLevel;
+ out->batteryVoltage = in.batteryVoltageMillivolts;
+ out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
+ out->batteryCurrent = in.batteryCurrentMicroamps;
+ out->batteryCycleCount = in.batteryCycleCount;
+ out->batteryFullCharge = in.batteryFullChargeUah;
+ out->batteryChargeCounter = in.batteryChargeCounterUah;
+ out->batteryTechnology = in.batteryTechnology;
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_0::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
+ // Skip storageInfo and diskStats
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+ ::android::hardware::health::V2_1::HealthInfo* out) {
+ translateToHidl(in, &out->legacy);
+ out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
+ in.batteryCapacityLevel);
+ out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+ out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+}
+
+} // namespace
namespace android {
@@ -74,17 +120,14 @@
return std::nullopt;
}
-static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
- *health_info_2_1 = HealthInfo_2_1{};
-
- // HIDL enum values are zero initialized, so they need to be initialized
- // properly.
- health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED;
- health_info_2_1->batteryChargeTimeToFullNowSeconds =
- (int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
- auto* props = &health_info_2_1->legacy.legacy;
- props->batteryStatus = BatteryStatus::UNKNOWN;
- props->batteryHealth = BatteryHealth::UNKNOWN;
+static void initHealthInfo(HealthInfo* health_info) {
+ *health_info = {
+ .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
+ .batteryChargeTimeToFullNowSeconds =
+ (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
+ .batteryStatus = BatteryStatus::UNKNOWN,
+ .batteryHealth = BatteryHealth::UNKNOWN,
+ };
}
BatteryMonitor::BatteryMonitor()
@@ -92,22 +135,31 @@
mBatteryDevicePresent(false),
mBatteryFixedCapacity(0),
mBatteryFixedTemperature(0),
- mChargerDockOnline(false),
- mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
+ mHealthInfo(std::make_unique<HealthInfo>()) {
initHealthInfo(mHealthInfo.get());
}
BatteryMonitor::~BatteryMonitor() {}
-const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
- return getHealthInfo_2_0().legacy;
+HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
+ HealthInfo_1_0 health_info_1_0;
+ translateToHidl(*mHealthInfo, &health_info_1_0);
+ return health_info_1_0;
}
-const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
- return getHealthInfo_2_1().legacy;
+HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
+ HealthInfo_2_0 health_info_2_0;
+ translateToHidl(*mHealthInfo, &health_info_2_0);
+ return health_info_2_0;
}
-const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
+HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
+ HealthInfo_2_1 health_info_2_1;
+ translateToHidl(*mHealthInfo, &health_info_2_1);
+ return health_info_2_1;
+}
+
+const HealthInfo& BatteryMonitor::getHealthInfo() const {
return *mHealthInfo;
}
@@ -175,46 +227,48 @@
return *ret;
}
-int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
+static int readFromFile(const String8& path, std::string* buf) {
+ buf->clear();
if (android::base::ReadFileToString(path.c_str(), buf)) {
*buf = android::base::Trim(*buf);
}
return buf->length();
}
-BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
+static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
static SysfsStringEnumMap<int> supplyTypeMap[] = {
- {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
- {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
- {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
- {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
- {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
- {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
- {"Dock", ANDROID_POWER_SUPPLY_TYPE_DOCK},
+ {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+ {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+ {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+ {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+ {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+ {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
{NULL, 0},
};
std::string buf;
- if (readFromFile(path, &buf) <= 0)
- return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ if (readFromFile(path, &buf) <= 0) {
+ return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ }
auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (!ret) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
- *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+ *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
}
-bool BatteryMonitor::getBooleanField(const String8& path) {
+static bool getBooleanField(const String8& path) {
std::string buf;
bool value = false;
@@ -225,7 +279,7 @@
return value;
}
-int BatteryMonitor::getIntField(const String8& path) {
+static int getIntField(const String8& path) {
std::string buf;
int value = 0;
@@ -235,7 +289,7 @@
return value;
}
-bool BatteryMonitor::isScopedPowerSupply(const char* name) {
+static bool isScopedPowerSupply(const char* name) {
constexpr char kScopeDevice[] = "Device";
String8 path;
@@ -247,32 +301,31 @@
void BatteryMonitor::updateValues(void) {
initHealthInfo(mHealthInfo.get());
- HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
-
if (!mHealthdConfig->batteryPresentPath.isEmpty())
- props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+ mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
else
- props.batteryPresent = mBatteryDevicePresent;
+ mHealthInfo->batteryPresent = mBatteryDevicePresent;
- props.batteryLevel = mBatteryFixedCapacity ?
- mBatteryFixedCapacity :
- getIntField(mHealthdConfig->batteryCapacityPath);
- props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+ mHealthInfo->batteryLevel = mBatteryFixedCapacity
+ ? mBatteryFixedCapacity
+ : getIntField(mHealthdConfig->batteryCapacityPath);
+ mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
- props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath);
+ mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
if (!mHealthdConfig->batteryFullChargePath.isEmpty())
- props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
+ mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
- props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+ mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
- props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
+ mHealthInfo->batteryChargeCounterUah =
+ getIntField(mHealthdConfig->batteryChargeCounterPath);
if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
- mHealthInfo->legacy.batteryCurrentAverage =
+ mHealthInfo->batteryCurrentAverageMicroamps =
getIntField(mHealthdConfig->batteryCurrentAvgPath);
if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
@@ -283,9 +336,9 @@
mHealthInfo->batteryFullChargeDesignCapacityUah =
getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
- props.batteryTemperature = mBatteryFixedTemperature ?
- mBatteryFixedTemperature :
- getIntField(mHealthdConfig->batteryTemperaturePath);
+ mHealthInfo->batteryTemperatureTenthsCelsius =
+ mBatteryFixedTemperature ? mBatteryFixedTemperature
+ : getIntField(mHealthdConfig->batteryTemperaturePath);
std::string buf;
@@ -293,13 +346,13 @@
mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
- props.batteryStatus = getBatteryStatus(buf.c_str());
+ mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
- props.batteryHealth = getBatteryHealth(buf.c_str());
+ mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
- props.batteryTechnology = String8(buf.c_str());
+ mHealthInfo->batteryTechnology = String8(buf.c_str());
double MaxPower = 0;
@@ -313,29 +366,26 @@
mChargerNames[i].string());
switch(readPowerSupplyType(path)) {
case ANDROID_POWER_SUPPLY_TYPE_AC:
- props.chargerAcOnline = true;
+ mHealthInfo->chargerAcOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_USB:
- props.chargerUsbOnline = true;
+ mHealthInfo->chargerUsbOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
- props.chargerWirelessOnline = true;
+ mHealthInfo->chargerWirelessOnline = true;
break;
case ANDROID_POWER_SUPPLY_TYPE_DOCK:
- mChargerDockOnline = true;
+ mHealthInfo->chargerDockOnline = true;
break;
default:
path.clear();
path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
- if (access(path.string(), R_OK) == 0) {
- mChargerDockOnline = true;
- KLOG_INFO(LOG_TAG, "%s: online\n",
- mChargerNames[i].string());
- } else {
+ if (access(path.string(), R_OK) == 0)
+ mHealthInfo->chargerDockOnline = true;
+ else
KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
mChargerNames[i].string());
- }
}
path.clear();
path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
@@ -354,38 +404,34 @@
double power = ((double)ChargingCurrent / MILLION) *
((double)ChargingVoltage / MILLION);
if (MaxPower < power) {
- props.maxChargingCurrent = ChargingCurrent;
- props.maxChargingVoltage = ChargingVoltage;
+ mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
+ mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
MaxPower = power;
}
}
}
}
-void BatteryMonitor::logValues(void) {
- logValues(*mHealthInfo, *mHealthdConfig);
-}
-
-void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
- const struct healthd_config& healthd_config) {
+static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
char dmesgline[256];
size_t len;
- const HealthInfo_1_0& props = health_info.legacy.legacy;
if (props.batteryPresent) {
snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
- props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
- abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
- props.batteryHealth, props.batteryStatus);
+ props.batteryLevel, props.batteryVoltageMillivolts,
+ props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
+ abs(props.batteryTemperatureTenthsCelsius / 10),
+ abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
+ props.batteryStatus);
len = strlen(dmesgline);
if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
- props.batteryCurrent);
+ props.batteryCurrentMicroamps);
}
if (!healthd_config.batteryFullChargePath.isEmpty()) {
len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
- props.batteryFullCharge);
+ props.batteryFullChargeUah);
}
if (!healthd_config.batteryCycleCountPath.isEmpty()) {
@@ -396,17 +442,28 @@
len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
}
- snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
+ snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
- props.chargerWirelessOnline ? "w" : "");
+ props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
}
+void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
+ const struct healthd_config& healthd_config) {
+ HealthInfo aidl_health_info;
+ (void)android::h2a::translate(health_info, &aidl_health_info);
+ doLogValues(aidl_health_info, healthd_config);
+}
+
+void BatteryMonitor::logValues(void) {
+ doLogValues(*mHealthInfo, *mHealthdConfig);
+}
+
bool BatteryMonitor::isChargerOnline() {
- const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+ const HealthInfo& props = *mHealthInfo;
return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
- mChargerDockOnline;
+ props.chargerDockOnline;
}
int BatteryMonitor::getChargeStatus() {
@@ -489,19 +546,19 @@
void BatteryMonitor::dumpState(int fd) {
int v;
char vs[128];
- const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+ const HealthInfo& props = *mHealthInfo;
snprintf(vs, sizeof(vs),
"ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
- mChargerDockOnline, props.maxChargingCurrent, props.maxChargingVoltage);
+ props.chargerDockOnline, props.maxChargingCurrentMicroamps,
+ props.maxChargingVoltageMicrovolts);
write(fd, vs, strlen(vs));
snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
props.batteryStatus, props.batteryHealth, props.batteryPresent);
write(fd, vs, strlen(vs));
- snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
- props.batteryLevel, props.batteryVoltage,
- props.batteryTemperature);
+ snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
+ props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
write(fd, vs, strlen(vs));
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
@@ -523,7 +580,7 @@
}
if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
- snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
+ snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
write(fd, vs, strlen(vs));
}
@@ -533,7 +590,7 @@
}
if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
- snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
+ snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
write(fd, vs, strlen(vs));
}
}
@@ -551,13 +608,13 @@
while ((entry = readdir(dir.get()))) {
const char* name = entry->d_name;
- std::vector<String8>::iterator itIgnoreName;
if (!strcmp(name, ".") || !strcmp(name, ".."))
continue;
- itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
- hc->ignorePowerSupplyNames.end(), String8(name));
+ std::vector<String8>::iterator itIgnoreName =
+ find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
+ String8(name));
if (itIgnoreName != hc->ignorePowerSupplyNames.end())
continue;
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
index 5893d10..17e363d 100644
--- a/healthd/TEST_MAPPING
+++ b/healthd/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libhealthd_charger_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libhealthd_charger_test"
+ }
]
}
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 89c2e25..8cbf5ea 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -25,6 +25,10 @@
#include <healthd/healthd.h>
+namespace aidl::android::hardware::health {
+class HealthInfo;
+} // namespace aidl::android::hardware::health
+
namespace android {
namespace hardware {
namespace health {
@@ -59,9 +63,10 @@
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
- const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
- const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
- const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
+ android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;
+ android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;
+ android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;
+ const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;
void updateValues(void);
void logValues(void);
@@ -76,15 +81,7 @@
bool mBatteryDevicePresent;
int mBatteryFixedCapacity;
int mBatteryFixedTemperature;
- // TODO(b/214126090): to migrate to AIDL HealthInfo
- bool mChargerDockOnline;
- std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
-
- int readFromFile(const String8& path, std::string* buf);
- PowerSupplyType readPowerSupplyType(const String8& path);
- bool getBooleanField(const String8& path);
- int getIntField(const String8& path);
- bool isScopedPowerSupply(const char* name);
+ std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
};
}; // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index 66427dc..dd67d04 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,12 +162,15 @@
"libavb",
"libc++fs",
"libcgrouprc_format",
+ "libfsverity_init",
"liblmkd_utils",
+ "libmini_keyctl_static",
"libmodprobe",
"libprocinfo",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
+ "libsigningutils",
"libsnapshot_cow",
"libsnapshot_init",
"libxml2",
@@ -178,6 +181,7 @@
"libbacktrace",
"libbase",
"libbootloader_message",
+ "libcrypto",
"libcutils",
"libdl",
"libext4_utils",
@@ -192,6 +196,7 @@
"libprocessgroup_setup",
"libselinux",
"libutils",
+ "libziparchive",
],
bootstrap: true,
visibility: [":__subpackages__"],
@@ -527,8 +532,8 @@
cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
}
-cc_binary {
- name: "host_init_verifier",
+cc_defaults {
+ name: "init_host_defaults",
host_supported: true,
cflags: [
"-Wall",
@@ -551,7 +556,6 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
],
- srcs: init_common_sources + init_host_sources,
proto: {
type: "lite",
},
@@ -569,6 +573,26 @@
},
}
+cc_binary {
+ name: "host_init_verifier",
+ defaults: ["init_host_defaults"],
+ srcs: init_common_sources + init_host_sources,
+}
+
+cc_library_host_static {
+ name: "libinit_host",
+ defaults: ["init_host_defaults"],
+ srcs: init_common_sources,
+ export_include_dirs: ["."],
+ proto: {
+ export_proto_headers: true,
+ },
+ visibility: [
+ // host_apex_verifier performs a subset of init.rc validation
+ "//system/apex/tools",
+ ],
+}
+
sh_binary {
name: "extra_free_kbytes.sh",
src: "extra_free_kbytes.sh",
diff --git a/init/OWNERS b/init/OWNERS
index 9e70e7d..4604d06 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1,2 @@
dvander@google.com
+jiyong@google.com
diff --git a/init/README.md b/init/README.md
index c102b1f..13c6ebd 100644
--- a/init/README.md
+++ b/init/README.md
@@ -804,13 +804,18 @@
`init.svc.<name>`
> State of a named service ("stopped", "stopping", "running", "restarting")
-`dev.mnt.blk.<mount_point>`
+`dev.mnt.dev.<mount_point>`, `dev.mnt.blk.<mount_point>`, `dev.mnt.rootdisk.<mount_point>`
> Block device base name associated with a *mount_point*.
The *mount_point* has / replaced by . and if referencing the root mount point
- "/", it will use "/root", specifically `dev.mnt.blk.root`.
- Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
- `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
- characteristics in a device agnostic manner.
+ "/", it will use "/root".
+ `dev.mnt.dev.<mount_point>` indicates a block device attached to filesystems.
+ (e.g., dm-N or sdaN/mmcblk0pN to access `/sys/fs/ext4/${dev.mnt.dev.<mount_point>}/`)
+
+ `dev.mnt.blk.<mount_point>` indicates the disk partition to the above block device.
+ (e.g., sdaN / mmcblk0pN to access `/sys/class/block/${dev.mnt.blk.<mount_point>}/`)
+
+ `dev.mnt.rootdisk.<mount_point>` indicates the root disk to contain the above disk partition.
+ (e.g., sda / mmcblk0 to access `/sys/class/block/${dev.mnt.rootdisk.<mount_point>}/queue`)
Init responds to properties that begin with `ctl.`. These properties take the format of
`ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter. The
@@ -1034,7 +1039,7 @@
/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
as 2). Note that the decision to boot normally into Android instead of booting
into recovery mode is made if androidboot.force_normal_boot=1 is present in the
-kernel commandline.
+kernel commandline, or in bootconfig with Android S and later.
Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 2401da3..3c7107a 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -147,6 +147,12 @@
Ueventd will additionally log all messages sent to stderr from the external program to the serial
console after the external program has exited.
+If the kernel command-line argument `firmware_class.path` is set, this path
+will be used first by the kernel to search for the firmware files. If found,
+ueventd will not be called at all. See the
+[kernel documentation](https://www.kernel.org/doc/html/v5.10/driver-api/firmware/fw_search_path.html)
+for more details on this feature.
+
## Coldboot
--------
Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 03b9eaa..fa1627c 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -9,5 +9,16 @@
{
"name": "MicrodroidHostTestCases"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "CtsInitTestCases"
+ },
+ {
+ "name": "init_kill_services_test"
+ },
+ {
+ "name": "MicrodroidHostTestCases"
+ }
]
}
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0eb894b..01db4f5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1306,58 +1306,11 @@
}
globfree(&glob_result);
- // Compare all files /apex/path.#rc and /apex/path.rc with the same "/apex/path" prefix,
- // choosing the one with the highest # that doesn't exceed the system's SDK.
- // (.rc == .0rc for ranking purposes)
- //
int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
- std::map<std::string, std::pair<std::string, int>> script_map;
-
- for (const auto& c : configs) {
- int sdk = 0;
- const std::vector<std::string> parts = android::base::Split(c, ".");
- std::string base;
- if (parts.size() < 2) {
- continue;
- }
-
- // parts[size()-1], aka the suffix, should be "rc" or "#rc"
- // any other pattern gets discarded
-
- const auto& suffix = parts[parts.size() - 1];
- if (suffix == "rc") {
- sdk = 0;
- } else {
- char trailer[9] = {0};
- int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
- if (r != 2) {
- continue;
- }
- if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
- continue;
- }
- }
-
- if (sdk < 0 || sdk > active_sdk) {
- continue;
- }
-
- base = parts[0];
- for (unsigned int i = 1; i < parts.size() - 1; i++) {
- base = base + "." + parts[i];
- }
-
- // is this preferred over what we already have
- auto it = script_map.find(base);
- if (it == script_map.end() || it->second.second < sdk) {
- script_map[base] = std::make_pair(c, sdk);
- }
- }
-
bool success = true;
- for (const auto& m : script_map) {
- success &= parser.ParseConfigFile(m.second.first);
+ for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
+ success &= parser.ParseConfigFile(c);
}
ServiceList::GetInstance().MarkServicesUpdate();
if (success) {
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 74d8aac..0580f86 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -23,6 +23,8 @@
#include <functional>
#include <map>
+#include <android-base/logging.h>
+
namespace android {
namespace init {
@@ -42,8 +44,11 @@
if (!events) {
return Error() << "Must specify events";
}
- auto sp = std::make_shared<decltype(handler)>(std::move(handler));
- auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(sp));
+
+ Info info;
+ info.events = events;
+ info.handler = std::make_shared<decltype(handler)>(std::move(handler));
+ auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
if (!inserted) {
return Error() << "Cannot specify two epoll handlers for a given FD";
}
@@ -84,8 +89,14 @@
}
std::vector<std::shared_ptr<Handler>> pending_functions;
for (int i = 0; i < num_events; ++i) {
- auto sp = *reinterpret_cast<std::shared_ptr<Handler>*>(ev[i].data.ptr);
- pending_functions.emplace_back(std::move(sp));
+ auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
+ if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
+ (ev[i].events & EPOLLIN) != ev[i].events) {
+ // This handler wants to know about exception events, and just got one.
+ // Log something informational.
+ LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
+ }
+ pending_functions.emplace_back(info.handler);
}
return pending_functions;
diff --git a/init/epoll.h b/init/epoll.h
index 0df5289..f58ae8d 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -46,8 +46,13 @@
std::optional<std::chrono::milliseconds> timeout);
private:
+ struct Info {
+ std::shared_ptr<Handler> handler;
+ uint32_t events;
+ };
+
android::base::unique_fd epoll_fd_;
- std::map<int, std::shared_ptr<Handler>> epoll_handlers_;
+ std::map<int, Info> epoll_handlers_;
};
} // namespace init
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c7b7b0c..d050ed7 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -27,6 +27,7 @@
#include <sys/utsname.h>
#include <unistd.h>
+#include <chrono>
#include <filesystem>
#include <string>
#include <vector>
@@ -107,6 +108,39 @@
cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
+static void Copy(const char* src, const char* dst) {
+ if (link(src, dst) == 0) {
+ LOG(INFO) << "hard linking " << src << " to " << dst << " succeeded";
+ return;
+ }
+ PLOG(FATAL) << "hard linking " << src << " to " << dst << " failed";
+}
+
+// Move snapuserd before switching root, so that it is available at the same path
+// after switching root.
+void PrepareSwitchRoot() {
+ constexpr const char* src = "/system/bin/snapuserd";
+ constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+
+ 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 (access(dst_dir.c_str(), F_OK) != 0) {
+ if (!fs::create_directories(dst_dir, ec)) {
+ LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
+ }
+ }
+ Copy(src, dst);
+}
} // namespace
std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -123,7 +157,7 @@
}
#define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
+bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
struct utsname uts;
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
@@ -172,7 +206,8 @@
}
Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
- bool retval = m.LoadListedModules(!want_console);
+ bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
+ : m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
return retval;
@@ -254,6 +289,9 @@
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
+
+ // First stage init stores Mainline sepolicy here.
+ CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
SetStdioToDevNull(argv);
@@ -282,11 +320,13 @@
}
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
+ auto want_parallel =
+ bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos;
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
- module_count)) {
+ want_parallel, module_count)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
@@ -301,12 +341,11 @@
<< module_elapse_time.count() << " ms";
}
-
bool created_devices = false;
if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
if (!IsRecoveryMode()) {
created_devices = DoCreateDevices();
- if (!created_devices){
+ if (!created_devices) {
LOG(ERROR) << "Failed to create device nodes early";
}
}
@@ -349,10 +388,11 @@
if (ForceNormalBoot(cmdline, bootconfig)) {
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) {
- LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+ PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}
diff --git a/init/init.cpp b/init/init.cpp
index 1df4c44..5a0b3a6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -33,7 +33,6 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-#include <filesystem>
#include <functional>
#include <map>
#include <memory>
@@ -47,7 +46,6 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <backtrace/Backtrace.h>
@@ -580,12 +578,29 @@
HandlePowerctlMessage("shutdown,container");
}
+static constexpr std::chrono::milliseconds kDiagnosticTimeout = 10s;
+
static void HandleSignalFd() {
signalfd_siginfo siginfo;
- ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
- if (bytes_read != sizeof(siginfo)) {
- PLOG(ERROR) << "Failed to read siginfo from signal_fd";
- return;
+ auto started = std::chrono::steady_clock::now();
+ for (;;) {
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read < 0 && errno == EAGAIN) {
+ auto now = std::chrono::steady_clock::now();
+ std::chrono::duration<double> waited = now - started;
+ if (waited >= kDiagnosticTimeout) {
+ LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";
+ started = now;
+ }
+
+ std::this_thread::sleep_for(100ms);
+ continue;
+ }
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+ return;
+ }
+ break;
}
switch (siginfo.ssi_signo) {
@@ -641,12 +656,13 @@
LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
}
- signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
if (signal_fd == -1) {
PLOG(FATAL) << "failed to create signalfd";
}
- if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {
+ constexpr int flags = EPOLLIN | EPOLLPRI;
+ if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
LOG(FATAL) << result.error();
}
}
@@ -775,82 +791,6 @@
return {};
}
-static bool SystemReadSmokeTest() {
- std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
- android::base::unique_fd fd(open(dev.c_str(), O_RDONLY));
- if (fd < 0) {
- PLOG(ERROR) << "open " << dev << " failed, will not diangose snapuserd hangs";
- return false;
- }
-
- for (size_t i = 1; i <= 100; i++) {
- // Skip around the partition a bit.
- size_t offset = i * 4096 * 512;
-
- char b;
- ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), &b, 1, offset));
- if (n < 0) {
- PLOG(ERROR) << "snapuserd smoke test read failed";
- return false;
- }
- }
- return true;
-}
-
-static void DiagnoseSnapuserdHang(pid_t pid) {
- bool succeeded = false;
-
- std::mutex m;
- std::condition_variable cv;
-
- // Enforce an ordering between this and the thread startup, by taking the
- // lock before we lanuch the thread.
- std::unique_lock<std::mutex> cv_lock(m);
-
- std::thread t([&]() -> void {
- std::lock_guard<std::mutex> lock(m);
- succeeded = SystemReadSmokeTest();
- cv.notify_all();
- });
-
- auto join = android::base::make_scope_guard([&]() -> void {
- // If the smoke test is hung, then this will too. We expect the device to
- // automatically reboot once the watchdog kicks in.
- t.join();
- });
-
- auto now = std::chrono::system_clock::now();
- auto deadline = now + 10s;
- auto status = cv.wait_until(cv_lock, deadline);
- if (status == std::cv_status::timeout) {
- LOG(ERROR) << "snapuserd smoke test timed out";
- } else if (!succeeded) {
- LOG(ERROR) << "snapuserd smoke test failed";
- }
-
- if (succeeded) {
- LOG(INFO) << "snapuserd smoke test succeeded";
- return;
- }
-
- while (true) {
- LOG(ERROR) << "snapuserd problem detected, printing open fds";
-
- std::error_code ec;
- std::string proc_dir = "/proc/" + std::to_string(pid) + "/fd";
- for (const auto& entry : std::filesystem::directory_iterator(proc_dir)) {
- std::string target;
- if (android::base::Readlink(entry.path(), &target)) {
- LOG(ERROR) << "snapuserd opened: " << target;
- } else {
- LOG(ERROR) << "snapuserd opened: " << entry.path();
- }
- }
-
- std::this_thread::sleep_for(10s);
- }
-}
-
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
@@ -864,11 +804,6 @@
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
- if (auto pid = GetSnapuserdFirstStagePid()) {
- std::thread t(DiagnoseSnapuserdHang, *pid);
- t.detach();
- }
-
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
@@ -1020,7 +955,7 @@
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
- auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
+ auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
@@ -1060,6 +995,13 @@
for (const auto& function : *pending_functions) {
(*function)();
}
+ } else if (Service::is_exec_service_running()) {
+ std::chrono::duration<double> waited =
+ std::chrono::steady_clock::now() - Service::exec_service_started();
+ if (waited >= kDiagnosticTimeout) {
+ LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
+ << " without SIGCHLD";
+ }
}
if (!IsShuttingDown()) {
HandleControlMessages();
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index f0d8d45..227ce2f 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <algorithm>
+#include <filesystem>
#include <string>
#include <utility>
#include <vector>
@@ -32,6 +33,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fstab/fstab.h>
@@ -39,6 +41,9 @@
#include "epoll.h"
+using android::base::Basename;
+using android::base::StringPrintf;
+
namespace android {
namespace init {
@@ -67,41 +72,82 @@
return MountHandlerEntry(fields[0], fields[1], fields[2]);
}
+// return sda25 for dm-4, sda25 for sda25, or mmcblk0p24 for mmcblk0p24
+std::string GetDiskPart(std::string blockdev) {
+ if (blockdev.find('/') != std::string::npos) return {};
+
+ while (android::base::StartsWith(blockdev, "dm-")) {
+ auto& dm = dm::DeviceMapper::Instance();
+ std::optional<std::string> parent = dm.GetParentBlockDeviceByPath("/dev/block/" + blockdev);
+ if (parent) {
+ blockdev = android::base::Basename(*parent);
+ } else {
+ return {};
+ }
+ }
+ return blockdev;
+}
+
+// return sda for sda25, or mmcblk0 for mmcblk0p24
+std::string GetRootDisk(std::string blockdev) {
+ if (blockdev.empty()) return {};
+ if (blockdev.find('/') != std::string::npos) return {};
+
+ std::error_code ec;
+ for (const auto& entry : std::filesystem::directory_iterator("/sys/block", ec)) {
+ const std::string path = entry.path().string();
+ if (std::filesystem::exists(StringPrintf("%s/%s", path.c_str(), blockdev.c_str()))) {
+ return Basename(path);
+ }
+ }
+ return {};
+}
+
void SetMountProperty(const MountHandlerEntry& entry, bool add) {
static constexpr char devblock[] = "/dev/block/";
if (!android::base::StartsWith(entry.blk_device, devblock)) return;
- std::string value;
+ auto target = entry.blk_device.substr(strlen(devblock));
+ std::string diskpart, rootdisk;
if (add) {
- value = entry.blk_device.substr(strlen(devblock));
- if (android::base::StartsWith(value, "sd")) {
- // All sd partitions inherit their queue characteristics
- // from the whole device reference. Strip partition number.
- auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
- if (it != value.end()) value.erase(it, value.end());
- }
- auto queue = "/sys/block/" + value + "/queue";
+ diskpart = GetDiskPart(target);
+ rootdisk = GetRootDisk(diskpart);
+
struct stat sb;
- if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
- if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+ if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) rootdisk = "";
// Clear the noise associated with loopback and APEX.
- if (android::base::StartsWith(value, "loop")) value = "";
- if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+ if (android::base::StartsWith(target, "loop")) rootdisk = "";
+ if (android::base::StartsWith(entry.mount_point, "/apex/")) rootdisk = "";
}
auto mount_prop = entry.mount_point;
if (mount_prop == "/") mount_prop = "/root";
std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
auto dev_mount_prop = "dev.mnt.dev" + mount_prop;
- // Set property even if its value does not change to trigger 'on property:'
+ auto rootdisk_mount_prop = "dev.mnt.rootdisk" + mount_prop;
+ // Set property even if its rootdisk does not change to trigger 'on property:'
// handling, except for clearing non-existent or already clear property.
// Goal is reduction of empty properties and associated triggers.
- if (value.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
- android::base::SetProperty(blk_mount_prop, value);
- if (!value.empty()) {
- android::base::SetProperty(dev_mount_prop, entry.blk_device.substr(strlen(devblock)));
- } else {
+ if (rootdisk.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
+
+ if (rootdisk.empty()) {
+ android::base::SetProperty(blk_mount_prop, "");
android::base::SetProperty(dev_mount_prop, "");
+ android::base::SetProperty(rootdisk_mount_prop, "");
+ return;
}
+
+ // 1. dm-N
+ // dev.mnt.dev.data = dm-N
+ // dev.mnt.blk.data = sdaN or mmcblk0pN
+ // dev.mnt.rootdisk.data = sda or mmcblk0
+ //
+ // 2. sdaN or mmcblk0pN
+ // dev.mnt.dev.data = sdaN or mmcblk0pN
+ // dev.mnt.blk.data = sdaN or mmcblk0pN
+ // dev.mnt.rootdisk.data = sda or mmcblk0
+ android::base::SetProperty(dev_mount_prop, target);
+ android::base::SetProperty(blk_mount_prop, diskpart);
+ android::base::SetProperty(rootdisk_mount_prop, rootdisk);
}
} // namespace
diff --git a/init/parser.cpp b/init/parser.cpp
index 5c18551..abc2017 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -18,6 +18,8 @@
#include <dirent.h>
+#include <map>
+
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -154,6 +156,58 @@
return true;
}
+std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk) {
+ std::vector<std::string> filtered_configs;
+
+ std::map<std::string, std::pair<std::string, int>> script_map;
+ for (const auto& c : configs) {
+ int sdk = 0;
+ const std::vector<std::string> parts = android::base::Split(c, ".");
+ std::string base;
+ if (parts.size() < 2) {
+ continue;
+ }
+
+ // parts[size()-1], aka the suffix, should be "rc" or "#rc"
+ // any other pattern gets discarded
+
+ const auto& suffix = parts[parts.size() - 1];
+ if (suffix == "rc") {
+ sdk = 0;
+ } else {
+ char trailer[9] = {0};
+ int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
+ if (r != 2) {
+ continue;
+ }
+ if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
+ continue;
+ }
+ }
+
+ if (sdk < 0 || sdk > active_sdk) {
+ continue;
+ }
+
+ base = parts[0];
+ for (unsigned int i = 1; i < parts.size() - 1; i++) {
+ base = base + "." + parts[i];
+ }
+
+ // is this preferred over what we already have
+ auto it = script_map.find(base);
+ if (it == script_map.end() || it->second.second < sdk) {
+ script_map[base] = std::make_pair(c, sdk);
+ }
+ }
+
+ for (const auto& m : script_map) {
+ filtered_configs.push_back(m.second.first);
+ }
+ return filtered_configs;
+}
+
bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 95b0cd7..2f4108f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,6 +76,12 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ // Compare all files */path.#rc and */path.rc with the same path prefix.
+ // Keep the one with the highest # that doesn't exceed the system's SDK.
+ // (.rc == .0rc for ranking purposes)
+ std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk);
+
// Host init verifier check file permissions.
bool ParseConfigFileInsecure(const std::string& path);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f2b2cda..9f7c215 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1035,8 +1035,8 @@
constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
// Api level properties of the board. The order of the properties must be kept.
- std::vector<std::string> BOARD_API_LEVEL_PROPS = {
- "ro.board.api_level", "ro.board.first_api_level", "ro.vendor.build.version.sdk"};
+ std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
+ "ro.board.first_api_level"};
// Api level properties of the device. The order of the properties must be kept.
std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
"ro.build.version.sdk"};
@@ -1172,6 +1172,9 @@
// Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
+ if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
+ }
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
&property_infos);
@@ -1195,6 +1198,7 @@
LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
+ LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 28cd012..be8c554 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,26 +26,29 @@
// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
-// The split policy is for supporting treble devices. It splits the SEPolicy across files on
-// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
-// portion of the policy). This is necessary to allow the system image to be updated independently
-// of the vendor image, while maintaining contributions from both partitions in the SEPolicy. This
-// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
-// identical to the system image shipped on a vendor's device.
+// The split policy is for supporting treble devices and updateable apexes. It splits the SEPolicy
+// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
+// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
+// /odm/etc/selinux, and /dev/selinux (the apex portion of policy). This is necessary to allow
+// images to be updated independently of the vendor image, while maintaining contributions from
+// multiple partitions in the SEPolicy. This is especially important for VTS testing, where the
+// SEPolicy on the Google System Image may not be identical to the system image shipped on a
+// vendor's device.
// The split SEPolicy is loaded as described below:
// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
// /odm/etc/selinux/precompiled_sepolicy if odm parition is present. Stored along with this file
-// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
-// were used to compile this precompiled policy. The system partition contains a similar sha256
-// of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext and
-// product paritition contain sha256 hashes of their SEPolicy. The init loads this
+// are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
+// that were used to compile this precompiled policy. The system partition contains a similar
+// sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
+// product, and apex contain sha256 hashes of their SEPolicy. Init loads this
// precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-// /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
-// 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
-// OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+// /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
+// respectively.
+// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (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 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.
@@ -58,19 +61,25 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <fstream>
+#include <CertUtils.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
+#include <fsverity_init.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
+#include <mini_keyctl_utils.h>
#include <selinux/android.h>
+#include <ziparchive/zip_archive.h>
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
@@ -247,6 +256,7 @@
precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
{"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
+ {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
};
for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -325,7 +335,7 @@
// * vendor -- policy needed due to logic contained in the vendor image,
// * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
// with newer versions of platform policy.
- // * (optional) policy needed due to logic on product, system_ext, or odm images.
+ // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
// secilc is invoked to compile the above three policy files into a single monolithic policy
// file. This file is then loaded into the kernel.
@@ -421,6 +431,12 @@
if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
odm_policy_cil_file.clear();
}
+
+ // apex_sepolicy.cil is default but optional.
+ std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
+ if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
+ apex_policy_cil_file.clear();
+ }
const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
// clang-format off
@@ -463,6 +479,9 @@
if (!odm_policy_cil_file.empty()) {
compile_args.push_back(odm_policy_cil_file.c_str());
}
+ if (!apex_policy_cil_file.empty()) {
+ compile_args.push_back(apex_policy_cil_file.c_str());
+ }
compile_args.push_back(nullptr);
if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -489,6 +508,197 @@
return true;
}
+constexpr const char* kSigningCertRelease =
+ "/system/etc/selinux/com.android.sepolicy.cert-release.der";
+constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
+const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
+const std::string kSepolicyZip = "SEPolicy.zip";
+const std::string kSepolicySignature = "SEPolicy.zip.sig";
+
+const std::string kTmpfsDir = "/dev/selinux/";
+
+// Files that are deleted after policy is compiled/loaded.
+const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
+// Files that need to persist because they are used by userspace processes.
+const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
+ "apex_service_contexts", "apex_seapp_contexts",
+ "apex_test"};
+
+Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
+ ZipEntry entry;
+ std::string dstPath = kTmpfsDir + fileName;
+
+ int ret = FindEntry(archive, fileName, &entry);
+ if (ret != 0) {
+ // All files are optional. If a file doesn't exist, return without error.
+ return {};
+ }
+
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd == -1) {
+ return Error() << "Failed to open " << dstPath;
+ }
+
+ ret = ExtractEntryToFile(archive, &entry, fd);
+ if (ret != 0) {
+ return Error() << "Failed to extract entry \"" << fileName << "\" ("
+ << entry.uncompressed_length << " bytes) to \"" << dstPath
+ << "\": " << ErrorCodeString(ret);
+ }
+
+ return {};
+}
+
+Result<void> GetPolicyFromApex(const std::string& dir) {
+ LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
+ unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+ if (fd < 0) {
+ return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
+ }
+
+ ZipArchiveHandle handle;
+ int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
+ /*assume_ownership=*/false);
+ if (ret < 0) {
+ return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
+ << ErrorCodeString(ret);
+ }
+
+ auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
+
+ for (const auto& file : kApexSepolicy) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ for (const auto& file : kApexSepolicyTmp) {
+ auto extract = PutFileInTmpfs(handle, file);
+ if (!extract.ok()) {
+ return extract.error();
+ }
+ }
+ return {};
+}
+
+Result<void> LoadSepolicyApexCerts() {
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ return Error() << "Failed to find .fs-verity keyring id";
+ }
+
+ // TODO(b/199914227) the release key should always exist. Once it's checked in, start
+ // throwing an error here if it doesn't exist.
+ if (access(kSigningCertRelease, F_OK) == 0) {
+ LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
+ }
+ return {};
+}
+
+Result<void> SepolicyFsVerityCheck() {
+ return Error() << "TODO implementent support for fsverity SEPolicy.";
+}
+
+Result<void> SepolicyCheckSignature(const std::string& dir) {
+ std::string signature;
+ if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
+ return ErrnoError() << "Failed to read " << kSepolicySignature;
+ }
+
+ std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
+ if (!sepolicyZip) {
+ return Error() << "Failed to open " << kSepolicyZip;
+ }
+ sepolicyZip.seekg(0);
+ std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
+ std::istreambuf_iterator<char>());
+
+ auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
+ if (!releaseKey.ok()) {
+ return releaseKey.error();
+ }
+
+ return verifySignature(sepolicyStr, signature, *releaseKey);
+}
+
+Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
+ if (supportsFsVerity) {
+ auto fsVerityCheck = SepolicyFsVerityCheck();
+ if (fsVerityCheck.ok()) {
+ return fsVerityCheck;
+ }
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
+ // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
+ }
+
+ auto sepolicySignature = SepolicyCheckSignature(dir);
+ if (!sepolicySignature.ok()) {
+ return Error() << "Apex SEPolicy failed signature check";
+ }
+ return {};
+}
+
+void CleanupApexSepolicy() {
+ for (const auto& file : kApexSepolicyTmp) {
+ std::string path = kTmpfsDir + file;
+ unlink(path.c_str());
+ }
+}
+
+// Updatable sepolicy is shipped within an zip within an APEX. Because
+// it needs to be available before Apexes are mounted, apexd copies
+// the zip from the APEX and stores it in /metadata/sepolicy. If there is
+// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
+// loaded from /system/etc/selinux/apex. Init performs the following
+// steps on boot:
+//
+// 1. Validates the zip by checking its signature against a public key that is
+// stored in /system/etc/selinux.
+// 2. Extracts files from zip and stores them in /dev/selinux.
+// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
+// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
+// is used. This is the same flow as on-device compilation of policy for Treble.
+// 4. Cleans up files in /dev/selinux which are no longer needed.
+// 5. Restorecons the remaining files in /dev/selinux.
+// 6. Sets selinux into enforcing mode and continues normal booting.
+//
+void PrepareApexSepolicy() {
+ bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+ if (supportsFsVerity) {
+ auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
+ if (!loadSepolicyApexCerts.ok()) {
+ // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
+ // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+ // logic is implemented.
+ LOG(INFO) << loadSepolicyApexCerts.error();
+ }
+ }
+ // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
+ // /system.
+ auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
+ ? kSepolicyApexMetadataDir
+ : kSepolicyApexSystemDir;
+
+ auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
+ if (!sepolicyVerify.ok()) {
+ LOG(INFO) << "Error: " << sepolicyVerify.error();
+ // If signature verification fails, fall back to version on /system.
+ // This file doesn't need to be verified because it lives on the system partition which
+ // is signed and protected by verified boot.
+ dir = kSepolicyApexSystemDir;
+ }
+
+ auto apex = GetPolicyFromApex(dir);
+ if (!apex.ok()) {
+ // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
+ LOG(ERROR) << apex.error();
+ }
+}
+
void ReadPolicy(std::string* policy) {
PolicyFile policy_file;
@@ -552,6 +762,7 @@
void SelinuxRestoreContext() {
LOG(INFO) << "Running restorecon...";
selinux_android_restorecon("/dev", 0);
+ selinux_android_restorecon("/dev/console", 0);
selinux_android_restorecon("/dev/kmsg", 0);
if constexpr (WORLD_WRITABLE_KMSG) {
selinux_android_restorecon("/dev/kmsg_debug", 0);
@@ -740,9 +951,12 @@
LOG(INFO) << "Opening SELinux policy";
+ PrepareApexSepolicy();
+
// Read the policy before potentially killing snapuserd.
std::string policy;
ReadPolicy(&policy);
+ CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
@@ -760,6 +974,13 @@
snapuserd_helper = nullptr;
}
+ // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
+ // needed to transition files from tmpfs to *_contexts_file context should not be granted to
+ // any process after selinux is set into enforcing mode.
+ if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
+ PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
+ }
+
SelinuxSetEnforcement();
// We're in the kernel domain and want to transition to the init domain. File systems that
diff --git a/init/service.cpp b/init/service.cpp
index f7318cb..077477a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -127,6 +127,7 @@
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
+std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
const std::vector<std::string>& args, bool from_apex)
@@ -388,6 +389,7 @@
flags_ |= SVC_EXEC;
is_exec_service_running_ = true;
+ exec_service_started_ = std::chrono::steady_clock::now();
LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
<< " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
@@ -397,6 +399,122 @@
return {};
}
+static void ClosePipe(const std::array<int, 2>* pipe) {
+ for (const auto fd : *pipe) {
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+}
+
+Result<void> Service::CheckConsole() {
+ if (!(flags_ & SVC_CONSOLE)) {
+ return {};
+ }
+
+ if (proc_attr_.console.empty()) {
+ proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
+ }
+
+ // Make sure that open call succeeds to ensure a console driver is
+ // properly registered for the device node
+ int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
+ if (console_fd < 0) {
+ flags_ |= SVC_DISABLED;
+ return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
+ }
+ close(console_fd);
+ return {};
+}
+
+// Configures the memory cgroup properties for the service.
+void Service::ConfigureMemcg() {
+ if (swappiness_ != -1) {
+ if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+ PLOG(ERROR) << "setProcessGroupSwappiness failed";
+ }
+ }
+
+ if (soft_limit_in_bytes_ != -1) {
+ if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+ PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+ }
+ }
+
+ size_t computed_limit_in_bytes = limit_in_bytes_;
+ if (limit_percent_ != -1) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ long num_pages = sysconf(_SC_PHYS_PAGES);
+ if (page_size > 0 && num_pages > 0) {
+ size_t max_mem = SIZE_MAX;
+ if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+ max_mem = size_t(num_pages) * size_t(page_size);
+ }
+ computed_limit_in_bytes =
+ std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+ }
+ }
+
+ if (!limit_property_.empty()) {
+ // This ends up overwriting computed_limit_in_bytes but only if the
+ // property is defined.
+ computed_limit_in_bytes =
+ android::base::GetUintProperty(limit_property_, computed_limit_in_bytes, SIZE_MAX);
+ }
+
+ if (computed_limit_in_bytes != size_t(-1)) {
+ if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+ PLOG(ERROR) << "setProcessGroupLimit failed";
+ }
+ }
+}
+
+// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
+void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
+ const std::vector<Descriptor>& descriptors,
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
+ if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
+ LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
+ }
+
+ for (const auto& [key, value] : environment_vars_) {
+ setenv(key.c_str(), value.c_str(), 1);
+ }
+
+ for (const auto& descriptor : descriptors) {
+ descriptor.Publish();
+ }
+
+ if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
+ LOG(ERROR) << "failed to write pid to files: " << result.error();
+ }
+
+ // Wait until the cgroups have been created and until the cgroup controllers have been
+ // activated.
+ char byte = 0;
+ if (read((*pipefd)[0], &byte, 1) < 0) {
+ PLOG(ERROR) << "failed to read from notification channel";
+ }
+ pipefd.reset();
+ if (!byte) {
+ LOG(FATAL) << "Service '" << name_ << "' failed to start due to a fatal error";
+ _exit(EXIT_FAILURE);
+ }
+
+ if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+ LOG(ERROR) << "failed to set task profiles";
+ }
+
+ // As requested, set our gid, supplemental gids, uid, context, and
+ // priority. Aborts on failure.
+ SetProcessAttributesAndCaps();
+
+ if (!ExpandArgsAndExecv(args_, sigstop_)) {
+ PLOG(ERROR) << "cannot execv('" << args_[0]
+ << "'). See the 'Debugging init' section of init's README.md for tips";
+ }
+}
+
Result<void> Service::Start() {
auto reboot_on_failure = make_scope_guard([this] {
if (on_failure_reboot_target_) {
@@ -428,20 +546,14 @@
return {};
}
- bool needs_console = (flags_ & SVC_CONSOLE);
- if (needs_console) {
- if (proc_attr_.console.empty()) {
- proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
- }
+ std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
+ ClosePipe);
+ if (pipe(pipefd->data()) < 0) {
+ return ErrnoError() << "pipe()";
+ }
- // Make sure that open call succeeds to ensure a console driver is
- // properly registered for the device node
- int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
- if (console_fd < 0) {
- flags_ |= SVC_DISABLED;
- return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
- }
- close(console_fd);
+ if (Result<void> result = CheckConsole(); !result.ok()) {
+ return result;
}
struct stat sb;
@@ -513,38 +625,7 @@
if (pid == 0) {
umask(077);
-
- if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
- !result.ok()) {
- LOG(FATAL) << "Service '" << name_
- << "' failed to set up namespaces: " << result.error();
- }
-
- for (const auto& [key, value] : environment_vars_) {
- setenv(key.c_str(), value.c_str(), 1);
- }
-
- for (const auto& descriptor : descriptors) {
- descriptor.Publish();
- }
-
- if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
- LOG(ERROR) << "failed to write pid to files: " << result.error();
- }
-
- if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
- LOG(ERROR) << "failed to set task profiles";
- }
-
- // As requested, set our gid, supplemental gids, uid, context, and
- // priority. Aborts on failure.
- SetProcessAttributesAndCaps();
-
- if (!ExpandArgsAndExecv(args_, sigstop_)) {
- PLOG(ERROR) << "cannot execv('" << args_[0]
- << "'). See the 'Debugging init' section of init's README.md for tips";
- }
-
+ RunService(override_mount_namespace, descriptors, std::move(pipefd));
_exit(127);
}
@@ -571,53 +652,25 @@
limit_percent_ != -1 || !limit_property_.empty();
errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
if (errno != 0) {
- PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
- << ") failed for service '" << name_ << "'";
- } else if (use_memcg) {
- if (swappiness_ != -1) {
- if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
- PLOG(ERROR) << "setProcessGroupSwappiness failed";
- }
+ if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
+ return ErrnoError() << "sending notification failed";
}
+ return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+ << ") failed for service '" << name_ << "'";
+ }
- if (soft_limit_in_bytes_ != -1) {
- if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
- PLOG(ERROR) << "setProcessGroupSoftLimit failed";
- }
- }
-
- size_t computed_limit_in_bytes = limit_in_bytes_;
- if (limit_percent_ != -1) {
- long page_size = sysconf(_SC_PAGESIZE);
- long num_pages = sysconf(_SC_PHYS_PAGES);
- if (page_size > 0 && num_pages > 0) {
- size_t max_mem = SIZE_MAX;
- if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
- max_mem = size_t(num_pages) * size_t(page_size);
- }
- computed_limit_in_bytes =
- std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
- }
- }
-
- if (!limit_property_.empty()) {
- // This ends up overwriting computed_limit_in_bytes but only if the
- // property is defined.
- computed_limit_in_bytes = android::base::GetUintProperty(
- limit_property_, computed_limit_in_bytes, SIZE_MAX);
- }
-
- if (computed_limit_in_bytes != size_t(-1)) {
- if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
- PLOG(ERROR) << "setProcessGroupLimit failed";
- }
- }
+ if (use_memcg) {
+ ConfigureMemcg();
}
if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
}
+ if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
+ return ErrnoError() << "sending notification failed";
+ }
+
NotifyStateChange("running");
reboot_on_failure.Disable();
return {};
diff --git a/init/service.h b/init/service.h
index 3289f54..d233cbf 100644
--- a/init/service.h
+++ b/init/service.h
@@ -102,6 +102,9 @@
size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
static bool is_exec_service_running() { return is_exec_service_running_; }
+ static std::chrono::time_point<std::chrono::steady_clock> exec_service_started() {
+ return exec_service_started_;
+ }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
@@ -145,9 +148,17 @@
void KillProcessGroup(int signal, bool report_oneshot = false);
void SetProcessAttributesAndCaps();
void ResetFlagsForStart();
+ Result<void> CheckConsole();
+ void ConfigureMemcg();
+ void RunService(
+ const std::optional<MountNamespace>& override_mount_namespace,
+ const std::vector<Descriptor>& descriptors,
+ std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
static unsigned long next_start_order_;
static bool is_exec_service_running_;
+ static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
+ static pid_t exec_service_pid_;
std::string name_;
std::set<std::string> classnames_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 57c311a..9e914ee 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -27,6 +27,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
+#include <processgroup/processgroup.h>
#include <system/thread_defs.h>
#include "lmkd_service.h"
@@ -202,7 +203,7 @@
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
- if (svc->interfaces().count(fullname) > 0) {
+ if (svc->interfaces().count(fullname) > 0 && !service_->is_override()) {
return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
<< " but is already defined by " << svc->name();
}
@@ -395,7 +396,15 @@
Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
args.erase(args.begin());
- service_->task_profiles_ = std::move(args);
+ if (service_->task_profiles_.empty()) {
+ service_->task_profiles_ = std::move(args);
+ } else {
+ // Some task profiles might have been added during writepid conversions
+ service_->task_profiles_.insert(service_->task_profiles_.end(),
+ std::make_move_iterator(args.begin()),
+ std::make_move_iterator(args.end()));
+ args.clear();
+ }
return {};
}
@@ -521,8 +530,37 @@
return {};
}
+// Convert legacy paths used to migrate processes between cgroups using writepid command.
+// We can't get these paths from TaskProfiles because profile definitions are changing
+// when we migrate to cgroups v2 while these hardcoded paths stay the same.
+static std::optional<const std::string> ConvertTaskFileToProfile(const std::string& file) {
+ static const std::map<const std::string, const std::string> map = {
+ {"/dev/stune/top-app/tasks", "MaxPerformance"},
+ {"/dev/stune/foreground/tasks", "HighPerformance"},
+ {"/dev/cpuset/camera-daemon/tasks", "CameraServiceCapacity"},
+ {"/dev/cpuset/foreground/tasks", "ProcessCapacityHigh"},
+ {"/dev/cpuset/system-background/tasks", "ServiceCapacityLow"},
+ {"/dev/stune/nnapi-hal/tasks", "NNApiHALPerformance"},
+ {"/dev/blkio/background/tasks", "LowIoPriority"},
+ };
+ auto iter = map.find(file);
+ return iter == map.end() ? std::nullopt : std::make_optional<const std::string>(iter->second);
+}
+
Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
args.erase(args.begin());
+ // Convert any cgroup writes into appropriate task_profiles
+ for (auto iter = args.begin(); iter != args.end();) {
+ auto task_profile = ConvertTaskFileToProfile(*iter);
+ if (task_profile) {
+ LOG(WARNING) << "'writepid " << *iter << "' is converted into 'task_profiles "
+ << task_profile.value() << "' for service " << service_->name();
+ service_->task_profiles_.push_back(task_profile.value());
+ iter = args.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
service_->writepid_files_ = std::move(args);
return {};
}
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 263cb73..eed5c65 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -18,6 +18,7 @@
#include <fcntl.h>
#include <grp.h>
+#include <map>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
@@ -305,6 +306,16 @@
} else {
LOG(ERROR) << "cpuset cgroup controller is not mounted!";
}
+
+ // Issue a warning whenever writepid is being used with a cgroup. This can't be done during
+ // command parsing because cgroups might not be configured at the time or parsing.
+ for (const auto& file : *files) {
+ if (CgroupGetControllerFromPath(file, nullptr)) {
+ LOG(WARNING) << "writepid usage with cgroups path '" << file
+ << "' is obsolete, please use task_profiles!";
+ }
+ }
+
std::string pid_str = std::to_string(getpid());
for (const auto& file : *files) {
if (!WriteStringToFile(pid_str, file)) {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 9b2c7d9..6fc64df 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -95,7 +95,10 @@
LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
}
- if (!service) return pid;
+ if (!service) {
+ LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
+ return pid;
+ }
service->Reap(siginfo);
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 575b67f..86fad80 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -78,7 +78,8 @@
auto new_mount_path = new_root + mount_path;
mkdir(new_mount_path.c_str(), 0755);
if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
- PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+ PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
+ << "'" << new_mount_path << "'";
}
}
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 68c6b51..c6bf708 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -303,7 +303,7 @@
std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
- if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
+ if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
// TODO: Remove these legacy paths once Android S is no longer supported.
canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
} else {
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index e512ab7..cca7d93 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libcutils_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libcutils_test"
+ }
]
}
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 9a2305c..0575ccf 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -30,6 +30,8 @@
extern appid_t multiuser_get_app_id(uid_t uid);
extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+extern uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id);
+extern uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid);
extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index 3f5e41f..a5ffb03 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -34,24 +34,6 @@
extern int qtaguid_untagSocket(int sockfd);
/*
- * For the given uid, switch counter sets.
- * The kernel only keeps a limited number of sets.
- * 2 for now.
- */
-extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
-
-/*
- * Delete all tag info that relates to the given tag an uid.
- * If the tag is 0, then ALL info about the uid is freed.
- * The delete data also affects active tagged sockets, which are
- * then untagged.
- * The calling process can only operate on its own tags.
- * Unless it is part of the happy AID_NET_BW_ACCT group.
- * In which case it can clobber everything.
- */
-extern int qtaguid_deleteTagData(int tag, uid_t uid);
-
-/*
* Enable/disable qtaguid functionnality at a lower level.
* When pacified, the kernel will accept commands but do nothing.
*/
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 97e93f8..98ae0d4 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_THERMAL (1 << 27)
+#define ATRACE_TAG_LAST ATRACE_TAG_THERMAL
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
@@ -208,6 +209,37 @@
}
/**
+ * Trace the beginning of an asynchronous event. In addition to the name and a
+ * cookie as in ATRACE_ASYNC_BEGIN/ATRACE_ASYNC_END, a track name argument is
+ * provided, which is the name of the row where this async event should be
+ * recorded. The track name, name, and cookie used to begin an event must be
+ * used to end it.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
+ atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
+ const char* name, int32_t cookie) {
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
+ atrace_async_for_track_begin_body(track_name, name, cookie);
+ }
+}
+
+/**
+ * Trace the end of an asynchronous event.
+ * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
+ atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
+ const char* name, int32_t cookie) {
+ if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+ void atrace_async_for_track_end_body(const char*, const char*, int32_t);
+ atrace_async_for_track_end_body(track_name, name, cookie);
+ }
+}
+
+/**
* Trace an instantaneous context. name is used to identify the context.
*
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
@@ -226,17 +258,18 @@
/**
* Trace an instantaneous context. name is used to identify the context.
- * trackName is the name of the row where the event should be recorded.
+ * track_name is the name of the row where the event should be recorded.
*
* An "instant" is an event with no defined duration. Visually is displayed like a single marker
* in the timeline (rather than a span, in the case of begin/end events).
*/
#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
atrace_instant_for_track(ATRACE_TAG, trackName, name)
-static inline void atrace_instant_for_track(uint64_t tag, const char* trackName, const char* name) {
+static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
+ const char* name) {
if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
void atrace_instant_for_track_body(const char*, const char*);
- atrace_instant_for_track_body(trackName, name);
+ atrace_instant_for_track_body(track_name, name);
}
}
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 23d1415..8bb8652 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,11 @@
#define AID_UWB 1083 /* UWB subsystem */
#define AID_THREAD_NETWORK 1084 /* Thread Network subsystem */
#define AID_DICED 1085 /* Android's DICE daemon */
+#define AID_DMESGD 1086 /* dmesg parsing daemon for kernel report collection */
+#define AID_JC_WEAVER 1087 /* Javacard Weaver HAL - to manage omapi ARA rules */
+#define AID_JC_STRONGBOX 1088 /* Javacard Strongbox HAL - to manage omapi ARA rules */
+#define AID_JC_IDENTITYCRED 1089 /* Javacard Identity Cred HAL - to manage omapi ARA rules */
+#define AID_SDK_SANDBOX 1090 /* SDK sandbox virtual UID */
/* Changes to this file must be made in AOSP, *not* in internal branches. */
#define AID_SHELL 2000 /* adb and debug shell user */
@@ -209,6 +214,10 @@
*/
#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
+/* use the ranges below to determine whether a process is sdk sandbox */
+#define AID_SDK_SANDBOX_PROCESS_START 20000 /* start of uids allocated to sdk sandbox processes */
+#define AID_SDK_SANDBOX_PROCESS_END 29999 /* end of uids allocated to sdk sandbox processes */
+
/* use the ranges below to determine whether a process is isolated */
#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
index 7ef3854..56a39a6 100644
--- a/libcutils/include/private/android_projectid_config.h
+++ b/libcutils/include/private/android_projectid_config.h
@@ -49,3 +49,13 @@
#define PROJECT_ID_EXT_OBB_START 40000
/* End of project IDs for apps to mark external OBB data. */
#define PROJECT_ID_EXT_OBB_END 49999
+
+/* Start of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_START 50000
+/* End of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_END 59999
+
+/* Start of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_START 60000
+/* End of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_END 69999
diff --git a/libcutils/multiuser.cpp b/libcutils/multiuser.cpp
index 0fd3d0c..967f991 100644
--- a/libcutils/multiuser.cpp
+++ b/libcutils/multiuser.cpp
@@ -29,6 +29,25 @@
return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
+uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id) {
+ int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
+ if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET) + sdk_sandbox_offset;
+ } else {
+ return -1;
+ }
+}
+
+uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid) {
+ appid_t app_id = multiuser_get_app_id(uid);
+ int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
+ if (app_id >= AID_SDK_SANDBOX_PROCESS_START && app_id <= AID_SDK_SANDBOX_PROCESS_END) {
+ return uid - sdk_sandbox_offset;
+ } else {
+ return -1;
+ }
+}
+
gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
diff --git a/libcutils/multiuser_test.cpp b/libcutils/multiuser_test.cpp
index 4b0fd13..b57223d 100644
--- a/libcutils/multiuser_test.cpp
+++ b/libcutils/multiuser_test.cpp
@@ -18,6 +18,7 @@
#include <gtest/gtest.h>
static constexpr auto ERR_GID = static_cast<gid_t>(-1);
+static constexpr auto ERR_UID = static_cast<uid_t>(-1);
TEST(MultiuserTest, TestMerge) {
EXPECT_EQ(0U, multiuser_get_uid(0, 0));
@@ -30,6 +31,40 @@
EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
}
+TEST(MultiuserTest, TestSdkSandboxUid) {
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 0));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 1000));
+ EXPECT_EQ(20000U, multiuser_get_sdk_sandbox_uid(0, 10000));
+ EXPECT_EQ(25000U, multiuser_get_sdk_sandbox_uid(0, 15000));
+ EXPECT_EQ(29999U, multiuser_get_sdk_sandbox_uid(0, 19999));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 50000));
+
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 0));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 1000));
+ EXPECT_EQ(1020000U, multiuser_get_sdk_sandbox_uid(10, 10000));
+ EXPECT_EQ(1025000U, multiuser_get_sdk_sandbox_uid(10, 15000));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 20000));
+ EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSdkSandboxUidConvertation) {
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(0));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(10000));
+ EXPECT_EQ(10000U, multiuser_convert_sdk_sandbox_to_app_uid(20000));
+ EXPECT_EQ(15000U, multiuser_convert_sdk_sandbox_to_app_uid(25000));
+ EXPECT_EQ(19999U, multiuser_convert_sdk_sandbox_to_app_uid(29999));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(50000));
+
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1001000));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1010000));
+ EXPECT_EQ(1010000U, multiuser_convert_sdk_sandbox_to_app_uid(1020000));
+ EXPECT_EQ(1015000U, multiuser_convert_sdk_sandbox_to_app_uid(1025000));
+ EXPECT_EQ(1019999U, multiuser_convert_sdk_sandbox_to_app_uid(1029999));
+ EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1050000));
+}
+
TEST(MultiuserTest, TestSplitUser) {
EXPECT_EQ(0U, multiuser_get_user_id(0));
EXPECT_EQ(0U, multiuser_get_user_id(1000));
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index 2fe877c..a987b85 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -34,8 +34,6 @@
public:
int (*netdTagSocket)(int, uint32_t, uid_t);
int (*netdUntagSocket)(int);
- int (*netdSetCounterSet)(uint32_t, uid_t);
- int (*netdDeleteTagData)(uint32_t, uid_t);
};
int stubTagSocket(int, uint32_t, uid_t) {
@@ -46,16 +44,8 @@
return -EREMOTEIO;
}
-int stubSetCounterSet(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
-int stubDeleteTagData(uint32_t, uid_t) {
- return -EREMOTEIO;
-}
-
netdHandler initHandler(void) {
- netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
@@ -73,15 +63,6 @@
ALOGE("load netdUntagSocket handler failed: %s", dlerror());
}
- handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
- if (!handler.netdSetCounterSet) {
- ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
- }
-
- handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
- if (!handler.netdDeleteTagData) {
- ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
- }
return handler;
}
@@ -114,13 +95,3 @@
ALOGV("Untagging socket %d", sockfd);
return getHandler().netdUntagSocket(sockfd);
}
-
-int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
- ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
- return getHandler().netdSetCounterSet(counterSetNum, uid);
-}
-
-int qtaguid_deleteTagData(int tag, uid_t uid) {
- ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
- return getHandler().netdDeleteTagData(tag, uid);
-}
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index ef7c72d..8901e4a 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -131,29 +131,41 @@
// Write trace events to container trace file. Note that we need to amend tid and time information
// here comparing to normal ftrace, where those informations are added by kernel.
-#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, \
+ track_name, name, value) { \
char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+ const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
int pid = getpid(); \
int tid = gettid(); \
uint64_t ts = gettime(CLOCK_MONOTONIC); \
uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
int len = snprintf( \
buf, sizeof(buf), \
- ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
- pid, tid, ts, tts, name, value); \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%s" value_format, \
+ pid, tid, ts, tts, track_name, track_name_sep, name, value); \
if (len >= (int) sizeof(buf)) { \
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
/* Truncate the name to make the message fit. */ \
if (name_len > 0) { \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
len = snprintf( \
- buf, sizeof(buf), \
- ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
- pid, tid, ts, tts, name_len, name, value); \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%.*s" value_format, \
+ pid, tid, ts, tts, track_name, track_name_sep, name_len, name, value); \
} else { \
- /* Data is still too long. Drop it. */ \
- ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
- len = 0; \
+ int track_name_len = 0; \
+ if (track_name[0] != '\0') { \
+ track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+ } \
+ if (track_name_len <= 0){ \
+ /* Data is still too long. Drop it. */ \
+ len = 0; \
+ } else { \
+ /* Truncate the trackName and name to make the message fit. */ \
+ len = snprintf( \
+ buf, sizeof(buf), \
+ ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s|%.1s" value_format, \
+ pid, tid, ts, tts, track_name_len, track_name, name, value); \
+ } \
} \
} \
if (len > 0) { \
@@ -161,10 +173,10 @@
} \
}
-#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, track_name, name, value) { \
pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
if (atrace_container_sock_fd != -1) { \
- WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+ WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, track_name, name, value); \
} \
pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
}
@@ -172,93 +184,115 @@
void atrace_begin_body(const char* name)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+ WRITE_MSG_IN_CONTAINER("B", "|", "%s", "", name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("B|%d|", "%s", name, "");
+ WRITE_MSG("B|%d|", "%s", "", name, "");
}
void atrace_end_body()
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+ WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "", "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("E|%d", "%s", "", "");
+ WRITE_MSG("E|%d", "%s", "", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+ WRITE_MSG_IN_CONTAINER("S", "|", "|%d", "", name, cookie);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+ WRITE_MSG_IN_CONTAINER("F", "|", "|%d", "", name, cookie);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("T", "|", "|%d", track_name, name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+ if (CC_LIKELY(atrace_use_container_sock)) {
+ WRITE_MSG_IN_CONTAINER("U", "|", "|%d", track_name, name, cookie);
+ return;
+ }
+
+ if (atrace_marker_fd < 0) return;
+
+ WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
}
void atrace_instant_body(const char* name) {
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("I", "|", "%s", name, "");
+ WRITE_MSG_IN_CONTAINER("I", "|", "%s", "", name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("I|%d|", "%s", name, "");
+ WRITE_MSG("I|%d|", "%s", "", name, "");
}
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("N", "|", "|%s", trackName, name);
+ WRITE_MSG_IN_CONTAINER("N", "|", "%s", track_name, name, "");
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("N|%d|", "|%s", name, trackName);
+ WRITE_MSG("N|%d|", "%s", track_name, name, "");
}
void atrace_int_body(const char* name, int32_t value)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, "", name, value);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
if (CC_LIKELY(atrace_use_container_sock)) {
- WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+ WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, "", name, value);
return;
}
if (atrace_marker_fd < 0) return;
- WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
}
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 25c86f4..eacc8ee 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -71,38 +71,46 @@
void atrace_begin_body(const char* name)
{
- WRITE_MSG("B|%d|", "%s", name, "");
+ WRITE_MSG("B|%d|", "%s", "", name, "");
}
void atrace_end_body()
{
- WRITE_MSG("E|%d", "%s", "", "");
+ WRITE_MSG("E|%d", "%s", "", "", "");
}
void atrace_async_begin_body(const char* name, int32_t cookie)
{
- WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
}
void atrace_async_end_body(const char* name, int32_t cookie)
{
- WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+ WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+ WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+ WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
}
void atrace_instant_body(const char* name) {
- WRITE_MSG("I|%d|", "%s", name, "");
+ WRITE_MSG("I|%d|", "%s", "", name, "");
}
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
- WRITE_MSG("N|%d|", "|%s", trackName, name);
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
+ WRITE_MSG("N|%d|", "%s", track_name, name, "");
}
void atrace_int_body(const char* name, int32_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
}
void atrace_int64_body(const char* name, int64_t value)
{
- WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+ WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
}
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 3b459e0..94945ec 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -185,21 +185,36 @@
}
}
-#define WRITE_MSG(format_begin, format_end, name, value) { \
+#define WRITE_MSG(format_begin, format_end, track_name, name, value) { \
char buf[ATRACE_MESSAGE_LENGTH] __attribute__((uninitialized)); \
+ const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
int pid = getpid(); \
- int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
- name, value); \
+ int len = snprintf(buf, sizeof(buf), format_begin "%s%s%s" format_end, pid, \
+ track_name, track_name_sep, name, value); \
if (len >= (int) sizeof(buf)) { \
- /* Given the sizeof(buf), and all of the current format buffers, \
- * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
/* Truncate the name to make the message fit. */ \
- ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
- len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
- name_len, name, value); \
+ if (name_len > 0) { \
+ len = snprintf(buf, sizeof(buf), format_begin "%s%s%.*s" format_end, pid, \
+ track_name, track_name_sep, name_len, name, value); \
+ } else { \
+ int track_name_len = 0; \
+ if (track_name[0] != '\0') { \
+ track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+ } \
+ if (track_name_len <= 0) { \
+ /* Data is still too long. Drop it. */ \
+ len = 0; \
+ } else { \
+ /* Truncate the trackName and name to make the message fit */ \
+ len = snprintf(buf, sizeof(buf), format_begin "%.*s|%.1s" format_end, pid, \
+ track_name_len, track_name, name, value); \
+ } \
+ } \
} \
- write(atrace_marker_fd, buf, len); \
+ if (len > 0) { \
+ write(atrace_marker_fd, buf, len); \
+ } \
}
#endif // __TRACE_DEV_INC
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index ff6d202..841674a 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,6 +195,226 @@
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_normal) {
+ atrace_async_for_track_begin_body("fake_track", "fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("T|%d|fake_track|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {
+ const int name_size = 5;
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string track_name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += track_name + "|name|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify name truncation
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ track_name += '*';
+ expected = android::base::StringPrintf("T|%d|", getpid());
+ expected += track_name + "|nam|12345";
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+ expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("T|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("T|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+ expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+ name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
+ atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ std::string expected = android::base::StringPrintf("U|%d|fake_track|fake_name|12345", getpid());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
+ const int name_size = 5;
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string track_name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += track_name + "|name|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify name truncation
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ track_name += '*';
+ expected = android::base::StringPrintf("U|%d|", getpid());
+ expected += track_name + "|nam|12345";
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+ expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string name =
+ MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name + "|12345";
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("U|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+ expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("U|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+ expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+ name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
TEST_F(TraceDevTest, atrace_instant_body_normal) {
atrace_instant_body("fake_name");
@@ -255,43 +475,100 @@
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_instant_for_track_body_exact) {
- const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_track_name) {
+ const int name_size = 5;
std::string expected = android::base::StringPrintf("N|%d|", getpid());
- std::string trackName = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize);
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ std::string track_name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size);
+ atrace_instant_for_track_body(track_name.c_str(), "name");
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- expected += trackName + "|name";
+ expected += track_name + "|name";
ASSERT_STREQ(expected.c_str(), actual.c_str());
- // Add a single character and verify we get the exact same value as before.
+ // Add a single character and verify name truncation
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
- trackName += '*';
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ track_name += '*';
+ expected = android::base::StringPrintf("N|%d|", getpid());
+ expected += track_name + "|nam";
+ atrace_instant_for_track_body(track_name.c_str(), "name");
EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated) {
- const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_track_name) {
std::string expected = android::base::StringPrintf("N|%d|", getpid());
- std::string trackName = MakeName(2 * ATRACE_MESSAGE_LENGTH);
- atrace_instant_for_track_body(trackName.c_str(), "name");
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body(track_name.c_str(), "name");
ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
std::string actual;
ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
- int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize;
- expected += android::base::StringPrintf("%.*s|name", expected_len, trackName.c_str());
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+ expected += android::base::StringPrintf("%.*s|n", expected_len, track_name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_name) {
+ const int track_name_size = 11;
+ std::string expected = android::base::StringPrintf("N|%d|", getpid());
+ std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size);
+ atrace_instant_for_track_body("track_name", name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ expected += "track_name|" + name;
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+ // Add a single character and verify we get the same value as before.
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ name += '*';
+ atrace_instant_for_track_body("track_name", name.c_str());
+ EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_name) {
+ std::string expected = android::base::StringPrintf("N|%d|track_name|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body("track_name", name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+ expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+ ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_both) {
+ std::string expected = android::base::StringPrintf("N|%d|", getpid());
+ std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+ atrace_instant_for_track_body(track_name.c_str(), name.c_str());
+
+ ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+ ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+ std::string actual;
+ ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+ int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+ expected +=
+ android::base::StringPrintf("%.*s|%.1s", expected_len, track_name.c_str(), name.c_str());
ASSERT_STREQ(expected.c_str(), actual.c_str());
}
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index b01a0ec..c2a379b 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,9 +28,12 @@
void atrace_end_body() { }
void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
+ int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
+ int32_t /*cookie*/) {}
void atrace_instant_body(const char* /*name*/) {}
-void atrace_instant_for_track_body(const char* /*trackName*/,
- const char* /*name*/) {}
+void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
void atrace_init() {}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index a3d643e..f523d4e 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -27,12 +27,8 @@
darwin: {
enabled: false,
},
- linux_glibc: {
+ host_linux: {
cflags: [
- "-O2",
- "-g",
- "-W",
- "-Wall",
"-D_LARGEFILE64_SOURCE",
],
},
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index f31b5f1..16103d2 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -44,4 +44,9 @@
shared_libs: ["android.hardware.graphics.allocator@2.0"],
header_libs: ["libhardware_headers"],
min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media.swcodec",
+ "test_com.android.media.swcodec",
+ ],
}
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
index 526b1e4..888593e 100644
--- a/libmodprobe/TEST_MAPPING
+++ b/libmodprobe/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libmodprobe_tests"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libmodprobe_tests"
+ }
]
}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index c934860..5d79d6a 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,17 +16,21 @@
#pragma once
+#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
+#include <android-base/thread_annotations.h>
+
class Modprobe {
public:
Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
bool use_blocklist = true);
+ bool LoadModulesParallel(int num_threads);
bool LoadListedModules(bool strict = true);
bool LoadWithAliases(const std::string& module_name, bool strict,
const std::string& parameters = "");
@@ -66,7 +70,9 @@
std::vector<std::string> module_load_;
std::unordered_map<std::string, std::string> module_options_;
std::set<std::string> module_blocklist_;
+ std::mutex module_loaded_lock_;
std::unordered_set<std::string> module_loaded_;
+ std::unordered_set<std::string> module_loaded_paths_;
int module_count_ = 0;
bool blocklist_enabled = false;
};
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1a9d364..3054d2b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -21,8 +21,10 @@
#include <sys/syscall.h>
#include <algorithm>
+#include <map>
#include <set>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -437,6 +439,97 @@
return module_blocklist_.count(canonical_name) > 0;
}
+// Another option to load kernel modules. load in independent modules in parallel
+// and then load modules which only have soft dependency, third update dependency list of other
+// remaining modules, repeat these steps until all modules are loaded.
+bool Modprobe::LoadModulesParallel(int num_threads) {
+ bool ret = true;
+ std::map<std::string, std::set<std::string>> mod_with_deps;
+ std::map<std::string, std::set<std::string>> mod_with_softdeps;
+
+ // Get dependencies
+ for (const auto& module : module_load_) {
+ auto dependencies = GetDependencies(MakeCanonical(module));
+
+ for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
+ mod_with_deps[module].emplace(*dep);
+ }
+ }
+
+ // Get soft dependencies
+ for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
+ mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ }
+
+ // Get soft post dependencies
+ for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
+ mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+ }
+
+ while (!mod_with_deps.empty()) {
+ std::vector<std::thread> threads;
+ std::vector<std::string> mods_path_to_load;
+ std::vector<std::string> mods_with_softdep_to_load;
+ std::mutex vector_lock;
+
+ // Find independent modules and modules only having soft dependencies
+ for (const auto& [it_mod, it_dep] : mod_with_deps) {
+ if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
+ mods_path_to_load.emplace_back(*(it_dep.begin()));
+ } else if (it_dep.size() == 1) {
+ mods_with_softdep_to_load.emplace_back(it_mod);
+ }
+ }
+
+ // Load independent modules in parallel
+ auto thread_function = [&] {
+ std::unique_lock lk(vector_lock);
+ while (!mods_path_to_load.empty()) {
+ auto mod_path_to_load = std::move(mods_path_to_load.back());
+ mods_path_to_load.pop_back();
+
+ lk.unlock();
+ ret &= Insmod(mod_path_to_load, "");
+ lk.lock();
+ }
+ };
+
+ std::generate_n(std::back_inserter(threads), num_threads,
+ [&] { return std::thread(thread_function); });
+
+ // Wait for the threads.
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ // Since we cannot assure if these soft dependencies tree are overlap,
+ // we loaded these modules one by one.
+ for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
+ dep++) {
+ ret &= LoadWithAliases(*dep, true);
+ }
+
+ std::lock_guard guard(module_loaded_lock_);
+ // Remove loaded module form mod_with_deps and soft dependencies of other modules
+ for (const auto& module_loaded : module_loaded_) {
+ mod_with_deps.erase(module_loaded);
+
+ for (auto& [mod, softdeps] : mod_with_softdeps) {
+ softdeps.erase(module_loaded);
+ }
+ }
+
+ // Remove loaded module form dependencies of other modules which are not loaded yet
+ for (const auto& module_loaded_path : module_loaded_paths_) {
+ for (auto& [mod, deps] : mod_with_deps) {
+ deps.erase(module_loaded_path);
+ }
+ }
+ }
+
+ return ret;
+}
+
bool Modprobe::LoadListedModules(bool strict) {
auto ret = true;
for (const auto& module : module_load_) {
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index fb1f5e7..94a1dc4 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -54,6 +54,8 @@
if (ret != 0) {
if (errno == EEXIST) {
// Module already loaded
+ std::lock_guard guard(module_loaded_lock_);
+ module_loaded_paths_.emplace(path_name);
module_loaded_.emplace(canonical_name);
return true;
}
@@ -62,6 +64,8 @@
}
LOG(INFO) << "Loaded kernel module " << path_name;
+ std::lock_guard guard(module_loaded_lock_);
+ module_loaded_paths_.emplace(path_name);
module_loaded_.emplace(canonical_name);
module_count_++;
return true;
@@ -74,6 +78,7 @@
PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
return false;
}
+ std::lock_guard guard(module_loaded_lock_);
module_loaded_.erase(canonical_name);
return true;
}
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index f960b61..f92a2b6 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -188,10 +188,11 @@
EXPECT_TRUE(modules_loaded == expected_after_remove);
- m = Modprobe({dir.path});
- EXPECT_FALSE(m.LoadWithAliases("test4", true));
- while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
- EXPECT_TRUE(m.LoadListedModules());
+ Modprobe m2({dir.path});
+
+ EXPECT_FALSE(m2.LoadWithAliases("test4", true));
+ while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front()));
+ EXPECT_TRUE(m2.LoadListedModules());
GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
for (auto i = expected_modules_blocklist_enabled.begin();
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index 51773f9..d69a7fb 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "libpackagelistparser_test"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "libpackagelistparser_test"
+ }
]
}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c68552d..7b0e0d3 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -73,3 +73,29 @@
],
min_sdk_version: "29",
}
+
+cc_test {
+ name: "task_profiles_test",
+ host_supported: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wexit-time-destructors",
+ "-Wno-unused-parameter",
+ ],
+ srcs: [
+ "task_profiles_test.cpp",
+ ],
+ header_libs: [
+ "libcutils_headers",
+ "libprocessgroup_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libprocessgroup",
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 352847a..8c00326 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -231,7 +231,8 @@
const ACgroupController* controller = ACgroupFile_getController(i);
if (ACgroupController_getFlags(controller) &
CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
- std::string str = std::string("+") + ACgroupController_getName(controller);
+ std::string str("+");
+ str.append(ACgroupController_getName(controller));
if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
return -errno;
}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cb2fe0a..54772b6 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -69,6 +69,23 @@
return true;
}
+static bool CgroupGetMemcgAppsPath(std::string* path) {
+ CgroupController controller = CgroupMap::GetInstance().FindController("memory");
+
+ if (!controller.HasValue()) {
+ return false;
+ }
+
+ if (path) {
+ *path = controller.path();
+ if (controller.version() == 1) {
+ *path += "/apps";
+ }
+ }
+
+ return true;
+}
+
bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name) {
auto controller = CgroupMap::GetInstance().FindControllerByPath(path);
@@ -85,7 +102,7 @@
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -100,7 +117,7 @@
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
const TaskProfiles& tp = TaskProfiles::GetInstance();
- const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+ const IProfileAttribute* attr = tp.GetAttribute(attr_name);
if (attr == nullptr) {
return false;
@@ -199,19 +216,19 @@
LOG(VERBOSE) << "removeAllProcessGroups()";
std::vector<std::string> cgroups;
- std::string path;
+ std::string path, memcg_apps_path;
if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
cgroups.push_back(path);
}
- if (CgroupGetControllerPath("memory", &path)) {
- cgroups.push_back(path + "/apps");
+ if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
+ cgroups.push_back(memcg_apps_path);
}
for (std::string cgroup_root_path : cgroups) {
std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
- PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+ PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
@@ -297,7 +314,8 @@
// This happens when process is already dead
return 0;
}
- PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+ PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+ << initialPid;
return -1;
}
@@ -410,10 +428,11 @@
int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- std::string memory_path;
- CgroupGetControllerPath("memory", &memory_path);
- memory_path += "/apps";
- if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
+ std::string memcg_apps_path;
+ if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
+ RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+ return -1;
+ }
}
return err;
@@ -441,11 +460,11 @@
struct stat cgroup_stat;
mode_t cgroup_mode = 0750;
- gid_t cgroup_uid = AID_SYSTEM;
- uid_t cgroup_gid = AID_SYSTEM;
+ uid_t cgroup_uid = AID_SYSTEM;
+ gid_t cgroup_gid = AID_SYSTEM;
int ret = 0;
- if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
+ if (stat(cgroup.c_str(), &cgroup_stat) < 0) {
PLOG(ERROR) << "Failed to get stats for " << cgroup;
} else {
cgroup_mode = cgroup_stat.st_mode;
@@ -490,10 +509,12 @@
return -EINVAL;
}
- if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
- CgroupGetControllerPath("memory", &cgroup);
- cgroup += "/apps";
- int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
+ if (std::string memcg_apps_path;
+ isMemoryCgroupSupported() && UsePerAppMemcg() && CgroupGetMemcgAppsPath(&memcg_apps_path)) {
+ // Note by bvanassche: passing 'false' as fourth argument below implies that the v1
+ // hierarchy is used. It is not clear to me whether the above conditions guarantee that the
+ // v1 hierarchy is used.
+ int ret = createProcessGroupInternal(uid, initialPid, memcg_apps_path, false);
if (ret != 0) {
return ret;
}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0634220..3e4393d 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -3,7 +3,7 @@
{
"Controller": "blkio",
"Path": "/dev/blkio",
- "Mode": "0755",
+ "Mode": "0775",
"UID": "system",
"GID": "system"
},
@@ -32,16 +32,13 @@
],
"Cgroups2": {
"Path": "/sys/fs/cgroup",
- "Mode": "0755",
+ "Mode": "0775",
"UID": "system",
"GID": "system",
"Controllers": [
{
"Controller": "freezer",
- "Path": ".",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
+ "Path": "."
}
]
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b668dcb..7e03964 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -16,14 +16,21 @@
"File": "top-app/cpus"
},
{
+ "Name": "MemStats",
+ "Controller": "memory",
+ "File": "memory.stat"
+ },
+ {
"Name": "MemLimit",
"Controller": "memory",
- "File": "memory.limit_in_bytes"
+ "File": "memory.limit_in_bytes",
+ "FileV2": "memory.max"
},
{
"Name": "MemSoftLimit",
"Controller": "memory",
- "File": "memory.soft_limit_in_bytes"
+ "File": "memory.soft_limit_in_bytes",
+ "FileV2": "memory.low"
},
{
"Name": "MemSwappiness",
@@ -31,6 +38,26 @@
"File": "memory.swappiness"
},
{
+ "Name": "MemUsage",
+ "Controller": "memory",
+ "File": "memory.usage_in_bytes"
+ },
+ {
+ "Name": "MemAndSwapUsage",
+ "Controller": "memory",
+ "File": "memory.memsw.usage_in_bytes"
+ },
+ {
+ "Name": "MemPressureLevel",
+ "Controller": "memory",
+ "File": "memory.pressure_level"
+ },
+ {
+ "Name": "MemCgroupEventControl",
+ "Controller": "memory",
+ "File": "cgroup.event_control"
+ },
+ {
"Name": "UClampMin",
"Controller": "cpu",
"File": "cpu.uclamp.min"
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 1de4395..ebcd9b5 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -25,11 +25,13 @@
repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
}
-// Next: 4
+// Next: 6
message Attribute {
string name = 1 [json_name = "Name"];
string controller = 2 [json_name = "Controller"];
string file = 3 [json_name = "File"];
+ string filev2 = 4 [json_name = "FileV2"];
+ string optional = 5 [json_name = "Optional"];
}
// Next: 3
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3121d24..3831ef2 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -147,12 +147,17 @@
static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* descriptors,
const Json::Value& cgroup, const std::string& name,
const std::string& root_path, int cgroups_version) {
+ const std::string cgroup_path = cgroup["Path"].asString();
std::string path;
if (!root_path.empty()) {
- path = root_path + "/" + cgroup["Path"].asString();
+ path = root_path;
+ if (cgroup_path != ".") {
+ path += "/";
+ path += cgroup_path;
+ }
} else {
- path = cgroup["Path"].asString();
+ path = cgroup_path;
}
uint32_t controller_flags = 0;
@@ -263,8 +268,18 @@
return false;
}
- result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- nullptr);
+ // The memory_recursiveprot mount option has been introduced by kernel commit
+ // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
+ // mount with that option enabled. If mounting fails because the kernel is too old,
+ // retry without that mount option.
+ if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ "memory_recursiveprot") < 0) {
+ LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
+ if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr) < 0) {
+ PLOG(ERROR) << "Failed to mount cgroup v2";
+ }
+ }
// selinux permissions change after mounting, so it's ok to change mode and owner now
if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 74ba7f6..e1c5934 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -112,6 +112,8 @@
return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
}
+IProfileAttribute::~IProfileAttribute() = default;
+
void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
controller_ = controller;
file_name_ = file_name;
@@ -127,11 +129,12 @@
return true;
}
+ const std::string& file_name =
+ controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
- file_name_.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
}
return true;
}
@@ -183,6 +186,12 @@
return true;
}
+#else
+
+bool SetTimerSlackAction::ExecuteForTask(int) const {
+ return true;
+};
+
#endif
bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -198,6 +207,17 @@
}
if (!WriteStringToFile(value_, path)) {
+ if (access(path.c_str(), F_OK) < 0) {
+ if (optional_) {
+ return true;
+ } else {
+ LOG(ERROR) << "No such cgroup attribute: " << path;
+ return false;
+ }
+ }
+ // The PLOG() statement below uses the error code stored in `errno` by
+ // WriteStringToFile() because access() only overwrites `errno` if it fails
+ // and because this code is only reached if the access() function returns 0.
PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
return false;
}
@@ -342,14 +362,33 @@
FdCacheHelper::Drop(fd_[cache_type]);
}
-WriteFileAction::WriteFileAction(const std::string& path, const std::string& value,
- bool logfailures)
- : path_(path), value_(value), logfailures_(logfailures) {
- FdCacheHelper::Init(path_, fd_);
+WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
+ const std::string& value, bool logfailures)
+ : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
+ FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);
+ if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);
}
-bool WriteFileAction::WriteValueToFile(const std::string& value, const std::string& path,
- bool logfailures) {
+bool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,
+ int uid, int pid, bool logfailures) const {
+ std::string value(value_);
+
+ value = StringReplace(value, "<uid>", std::to_string(uid), true);
+ value = StringReplace(value, "<pid>", std::to_string(pid), true);
+
+ CacheUseResult result = UseCachedFd(cache_type, value);
+
+ if (result != ProfileAction::UNUSED) {
+ return result == ProfileAction::SUCCESS;
+ }
+
+ std::string path;
+ if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
+ path = task_path_;
+ } else {
+ path = proc_path_;
+ }
+
// Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
// O_TRUNC which causes kernfs_mutex contention
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
@@ -370,21 +409,27 @@
ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
const std::string& value) const {
std::lock_guard<std::mutex> lock(fd_mutex_);
- if (FdCacheHelper::IsCached(fd_)) {
+ if (FdCacheHelper::IsCached(fd_[cache_type])) {
// fd is cached, reuse it
- if (!WriteStringToFd(value, fd_)) {
- if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << path_;
- return ProfileAction::FAIL;
+ bool ret = WriteStringToFd(value, fd_[cache_type]);
+
+ if (!ret && logfailures_) {
+ if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << task_path_;
+ } else {
+ PLOG(ERROR) << "Failed to write '" << value << "' to " << proc_path_;
+ }
}
- return ProfileAction::SUCCESS;
+ return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;
}
- if (fd_ == FdCacheHelper::FDS_INACCESSIBLE) {
+ if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
// no permissions to access the file, ignore
return ProfileAction::SUCCESS;
}
- if (cache_type == ResourceCacheType::RCT_TASK && fd_ == FdCacheHelper::FDS_APP_DEPENDENT) {
+ if (cache_type == ResourceCacheType::RCT_TASK &&
+ fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
// application-dependent path can't be used with tid
PLOG(ERROR) << "Application profile can't be applied to a thread";
return ProfileAction::FAIL;
@@ -393,46 +438,64 @@
}
bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
- std::string value(value_);
-
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(pid), true);
-
- CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, value);
- if (result != ProfileAction::UNUSED) {
- return result == ProfileAction::SUCCESS;
+ if (!proc_path_.empty()) {
+ return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);
}
- std::string path(path_);
- path = StringReplace(path, "<uid>", std::to_string(uid), true);
- path = StringReplace(path, "<pid>", std::to_string(pid), true);
+ DIR* d;
+ struct dirent* de;
+ char proc_path[255];
+ int t_pid;
- return WriteValueToFile(value, path, logfailures_);
+ sprintf(proc_path, "/proc/%d/task", pid);
+ if (!(d = opendir(proc_path))) {
+ return false;
+ }
+
+ while ((de = readdir(d))) {
+ if (de->d_name[0] == '.') {
+ continue;
+ }
+
+ t_pid = atoi(de->d_name);
+
+ if (!t_pid) {
+ continue;
+ }
+
+ WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);
+ }
+
+ closedir(d);
+
+ return true;
}
bool WriteFileAction::ExecuteForTask(int tid) const {
- std::string value(value_);
- int uid = getuid();
+ return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);
+}
- value = StringReplace(value, "<uid>", std::to_string(uid), true);
- value = StringReplace(value, "<pid>", std::to_string(tid), true);
-
- CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, value);
- if (result != ProfileAction::UNUSED) {
- return result == ProfileAction::SUCCESS;
+void WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {
+ std::lock_guard<std::mutex> lock(fd_mutex_);
+ if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
+ return;
}
-
- return WriteValueToFile(value, path_, logfailures_);
+ switch (cache_type) {
+ case (ProfileAction::RCT_TASK):
+ FdCacheHelper::Cache(task_path_, fd_[cache_type]);
+ break;
+ case (ProfileAction::RCT_PROCESS):
+ if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);
+ break;
+ default:
+ LOG(ERROR) << "Invalid cache type is specified!";
+ break;
+ }
}
-void WriteFileAction::EnableResourceCaching(ResourceCacheType) {
+void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
std::lock_guard<std::mutex> lock(fd_mutex_);
- FdCacheHelper::Cache(path_, fd_);
-}
-
-void WriteFileAction::DropResourceCaching(ResourceCacheType) {
- std::lock_guard<std::mutex> lock(fd_mutex_);
- FdCacheHelper::Drop(fd_);
+ FdCacheHelper::Drop(fd_[cache_type]);
}
bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
@@ -469,6 +532,7 @@
bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& element : elements_) {
if (!element->ExecuteForProcess(uid, pid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
@@ -481,6 +545,7 @@
}
for (const auto& element : elements_) {
if (!element->ExecuteForTask(tid)) {
+ LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
return false;
}
}
@@ -572,12 +637,19 @@
std::string name = attr[i]["Name"].asString();
std::string controller_name = attr[i]["Controller"].asString();
std::string file_attr = attr[i]["File"].asString();
+ std::string file_v2_attr = attr[i]["FileV2"].asString();
+
+ if (!file_v2_attr.empty() && file_attr.empty()) {
+ LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
+ return false;
+ }
auto controller = cg_map.FindController(controller_name);
if (controller.HasValue()) {
auto iter = attributes_.find(name);
if (iter == attributes_.end()) {
- attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
+ attributes_[name] =
+ std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
} else {
iter->second->Reset(controller, file_attr);
}
@@ -592,7 +664,7 @@
std::string profile_name = profile_val["Name"].asString();
const Json::Value& actions = profile_val["Actions"];
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(profile_name);
for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
const Json::Value& action_val = actions[act_idx];
@@ -622,11 +694,12 @@
} else if (action_name == "SetAttribute") {
std::string attr_name = params_val["Name"].asString();
std::string attr_value = params_val["Value"].asString();
+ bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
auto iter = attributes_.find(attr_name);
if (iter != attributes_.end()) {
- profile->Add(
- std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
+ profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
+ attr_value, optional));
} else {
LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
}
@@ -649,12 +722,14 @@
}
} else if (action_name == "WriteFile") {
std::string attr_filepath = params_val["FilePath"].asString();
+ std::string attr_procfilepath = params_val["ProcFilePath"].asString();
std::string attr_value = params_val["Value"].asString();
+ // FilePath and Value are mandatory
if (!attr_filepath.empty() && !attr_value.empty()) {
std::string attr_logfailures = params_val["LogFailures"].asString();
bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
- profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
- logfailures));
+ profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,
+ attr_value, logfailures));
} else if (attr_filepath.empty()) {
LOG(WARNING) << "WriteFile: invalid parameter: "
<< "empty filepath";
@@ -702,7 +777,7 @@
}
}
if (ret) {
- auto profile = std::make_shared<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
profile->Add(std::make_unique<ApplyProfileAction>(profiles));
profiles_[aggregateprofile_name] = profile;
}
@@ -720,7 +795,7 @@
return nullptr;
}
-const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
auto iter = attributes_.find(name);
if (iter != attributes_.end()) {
@@ -731,6 +806,7 @@
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
const std::vector<std::string>& profiles, bool use_fd_cache) {
+ bool success = true;
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
@@ -739,16 +815,19 @@
}
if (!profile->ExecuteForProcess(uid, pid)) {
PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << "process profile";
+ PLOG(WARNING) << "Failed to find " << name << " process profile";
+ success = false;
}
}
- return true;
+ return success;
}
bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
bool use_fd_cache) {
+ bool success = true;
for (const auto& name : profiles) {
TaskProfile* profile = GetProfile(name);
if (profile != nullptr) {
@@ -757,10 +836,12 @@
}
if (!profile->ExecuteForTask(tid)) {
PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ success = false;
}
} else {
- PLOG(WARNING) << "Failed to find " << name << "task profile";
+ PLOG(WARNING) << "Failed to find " << name << " task profile";
+ success = false;
}
}
- return true;
+ return success;
}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 1aaa196..df08f65 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,20 +26,35 @@
#include <android-base/unique_fd.h>
#include <cgroup_map.h>
-class ProfileAttribute {
+class IProfileAttribute {
public:
- ProfileAttribute(const CgroupController& controller, const std::string& file_name)
- : controller_(controller), file_name_(file_name) {}
+ virtual ~IProfileAttribute() = 0;
+ virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+ virtual const CgroupController* controller() const = 0;
+ virtual const std::string& file_name() const = 0;
+ virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+};
- const CgroupController* controller() const { return &controller_; }
- const std::string& file_name() const { return file_name_; }
- void Reset(const CgroupController& controller, const std::string& file_name);
+class ProfileAttribute : public IProfileAttribute {
+ public:
+ // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is
+ // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for
+ // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.
+ ProfileAttribute(const CgroupController& controller, const std::string& file_name,
+ const std::string& file_v2_name)
+ : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}
+ ~ProfileAttribute() = default;
- bool GetPathForTask(int tid, std::string* path) const;
+ const CgroupController* controller() const override { return &controller_; }
+ const std::string& file_name() const override { return file_name_; }
+ void Reset(const CgroupController& controller, const std::string& file_name) override;
+
+ bool GetPathForTask(int tid, std::string* path) const override;
private:
CgroupController controller_;
std::string file_name_;
+ std::string file_v2_name_;
};
// Abstract profile element
@@ -49,6 +64,8 @@
virtual ~ProfileAction() {}
+ virtual const char* Name() const = 0;
+
// Default implementations will fail
virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
virtual bool ExecuteForTask(int) const { return false; };
@@ -65,22 +82,21 @@
public:
SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetClamps"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
protected:
int boost_;
int clamp_;
};
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
class SetTimerSlackAction : public ProfileAction {
public:
SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetTimerSlack"; }
+ bool ExecuteForTask(int tid) const override;
private:
unsigned long slack_;
@@ -88,29 +104,20 @@
static bool IsTimerSlackSupported(int tid);
};
-#else
-
-class SetTimerSlackAction : public ProfileAction {
- public:
- SetTimerSlackAction(unsigned long) noexcept {}
-
- virtual bool ExecuteForTask(int) const { return true; }
-};
-
-#endif
-
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
- : attribute_(attribute), value_(value) {}
+ SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional)
+ : attribute_(attribute), value_(value), optional_(optional) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
+ const char* Name() const override { return "SetAttribute"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
private:
- const ProfileAttribute* attribute_;
+ const IProfileAttribute* attribute_;
std::string value_;
+ bool optional_;
};
// Set cgroup profile element
@@ -118,10 +125,11 @@
public:
SetCgroupAction(const CgroupController& c, const std::string& p);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ResourceCacheType cache_type);
- virtual void DropResourceCaching(ResourceCacheType cache_type);
+ const char* Name() const override { return "SetCgroup"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
const CgroupController* controller() const { return &controller_; }
@@ -138,28 +146,31 @@
// Write to file action
class WriteFileAction : public ProfileAction {
public:
- WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
+ WriteFileAction(const std::string& task_path, const std::string& proc_path,
+ const std::string& value, bool logfailures);
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ResourceCacheType cache_type);
- virtual void DropResourceCaching(ResourceCacheType cache_type);
+ const char* Name() const override { return "WriteFile"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ResourceCacheType cache_type) override;
+ void DropResourceCaching(ResourceCacheType cache_type) override;
private:
- std::string path_, value_;
+ std::string task_path_, proc_path_, value_;
bool logfailures_;
- android::base::unique_fd fd_;
+ android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
mutable std::mutex fd_mutex_;
- static bool WriteValueToFile(const std::string& value, const std::string& path,
- bool logfailures);
+ bool WriteValueToFile(const std::string& value, ResourceCacheType cache_type, int uid, int pid,
+ bool logfailures) const;
CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
};
class TaskProfile {
public:
- TaskProfile() : res_cached_(false) {}
+ TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}
+ const std::string& Name() const { return name_; }
void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
void MoveTo(TaskProfile* profile);
@@ -169,6 +180,7 @@
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
private:
+ const std::string name_;
bool res_cached_;
std::vector<std::unique_ptr<ProfileAction>> elements_;
};
@@ -179,10 +191,11 @@
ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
: profiles_(profiles) {}
- virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
- virtual bool ExecuteForTask(int tid) const;
- virtual void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
- virtual void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+ const char* Name() const override { return "ApplyProfileAction"; }
+ bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+ bool ExecuteForTask(int tid) const override;
+ void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+ void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
private:
std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -194,7 +207,7 @@
static TaskProfiles& GetInstance();
TaskProfile* GetProfile(const std::string& name) const;
- const ProfileAttribute* GetAttribute(const std::string& name) const;
+ const IProfileAttribute* GetAttribute(const std::string& name) const;
void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
bool use_fd_cache);
@@ -202,7 +215,7 @@
private:
std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
- std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+ std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
TaskProfiles();
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
new file mode 100644
index 0000000..09ac44c
--- /dev/null
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2022 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 "task_profiles.h"
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <mntent.h>
+#include <processgroup/processgroup.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+
+using ::android::base::ERROR;
+using ::android::base::LogFunction;
+using ::android::base::LogId;
+using ::android::base::LogSeverity;
+using ::android::base::SetLogger;
+using ::android::base::VERBOSE;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+namespace {
+
+bool IsCgroupV2Mounted() {
+ std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
+ if (!mnts) {
+ LOG(ERROR) << "Failed to open /proc/mounts";
+ return false;
+ }
+ struct mntent* mnt;
+ while ((mnt = getmntent(mnts.get()))) {
+ if (strcmp(mnt->mnt_fsname, "cgroup2") == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+class ScopedLogCapturer {
+ public:
+ struct log_args {
+ LogId log_buffer_id;
+ LogSeverity severity;
+ std::string tag;
+ std::string file;
+ unsigned int line;
+ std::string message;
+ };
+
+ // Constructor. Installs a new logger and saves the currently active logger.
+ ScopedLogCapturer() {
+ saved_severity_ = SetMinimumLogSeverity(android::base::VERBOSE);
+ saved_logger_ = SetLogger([this](LogId log_buffer_id, LogSeverity severity, const char* tag,
+ const char* file, unsigned int line, const char* message) {
+ if (saved_logger_) {
+ saved_logger_(log_buffer_id, severity, tag, file, line, message);
+ }
+ log_.emplace_back(log_args{.log_buffer_id = log_buffer_id,
+ .severity = severity,
+ .tag = tag,
+ .file = file,
+ .line = line,
+ .message = message});
+ });
+ }
+ // Destructor. Restores the original logger and log level.
+ ~ScopedLogCapturer() {
+ SetLogger(std::move(saved_logger_));
+ SetMinimumLogSeverity(saved_severity_);
+ }
+ ScopedLogCapturer(const ScopedLogCapturer&) = delete;
+ ScopedLogCapturer& operator=(const ScopedLogCapturer&) = delete;
+ // Returns the logged lines.
+ const std::vector<log_args>& Log() const { return log_; }
+
+ private:
+ LogSeverity saved_severity_;
+ LogFunction saved_logger_;
+ std::vector<log_args> log_;
+};
+
+// cgroup attribute at the top level of the cgroup hierarchy.
+class ProfileAttributeMock : public IProfileAttribute {
+ public:
+ ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
+ ~ProfileAttributeMock() override = default;
+ void Reset(const CgroupController& controller, const std::string& file_name) override {
+ CHECK(false);
+ }
+ const CgroupController* controller() const override {
+ CHECK(false);
+ return {};
+ }
+ const std::string& file_name() const override { return file_name_; }
+ bool GetPathForTask(int tid, std::string* path) const override {
+#ifdef __ANDROID__
+ CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+ CHECK_GT(path->length(), 0);
+ if (path->rbegin()[0] != '/') {
+ *path += "/";
+ }
+#else
+ // Not Android.
+ *path = "/sys/fs/cgroup/";
+#endif
+ *path += file_name_;
+ return true;
+ };
+
+ private:
+ const std::string file_name_;
+};
+
+struct TestParam {
+ const char* attr_name;
+ const char* attr_value;
+ bool optional_attr;
+ bool result;
+ LogSeverity log_severity;
+ const char* log_prefix;
+ const char* log_suffix;
+};
+
+class SetAttributeFixture : public TestWithParam<TestParam> {
+ public:
+ ~SetAttributeFixture() = default;
+};
+
+TEST_P(SetAttributeFixture, SetAttribute) {
+ // Treehugger runs host tests inside a container without cgroupv2 support.
+ if (!IsCgroupV2Mounted()) {
+ GTEST_SKIP();
+ return;
+ }
+ const TestParam params = GetParam();
+ ScopedLogCapturer captured_log;
+ ProfileAttributeMock pa(params.attr_name);
+ SetAttributeAction a(&pa, params.attr_value, params.optional_attr);
+ EXPECT_EQ(a.ExecuteForProcess(getuid(), getpid()), params.result);
+ auto log = captured_log.Log();
+ if (params.log_prefix || params.log_suffix) {
+ ASSERT_EQ(log.size(), 1);
+ EXPECT_EQ(log[0].severity, params.log_severity);
+ if (params.log_prefix) {
+ EXPECT_EQ(log[0].message.find(params.log_prefix), 0);
+ }
+ if (params.log_suffix) {
+ EXPECT_NE(log[0].message.find(params.log_suffix), std::string::npos);
+ }
+ } else {
+ ASSERT_EQ(log.size(), 0);
+ }
+}
+
+// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
+// exists }.
+INSTANTIATE_TEST_SUITE_P(
+ SetAttributeTestSuite, SetAttributeFixture,
+ Values(
+ // Test that attempting to write into a non-existing cgroup attribute fails and also
+ // that an error message is logged.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "No such cgroup attribute"},
+ // Test that attempting to write into an optional non-existing cgroup attribute
+ // results in the return value 'true' and also that no messages are logged.
+ TestParam{.attr_name = "no-such-attribute",
+ .attr_value = ".",
+ .optional_attr = true,
+ .result = true},
+ // Test that attempting to write an invalid value into an existing optional cgroup
+ // attribute fails and also that it causes an error
+ // message to be logged.
+ TestParam{.attr_name = "cgroup.procs",
+ .attr_value = "-1",
+ .optional_attr = true,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "Failed to write",
+ .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"},
+ // Test that attempting to write into an existing optional read-only cgroup
+ // attribute fails and also that it causes an error message to be logged.
+ TestParam{
+ .attr_name = "cgroup.controllers",
+ .attr_value = ".",
+ .optional_attr = false,
+ .result = false,
+ .log_severity = ERROR,
+ .log_prefix = "Failed to write",
+ .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
+
+} // namespace
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 89b8c2a..92f1e6a 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "libstatspull_lazy_test"
}
+ ],
+ "hwasan-postsubmit" : [
+ {
+ "name" : "libstatspull_lazy_test"
+ }
]
}
\ No newline at end of file
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index 13afc00..b182660 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name" : "libstatssocket_lazy_test"
}
+ ],
+ "hwasan-postsubmit" : [
+ {
+ "name" : "libstatssocket_lazy_test"
+ }
]
}
\ No newline at end of file
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index 1b6060a..a3c23b2 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -59,12 +59,14 @@
/*
* Structure for describing YCbCr formats for consumption by applications.
- * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*.
*
* Buffer chroma subsampling is defined in the format.
* e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
*
- * Buffers must have a 8 bit depth.
+ * Buffers must have a byte aligned channel depth or a byte aligned packed
+ * channel depth (e.g. 10 bits packed into 16 bits for
+ * HAL_PIXEL_FORMAT_YCbCr_P010).
*
* y, cb, and cr point to the first byte of their respective planes.
*
@@ -75,8 +77,8 @@
* cstride is the stride of the chroma planes.
*
* chroma_step is the distance in bytes from one chroma pixel value to the
- * next. This is 2 bytes for semiplanar (because chroma values are interleaved
- * and each chroma value is one byte) and 1 for planar.
+ * next. This is `2 * channel depth` bytes for semiplanar (because chroma
+ * values are interleaved) and `1 * channel depth` bytes for planar.
*/
struct android_ycbcr {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 234638b..1b29285 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -48,13 +48,11 @@
header_libs: ["libbacktrace_headers"],
export_header_lib_headers: ["libbacktrace_headers"],
},
- linux_glibc: {
+ host_linux: {
header_libs: ["libbacktrace_headers"],
export_header_lib_headers: ["libbacktrace_headers"],
},
linux_bionic: {
- header_libs: ["libbacktrace_headers"],
- export_header_lib_headers: ["libbacktrace_headers"],
enabled: true,
},
windows: {
@@ -100,7 +98,6 @@
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libprocessgroup",
"libvndksupport",
],
@@ -131,6 +128,9 @@
enabled: true,
},
},
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
}
cc_library {
@@ -188,6 +188,7 @@
defaults: ["libutils_defaults"],
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
+ min_sdk_version: "29",
srcs: [
"CallStack.cpp",
diff --git a/libutils/Errors_test.cpp b/libutils/Errors_test.cpp
index 873c994..0d13bb0 100644
--- a/libutils/Errors_test.cpp
+++ b/libutils/Errors_test.cpp
@@ -108,3 +108,65 @@
status_t b = g(false);
EXPECT_EQ(PERMISSION_DENIED, b);
}
+
+TEST(errors, conversion_promotion) {
+ constexpr size_t successVal = 10ull;
+ auto f = [&](bool success) -> Result<size_t, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+TEST(errors, conversion_promotion_bool) {
+ constexpr size_t successVal = true;
+ auto f = [&](bool success) -> Result<bool, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+TEST(errors, conversion_promotion_char) {
+ constexpr char successVal = 'a';
+ auto f = [&](bool success) -> Result<unsigned char, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value(), successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
+
+struct IntContainer {
+ // Implicit conversion from int is desired
+ IntContainer(int val) : val_(val) {}
+ int val_;
+};
+
+TEST(errors, conversion_construct) {
+ constexpr int successVal = 10;
+ auto f = [&](bool success) -> Result<IntContainer, StatusT> {
+ OR_RETURN(success_or_fail(success));
+ return successVal;
+ };
+ auto s = f(true);
+ ASSERT_TRUE(s.ok());
+ EXPECT_EQ(s.value().val_, successVal);
+ auto r = f(false);
+ EXPECT_TRUE(!r.ok());
+ EXPECT_EQ(PERMISSION_DENIED, r.error().code());
+}
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
index c4d917b..8b16947 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -298,8 +298,8 @@
}
TEST_F(LruCacheTest, Callback) {
- LruCache<SimpleKey, StringValue> cache(100);
EntryRemovedCallback callback;
+ LruCache<SimpleKey, StringValue> cache(100);
cache.setOnEntryRemovedListener(&callback);
cache.put(1, "one");
@@ -313,8 +313,8 @@
}
TEST_F(LruCacheTest, CallbackOnClear) {
- LruCache<SimpleKey, StringValue> cache(100);
EntryRemovedCallback callback;
+ LruCache<SimpleKey, StringValue> cache(100);
cache.setOnEntryRemovedListener(&callback);
cache.put(1, "one");
@@ -326,8 +326,8 @@
}
TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
- LruCache<KeyWithPointer, StringValue> cache(1);
InvalidateKeyCallback callback;
+ LruCache<KeyWithPointer, StringValue> cache(1);
cache.setOnEntryRemovedListener(&callback);
KeyWithPointer key1;
key1.ptr = new int(1);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 0518927..4ddac3d 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -50,12 +50,7 @@
// log all reference counting operations
#define PRINT_REFS 0
-// Continue after logging a stack trace if ~RefBase discovers that reference
-// count has never been incremented. Normally we conspicuously crash in that
-// case.
-#define DEBUG_REFBASE_DESTRUCTION 1
-
-#if !defined(_WIN32) && !defined(__APPLE__)
+#if defined(__linux__)
// CallStack is only supported on linux type platforms.
#define CALLSTACK_ENABLED 1
#else
@@ -751,17 +746,19 @@
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
// We never acquired a strong reference on this object.
-#if DEBUG_REFBASE_DESTRUCTION
- // Treating this as fatal is prone to causing boot loops. For debugging, it's
- // better to treat as non-fatal.
- ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+
+ // TODO: make this fatal, but too much code in Android manages RefBase with
+ // new/delete manually (or using other mechanisms such as std::make_unique).
+ // However, this is dangerous because it's also common for code to use the
+ // sp<T>(T*) constructor, assuming that if the object is around, it is already
+ // owned by an sp<>.
+ ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+ "object.",
+ mRefs->mWeak.load(), this);
#if CALLSTACK_ENABLED
CallStack::logStack(LOG_TAG);
#endif
-#else
- LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
-#endif
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 419b2de..3690389 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -313,7 +313,7 @@
if (n > 0) {
size_t oldLength = length();
- if (n > std::numeric_limits<size_t>::max() - 1 ||
+ if (static_cast<size_t>(n) > std::numeric_limits<size_t>::max() - 1 ||
oldLength > std::numeric_limits<size_t>::max() - n - 1) {
return NO_MEMORY;
}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 3bf5779..4dacdc6 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -84,14 +84,6 @@
delete t;
setpriority(PRIO_PROCESS, 0, prio);
- // A new thread will be in its parent's sched group by default,
- // so we just need to handle the background case.
- // currently set to system_background group which is different
- // from background group for app.
- if (prio >= ANDROID_PRIORITY_BACKGROUND) {
- SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true);
- }
-
if (name) {
androidSetThreadName(name);
free(name);
@@ -307,27 +299,16 @@
int androidSetThreadPriority(pid_t tid, int pri)
{
int rc = 0;
- int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
if (curr_pri == pri) {
return rc;
}
- if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
- } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
- rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
- }
-
- if (rc) {
- lasterr = errno;
- }
-
if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
rc = INVALID_OPERATION;
} else {
- errno = lasterr;
+ errno = 0;
}
return rc;
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 6002567..3221899 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -71,19 +71,17 @@
#define CONSTEXPR
#endif
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
+/* TEMP_FAILURE_RETRY is not available on macOS, but still useful there. */
#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({ \
- typeof (exp) _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
+#define TEMP_FAILURE_RETRY(exp) \
+ ({ \
+ __typeof__(exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; \
+ })
#endif
#if defined(_WIN32)
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index d14d223..22fb36d 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -34,15 +34,13 @@
* All error codes are negative values.
*/
-// Win32 #defines NO_ERROR as well. It has the same value, so there's no
-// real conflict, though it's a bit awkward.
-#ifdef _WIN32
-# undef NO_ERROR
-#endif
-
enum {
OK = 0, // Preferred constant for checking success.
+#ifndef NO_ERROR
+ // Win32 #defines NO_ERROR as well. It has the same value, so there's no
+ // real conflict, though it's a bit awkward.
NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
+#endif
UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value
@@ -76,10 +74,4 @@
// Human readable name of error
std::string statusToString(status_t status);
-// Restore define; enumeration is in "android" namespace, so the value defined
-// there won't work for Win32 code in a different namespace.
-#ifdef _WIN32
-# define NO_ERROR 0L
-#endif
-
} // namespace android
diff --git a/libutils/include/utils/ErrorsMacros.h b/libutils/include/utils/ErrorsMacros.h
index 048c538..fdc46e6 100644
--- a/libutils/include/utils/ErrorsMacros.h
+++ b/libutils/include/utils/ErrorsMacros.h
@@ -25,6 +25,7 @@
// [1] build/soong/cc/config/global.go#commonGlobalIncludes
#include <android-base/errors.h>
#include <android-base/result.h>
+#include <log/log_main.h>
#include <assert.h>
@@ -44,13 +45,58 @@
status_t val_;
};
+
namespace base {
+// TODO(b/221235365) StatusT fulfill ResultError contract and cleanup.
+
+// Unlike typical ResultError types, the underlying code should be a status_t
+// instead of a StatusT. We also special-case message generation.
+template<>
+struct ResultError<StatusT, false> {
+ ResultError(status_t s) : val_(s) {
+ LOG_FATAL_IF(s == OK, "Result error should not hold success");
+ }
+
+ template <typename T>
+ operator expected<T, ResultError<StatusT, false>>() const {
+ return unexpected(*this);
+ }
+
+ std::string message() const { return statusToString(val_); }
+ status_t code() const { return val_; }
+
+ private:
+ const status_t val_;
+};
+
+template<>
+struct ResultError<StatusT, true> {
+ template <typename T>
+ ResultError(T&& message, status_t s) : val_(s), message_(std::forward<T>(message)) {
+ LOG_FATAL_IF(s == OK, "Result error should not hold success");
+ }
+
+ ResultError(status_t s) : val_(s) {}
+
+ template <typename T>
+ operator expected<T, ResultError<StatusT, true>>() const {
+ return unexpected(*this);
+ }
+
+ status_t code() const { return val_; }
+
+ std::string message() const { return statusToString(val_) + message_; }
+ private:
+ const status_t val_;
+ std::string message_;
+};
// Specialization of android::base::OkOrFail<V> for V = status_t. This is used to use the OR_RETURN
// and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h
// for the detailed contract.
template <>
struct OkOrFail<status_t> {
+ static_assert(std::is_same_v<status_t, int>);
// Tests if status_t is a success value of not.
static bool IsOk(const status_t& s) { return s == OK; }
@@ -71,16 +117,70 @@
// Or converts into Result<T, StatusT>. This is used when OR_RETURN is used in a function whose
// return type is Result<T, StatusT>.
- template <typename T, typename = std::enable_if_t<!std::is_same_v<T, status_t>>>
+
+ template <typename T>
operator Result<T, StatusT>() && {
- return Error<StatusT>(std::move(val_));
+ return ResultError<StatusT>(std::move(val_));
}
- operator Result<int, StatusT>() && { return Error<StatusT>(std::move(val_)); }
+ template<typename T>
+ operator Result<T, StatusT, false>() && {
+ return ResultError<StatusT, false>(std::move(val_));
+ }
+ // Since user defined conversion can be followed by numeric conversion,
+ // we have to specialize all conversions to results holding numeric types to
+ // avoid conversion ambiguities with the constructor of expected.
+#pragma push_macro("SPECIALIZED_CONVERSION")
+#define SPECIALIZED_CONVERSION(type)\
+ operator Result<type, StatusT>() && { return ResultError<StatusT>(std::move(val_)); }\
+ operator Result<type, StatusT, false>() && { return ResultError<StatusT, false>(std::move(val_));}
+
+ SPECIALIZED_CONVERSION(int)
+ SPECIALIZED_CONVERSION(short int)
+ SPECIALIZED_CONVERSION(unsigned short int)
+ SPECIALIZED_CONVERSION(unsigned int)
+ SPECIALIZED_CONVERSION(long int)
+ SPECIALIZED_CONVERSION(unsigned long int)
+ SPECIALIZED_CONVERSION(long long int)
+ SPECIALIZED_CONVERSION(unsigned long long int)
+ SPECIALIZED_CONVERSION(bool)
+ SPECIALIZED_CONVERSION(char)
+ SPECIALIZED_CONVERSION(unsigned char)
+ SPECIALIZED_CONVERSION(signed char)
+ SPECIALIZED_CONVERSION(wchar_t)
+ SPECIALIZED_CONVERSION(char16_t)
+ SPECIALIZED_CONVERSION(char32_t)
+ SPECIALIZED_CONVERSION(float)
+ SPECIALIZED_CONVERSION(double)
+ SPECIALIZED_CONVERSION(long double)
+#undef SPECIALIZED_CONVERSION
+#pragma pop_macro("SPECIALIZED_CONVERSION")
// String representation of the error value.
static std::string ErrorMessage(const status_t& s) { return statusToString(s); }
};
-
} // namespace base
+
+
+// These conversions make StatusT directly comparable to status_t in order to
+// avoid calling code whenever comparisons are desired.
+
+template <bool include_message>
+bool operator==(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
+ return (l.code() == r);
+}
+template <bool include_message>
+bool operator==(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
+ return (l == r.code());
+}
+
+template <bool include_message>
+bool operator!=(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
+ return (l.code() != r);
+}
+template <bool include_message>
+bool operator!=(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
+ return (l != r.code());
+}
+
} // namespace android
diff --git a/libvndksupport/TEST_MAPPING b/libvndksupport/TEST_MAPPING
new file mode 100644
index 0000000..c9b3b20
--- /dev/null
+++ b/libvndksupport/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libvndksupport-tests"
+ }
+ ]
+}
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index ba700cb..0159395 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -31,4 +31,6 @@
"libvndksupport",
"libbase",
],
+
+ test_suites: ["general-tests"],
}
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index fcdc86a..20f6c84 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "propertyinfoserializer_tests"
+ }
]
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d592366..fe23b62 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,5 +1,7 @@
LOCAL_PATH:= $(call my-dir)
+$(eval $(call declare-1p-copy-files,system/core/rootdir,))
+
#######################################
# init-debug.rc
include $(CLEAR_VARS)
@@ -77,7 +79,11 @@
EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
- EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+ ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw
+ else
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+ endif
endif
# Put it here instead of in init.rc module definition,
@@ -197,15 +203,9 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
LOCAL_MODULE_STEM := $(LOCAL_MODULE)
include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
- $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(UBSAN_RUNTIME_LIBRARY) \
- $(TSAN_RUNTIME_LIBRARY) \
- $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
- $(2ND_UBSAN_RUNTIME_LIBRARY) \
- $(2ND_TSAN_RUNTIME_LIBRARY))
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := \
+ $(SANITIZER_STEMS) \
+ $(2ND_SANITIZER_STEMS)
$(LOCAL_BUILT_MODULE):
@echo "Generate: $@"
@mkdir -p $(dir $@)
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index e4d3d5e..1c7b716 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "CtsBionicTestCases"
}
+ ],
+ "hwasan-postsubmit": [
+ {
+ "name": "CtsBionicTestCases"
+ }
]
}
diff --git a/rootdir/init.no_zygote.rc b/rootdir/init.no_zygote.rc
new file mode 100644
index 0000000..8e0afcb
--- /dev/null
+++ b/rootdir/init.no_zygote.rc
@@ -0,0 +1,2 @@
+# This is an empty file that does not specify any zygote services.
+# It exists to support targets that do not include a zygote.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c4c9eca..5fbe756 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -824,7 +824,10 @@
# directory used for on-device refresh metrics file.
mkdir /data/misc/odrefresh 0777 system system
# directory used for on-device signing key blob
- mkdir /data/misc/odsign 0700 root root
+ mkdir /data/misc/odsign 0710 root system
+ # directory used for odsign metrics
+ mkdir /data/misc/odsign/metrics 0770 root system
+
# Directory for VirtualizationService temporary image files.
mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
@@ -1008,12 +1011,6 @@
# module, delete if not.
exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
- # If there is no post-fs-data action in the init.<device>.rc file, you
- # must uncomment this line, otherwise encrypted filesystems
- # won't work.
- # Set indication (checked by vold) that we have finished this action
- #setprop vold.post_fs_data_done 1
-
# sys.memfd_use set to false by default, which keeps it disabled
# until it is confirmed that apps and vendor processes don't make
# IOCTLs on ashmem fds any more.
@@ -1085,9 +1082,11 @@
mkdir /dev/sys/fs/by-name 0755 system system
symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
- # to access dm-<num> sysfs
+ # dev.mnt.dev.data=dm-N, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0, or
+ # dev.mnt.dev.data=sdaN/mmcblk0pN, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0
mkdir /dev/sys/block/by-name 0755 system system
- symlink /sys/devices/virtual/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+ symlink /sys/class/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+ symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
@@ -1099,8 +1098,9 @@
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
- # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+ # this requires enabling selinux entry for sda/mmcblk0 in vendor side
write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
+ write /dev/sys/block/by-name/rootdisk/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
@@ -1306,3 +1306,15 @@
on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
setprop sys.init.userspace_reboot.in_progress ""
+
+# Multi-Gen LRU Experiment
+on property:persist.device_config.mglru_native.lru_gen_config=none
+ write /sys/kernel/mm/lru_gen/enabled 0
+on property:persist.device_config.mglru_native.lru_gen_config=core
+ write /sys/kernel/mm/lru_gen/enabled 1
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
+ write /sys/kernel/mm/lru_gen/enabled 3
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young
+ write /sys/kernel/mm/lru_gen/enabled 5
+on property:persist.device_config.mglru_native.lru_gen_config=all
+ write /sys/kernel/mm/lru_gen/enabled 7
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../build/soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index f339553..ca522b7 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -34,184 +34,71 @@
full list for a release by running `toybox` directly.
-## Android 2.3 (Gingerbread)
+## Android 13 ("T")
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-umount uptime vmstat watchprops wipe
-
-
-## Android 4.0 (IceCreamSandwich)
-
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.1-4.3 (JellyBean)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
-printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
-sendevent setconsole setenforce setprop setsebool sleep smd start stop
-sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.4 (KitKat)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
-notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
-schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
-stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 5.0 (Lollipop)
-
-BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
-
-toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
-getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
-lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
-nohup notify ps readlink renice restorecon rmmod route runcon schedtop
-sendevent setenforce setprop setsebool smd start stop swapoff swapon
-top touch umount uptime vmstat watchprops wipe
-
-
-## Android 6.0 (Marshmallow)
-
-BSD: dd du grep
-
-toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
-newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
-chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
-env expand expr fallocate false find free getenforce getprop groups
-head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
-logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
-more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
-pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate umount uname uniq unix2dos usleep
-vmstat wc which whoami xargs yes
-
-
-## Android 7.0 (Nougat)
-
-BSD: dd grep
-
-toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
-sendevent start stop top
-
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
-chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
-dos2unix **du** echo env expand expr fallocate false find **flock** free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
-lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
-mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
-pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
-**uptime** usleep vmstat wc which whoami xargs **xxd** yes
-
-
-## Android 8.0 (Oreo)
-
-BSD: dd grep
+BSD: fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
-toolbox: getevent newfs\_msdos
-
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
-dos2unix du echo env expand expr fallocate false **file** find flock free
-getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
-losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
-mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
-nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
-readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
-seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
-**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
-sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
-tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
-vmstat wc which whoami xargs xxd yes **zcat**
-
-
-## Android 9.0 (Pie)
-
-BSD: dd grep
-
-bzip2: bzcat bzip2 bunzip2
+gavinhoward/bc: bc
one-true-awk: awk
-toolbox: getevent getprop newfs\_msdos
+toolbox: getevent getprop setprop start stop
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
-dos2unix du echo env expand expr fallocate false file find flock **fmt** free
-getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
-lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
-modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
-patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
-renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
-tail tar taskset tee time timeout top touch tr true truncate tty ulimit
-umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
-which whoami xargs xxd yes zcat
+toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
+chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
+dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
+microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
+mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
+od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
+printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
+restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl **uclampset** ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
-## Android 10 ("Q")
+## Android 12 ("S")
-BSD: grep fsck\_msdos newfs\_msdos
+BSD: fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
+gavinhoward/bc: bc
+
one-true-awk: awk
-toolbox: getevent getprop
+toolbox: getevent getprop setprop start stop
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
-false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
-getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
-**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
-**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
-losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
-mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
-mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
-paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
-printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
-**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
-true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
-**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
-wc which whoami xargs xxd yes zcat
+toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
+dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
+microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
+mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
+od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
+printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
+restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee **test** time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
+unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
+vmstat watch wc which whoami xargs xxd yes zcat
+
## Android 11 ("R")
@@ -245,34 +132,182 @@
usleep uudecode uuencode uuidgen vconfig **vi** vmstat watch wc which
whoami xargs xxd yes zcat
-## Android ("S")
-BSD: fsck\_msdos newfs\_msdos
+## Android 10 ("Q")
+
+BSD: grep fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2
-gavinhoward/bc: bc
+one-true-awk: awk
+
+toolbox: getevent getprop
+
+toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
+false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
+getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
+**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
+**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
+losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
+paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
+printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
+**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
+true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
+**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
+wc which whoami xargs xxd yes zcat
+
+
+## Android 9.0 (Pie)
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
one-true-awk: awk
-toolbox: getevent getprop setprop start stop
+toolbox: getevent getprop newfs\_msdos
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
-chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
-dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
-expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
-fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
-help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
-inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
-log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
-microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
-mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
-od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
-printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
-restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
-seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee **test** time timeout top touch tr traceroute
-traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
-unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
-vmstat watch wc which whoami xargs xxd yes zcat
+toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock **fmt** free
+getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
+lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
+modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
+patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
+renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
+tail tar taskset tee time timeout top touch tr true truncate tty ulimit
+umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
+which whoami xargs xxd yes zcat
+
+
+## Android 8.0 (Oreo)
+
+BSD: dd grep
+
+bzip2: bzcat bzip2 bunzip2
+
+toolbox: getevent newfs\_msdos
+
+toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
+dos2unix du echo env expand expr fallocate false **file** find flock free
+getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
+losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
+mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
+seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
+**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
+vmstat wc which whoami xargs xxd yes **zcat**
+
+
+## Android 7.0 (Nougat)
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
+dos2unix **du** echo env expand expr fallocate false find **flock** free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
+lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
+**uptime** usleep vmstat wc which whoami xargs **xxd** yes
+
+
+## Android 6.0 (Marshmallow)
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+## Android 5.0 (Lollipop)
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.4 (KitKat)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.1-4.3 (JellyBean)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.0 (IceCreamSandwich)
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+## Android 2.3 (Gingerbread)
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index fb855f7..8cc8b59 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -153,7 +153,7 @@
mUidm.init(is_charger_on(status));
// register listener after init uid_monitor
- aidl_health_callback = std::make_shared<HealthInfoCallback>(&mUidm);
+ aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);
ret = health->registerCallback(aidl_health_callback);
if (!ret.isOk()) {
LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index d787f7a..443a670 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -17,9 +17,10 @@
#define LOG_TAG "android.hardware.keymaster@3.0-impl.trusty"
-#include <authorization_set.h>
#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster_tags.h>
#include <trusty_keymaster/TrustyKeymaster3Device.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index e68ba82..9c7e781 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -18,9 +18,10 @@
#define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <authorization_set.h>
#include <cutils/log.h>
#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+#include <keymaster_tags.h>
#include <trusty_keymaster/TrustyKeymaster4Device.h>
#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
index 5fd628f..c8d8932 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -27,6 +27,7 @@
using ::keymaster::TrustyKeymaster;
using ::ndk::ScopedAStatus;
using secureclock::TimeStampToken;
+using ::std::array;
using ::std::optional;
using ::std::shared_ptr;
using ::std::vector;
@@ -77,8 +78,13 @@
const optional<TimeStampToken>& timestampToken) override;
ScopedAStatus earlyBootEnded() override;
- ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
- std::vector<uint8_t>* ephemeralKeyBlob) override;
+ ScopedAStatus convertStorageKeyToEphemeral(const vector<uint8_t>& storageKeyBlob,
+ vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
+ ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) override;
+ ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
protected:
std::shared_ptr<TrustyKeymaster> impl_;
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
new file mode 100644
index 0000000..ae24fb4
--- /dev/null
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit" : [
+ {
+ "name" : "vts_treble_vintf_framework_test"
+ }
+ ],
+ "hwasan-postsubmit" : [
+ {
+ "name" : "vts_treble_vintf_framework_test"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 68a7912..44780e8 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -306,7 +306,7 @@
}
ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
- const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
+ const vector<uint8_t>& storageKeyBlob, vector<uint8_t>* ephemeralKeyBlob) {
keymaster::ExportKeyRequest request(impl_->message_version());
request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
request.key_format = KM_KEY_FORMAT_RAW;
@@ -321,4 +321,17 @@
return ScopedAStatus::ok();
}
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* /* challenge */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& /* challenge */,
+ vector<uint8_t>* /* rootOfTrust */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {
+ return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
} // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 5664829..099f189 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -71,9 +71,10 @@
} // namespace
ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
- info->versionNumber = 1;
+ info->versionNumber = 2;
info->rpcAuthorName = "Google";
info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+ info->uniqueId = "Trusty: My password is ******";
return ScopedAStatus::ok();
}
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 7ca5050..0b995a2 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -1,6 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IKeyMintDevice/default</fqname>
</hal>
<hal format="aidl">
@@ -13,6 +14,7 @@
</hal>
<hal format="aidl">
<name>android.hardware.security.keymint</name>
+ <version>2</version>
<fqname>IRemotelyProvisionedComponent/default</fqname>
</hal>
</manifest>
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index df6b0f8..e2f376c 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -342,6 +342,19 @@
return 0;
}
+static int provision_ids(void) {
+ keymaster::SetAttestationIdsRequest req(4 /* ver */);
+ keymaster::EmptyKeymasterResponse rsp(4 /* ver */);
+
+ req.brand.Reinitialize("trusty", 6);
+ req.device.Reinitialize("trusty", 6);
+ req.product.Reinitialize("trusty", 6);
+ req.manufacturer.Reinitialize("trusty", 6);
+ req.model.Reinitialize("trusty", 6);
+
+ return trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
+}
+
int main(int argc, char** argv) {
int ret = 0;
@@ -353,10 +366,22 @@
ret = trusty_keymaster_connect();
if (ret) {
fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
- } else {
- ret = parse_xml_file(argv[optind]);
- trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
}
- return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ ret = parse_xml_file(argv[optind]);
+ if (ret) {
+ fprintf(stderr, "parse_xml_file failed %d\n", ret);
+ trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
+ }
+
+ ret = provision_ids();
+ if (ret) {
+ fprintf(stderr, "provision_ids failed %d\n", ret);
+ trusty_keymaster_disconnect();
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
}
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
new file mode 100644
index 0000000..bc1dcf6
--- /dev/null
+++ b/trusty/libtrusty-rs/Android.bp
@@ -0,0 +1,39 @@
+// Copyright (C) 2022 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+ name: "libtrusty-rs",
+ crate_name: "trusty",
+ srcs: [
+ "src/lib.rs"
+ ],
+ rustlibs: [
+ "libnix",
+ "liblibc",
+ ],
+}
+
+rust_test {
+ name: "libtrusty-rs-tests",
+ crate_name: "trusty_test",
+ srcs: ["tests/test.rs"],
+ rustlibs: [
+ "libtrusty-rs",
+ "liblibc",
+ ]
+}
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
new file mode 100644
index 0000000..28ea075
--- /dev/null
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -0,0 +1,224 @@
+// Copyright (C) 2022 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.
+
+//! Functionality for communicating with Trusty services.
+//!
+//! This crate provides the [`TipcChannel`] type, which allows you to establish a
+//! connection to a Trusty service and then communicate with that service.
+//!
+//! # Usage
+//!
+//! To connect to a Trusty service you need two things:
+//!
+//! * The filesystem path to the Trusty IPC device. This is usually
+//! `/dev/trusty-ipc-dev0`, which is exposed in the constant [`DEFAULT_DEVICE`].
+//! * The port name defined by the service, e.g. `com.android.ipc-unittest.srv.echo`.
+//!
+//! Pass these values to [`TipcChannel::connect`] to establish a connection to a
+//! service.
+//!
+//! Once connected use the [`send`][TipcChannel::send] and [`recv`][TipcChannel::recv]
+//! methods to communicate with the service. Messages are passed as byte buffers, and
+//! each Trusty service has its own protocol for what data messages are expected to
+//! contain. Consult the documentation for the service you are communicating with to
+//! determine how to format outgoing messages and interpret incoming ones.
+//!
+//! The connection is closed automatically when [`TipcChannel`] is dropped.
+//!
+//! # Examples
+//!
+//! This example is a simplified version of the echo test from `tipc-test-rs`:
+//!
+//! ```no_run
+//! use trusty::{DEFAULT_DEVICE, TipcChannel};
+//! use std::io::{Read, Write};
+//!
+//! let mut chann = TipcChannel::connect(
+//! DEFAULT_DEVICE,
+//! "com.android.ipc-unittest.srv.echo",
+//! ).unwrap();
+//!
+//! chann.send("Hello, world!".as_bytes()).unwrap();
+//!
+//! let mut read_buf = Vec::new();
+//! let read_len = stream.recv(&mut read_buf).unwrap();
+//!
+//! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();
+//! assert_eq!("Hello, world!", response);
+//!
+//! // The connection is closed here.
+//! ```
+
+use crate::sys::tipc_connect;
+use std::ffi::CString;
+use std::fs::File;
+use std::io::prelude::*;
+use std::io::{ErrorKind, Result};
+use std::os::unix::prelude::AsRawFd;
+use std::path::Path;
+
+mod sys;
+
+/// The default filesystem path for the Trusty IPC device.
+pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0";
+
+/// The maximum size an incoming TIPC message can be.
+///
+/// This can be used to pre-allocate buffer space in order to ensure that your
+/// read buffer can always hold an incoming message.
+pub const MAX_MESSAGE_SIZE: usize = 4096;
+
+/// A channel for communicating with a Trusty service.
+///
+/// See the [crate-level documentation][crate] for usage details and examples.
+#[derive(Debug)]
+pub struct TipcChannel(File);
+
+impl TipcChannel {
+ /// Attempts to establish a connection to the specified Trusty service.
+ ///
+ /// The first argument is the path of the Trusty device in the local filesystem,
+ /// e.g. `/dev/trusty-ipc-dev0`. The second argument is the name of the service
+ /// to connect to, e.g. `com.android.ipc-unittest.srv.echo`.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `service` contains any intermediate `NUL`
+ /// bytes. This is handled with a panic because the service names are all
+ /// hard-coded constants, and so such an error should always be indicative of a
+ /// bug in the calling code.
+ pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
+ let file = File::options().read(true).write(true).open(device)?;
+
+ let srv_name = CString::new(service).expect("Service name contained null bytes");
+ unsafe {
+ tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
+ }
+
+ Ok(TipcChannel(file))
+ }
+
+ /// Sends a message to the connected service.
+ ///
+ /// The entire contents of `buf` will be sent as a single message to the
+ /// connected service.
+ pub fn send(&mut self, buf: &[u8]) -> Result<()> {
+ let write_len = self.0.write(buf)?;
+
+ // Verify that the expected number of bytes were written. The entire message
+ // should always be written with a single `write` call, or an error should have
+ // been returned if the message couldn't be written. An assertion failure here
+ // potentially means a bug in the kernel driver.
+ assert_eq!(
+ buf.len(),
+ write_len,
+ "Failed to send full message ({} of {} bytes written)",
+ write_len,
+ buf.len(),
+ );
+
+ Ok(())
+ }
+
+ /// Reads the next incoming message.
+ ///
+ /// Attempts to read the next incoming message from the connected service if any
+ /// exist. If the initial capacity of `buf` is not enough to hold the incoming
+ /// message the function repeatedly attempts to reserve additional space until
+ /// it is able to fully read the message.
+ ///
+ /// Blocks until there is an incoming message if there is not already a message
+ /// ready to be received.
+ ///
+ /// # Errors
+ ///
+ /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
+ /// then the error is ignored and the operation will be tried again.
+ ///
+ /// If this function encounters an error with the error code `EMSGSIZE` then
+ /// additional space will be reserved in `buf` and the operation will be tried
+ /// again.
+ ///
+ /// If any other read error is encountered then this function immediately
+ /// returns the error to the caller, and the length of `buf` is set to 0.
+ pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {
+ // If no space has been allocated in the buffer reserve enough space to hold any
+ // incoming message.
+ if buf.capacity() == 0 {
+ buf.reserve(MAX_MESSAGE_SIZE);
+ }
+
+ loop {
+ // Resize the vec to make its full capacity available to write into.
+ buf.resize(buf.capacity(), 0);
+
+ match self.0.read(buf.as_mut_slice()) {
+ Ok(len) => {
+ buf.truncate(len);
+ return Ok(());
+ }
+
+ Err(err) => {
+ if let Some(libc::EMSGSIZE) = err.raw_os_error() {
+ // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity
+ // to contain the maximum message size. This should never happen, but if it
+ // does we don't want to hang by looping infinitely.
+ assert!(
+ buf.capacity() < MAX_MESSAGE_SIZE,
+ "Received `EMSGSIZE` error when buffer capacity was already at maximum",
+ );
+
+ // If we didn't have enough space to hold the incoming message, reserve
+ // enough space to fit the maximum message size regardless of how much
+ // capacity the buffer already had.
+ buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());
+ } else if err.kind() == ErrorKind::Interrupted {
+ // If we get an interrupted error the operation can be retried as-is, i.e.
+ // we don't need to allocate additional space.
+ continue;
+ } else {
+ buf.truncate(0);
+ return Err(err);
+ }
+ }
+ }
+ }
+ }
+
+ /// Reads the next incoming message without allocating.
+ ///
+ /// Returns the number of bytes in the received message, or any error that
+ /// occurred when reading the message.
+ ///
+ /// Blocks until there is an incoming message if there is not already a message
+ /// ready to be received.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large
+ /// enough to contain the incoming message. Use
+ /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to
+ /// determine if you need to increase the size of `buf`. If error code
+ /// `EMSGSIZE` is returned the incoming message will not be dropped, and a
+ /// subsequent call to `recv_no_alloc` can still read it.
+ ///
+ /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
+ /// operation should be retried if there is nothing else to do.
+ pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {
+ self.0.read(buf)
+ }
+
+ // TODO: Add method that is equivalent to `tipc_send`, i.e. that supports
+ // sending shared memory buffers.
+}
diff --git a/trusty/libtrusty-rs/src/sys.rs b/trusty/libtrusty-rs/src/sys.rs
new file mode 100644
index 0000000..f1c8c5f
--- /dev/null
+++ b/trusty/libtrusty-rs/src/sys.rs
@@ -0,0 +1,31 @@
+// NOTE: The ioctl definitions are sequestered into this module because the
+// `ioctl_*!` macros provided by the nix crate generate public functions that we
+// don't want to be part of this crate's public API.
+//
+// NOTE: We are manually re-declaring the types and constants here instead of using
+// bindgen and a separate `-sys` crate because the defines used for the ioctl
+// numbers (`TIPC_IOC_CONNECT` and `TIPC_IOC_SEND_MSG`) can't currently be
+// translated by bindgen.
+
+use std::os::raw::c_char;
+
+const TIPC_IOC_MAGIC: u8 = b'r';
+
+// NOTE: We use `ioctl_write_ptr_bad!` here due to an error in how the ioctl
+// code is defined in `trusty/ipc.h`.
+//
+// If we were to do `ioctl_write_ptr!(TIPC_IOC_MAGIC, 0x80, c_char)` it would
+// generate a function that takes a `*const c_char` data arg and would use
+// `size_of::<c_char>()` when generating the ioctl number. However, in
+// `trusty/ipc.h` the definition for `TIPC_IOC_CONNECT` declares the ioctl with
+// `char*`, meaning we need to use `size_of::<*const c_char>()` to generate an
+// ioctl number that matches what Trusty expects.
+//
+// To maintain compatibility with the `trusty/ipc.h` and the kernel driver we
+// use `ioctl_write_ptr_bad!` and manually use `request_code_write!` to generate
+// the ioctl number using the correct size.
+nix::ioctl_write_ptr_bad!(
+ tipc_connect,
+ nix::request_code_write!(TIPC_IOC_MAGIC, 0x80, std::mem::size_of::<*const c_char>()),
+ c_char
+);
diff --git a/trusty/libtrusty-rs/tests/test.rs b/trusty/libtrusty-rs/tests/test.rs
new file mode 100644
index 0000000..6bff479
--- /dev/null
+++ b/trusty/libtrusty-rs/tests/test.rs
@@ -0,0 +1,86 @@
+use trusty::{TipcChannel, DEFAULT_DEVICE};
+
+const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo";
+
+#[test]
+fn recv_no_alloc() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 32];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message from the TA. The response message will be the
+ // same as the message we just sent.
+ let mut recv_buf = [0u8; 32];
+ let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();
+
+ assert_eq!(
+ send_buf.len(),
+ read_len,
+ "Received data was wrong size (expected {} bytes, received {})",
+ send_buf.len(),
+ read_len,
+ );
+ assert_eq!(send_buf, recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_small_buf() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a long message to the echo service so that we can test receiving a long
+ // message.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Attempt to receive the response message with a buffer that is too small to
+ // contain the message.
+ let mut recv_buf = [0u8; 32];
+ let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();
+
+ assert_eq!(
+ Some(libc::EMSGSIZE),
+ err.raw_os_error(),
+ "Unexpected error err when receiving incoming message: {:?}",
+ err,
+ );
+}
+
+#[test]
+fn recv_empty_vec() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message. `recv_buf` is initially empty, and `recv` is
+ // responsible for allocating enough space to hold the message.
+ let mut recv_buf = Vec::new();
+ connection.recv(&mut recv_buf).unwrap();
+
+ assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_vec_existing_capacity() {
+ let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+ .expect("Failed to connect to Trusty service");
+
+ // Send a message to the echo TA.
+ let send_buf = [7u8; 2048];
+ connection.send(send_buf.as_slice()).unwrap();
+
+ // Receive the response message into a buffer that already has enough capacity
+ // to hold the message. No additional capacity should be allocated when
+ // receiving the message.
+ let mut recv_buf = Vec::with_capacity(2048);
+ connection.recv(&mut recv_buf).unwrap();
+
+ assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+ assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed");
+}
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
index 1fa6fe4..04e84c6 100644
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -23,15 +23,19 @@
/**
* enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
- * be mapped as nonsecure. Suitable for shared memory. The paired
- * fd must be a "memfd".
- * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will be
- * transitioned to "Secure" memory if Trusty is in TrustZone.
- * This transfer kind is suitable for donating video buffers or
- * other similar resources. The paired fd may need to come from a
- * platform-specific allocator for memory that may be
- * transitioned to "Secure".
+ * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it
+ * will be mapped as nonsecure. Suitable for shared memory.
+ * The paired fd must be a "dma_buf".
+ * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will
+ * be transitioned to "Secure" memory if Trusty is in
+ * TrustZone. This transfer kind is suitable for donating
+ * video buffers or other similar resources. The paired fd
+ * may need to come from a platform-specific allocator for
+ * memory that may be transitioned to "Secure".
+ * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be
+ * accessible only to Trusty. The paired fd may need to
+ * come from a platform-specific allocator that returns
+ * "Secure" buffers.
*
* Describes how the user would like the resource in question to be sent to
* Trusty. Options may be valid only for certain kinds of fds.
@@ -39,6 +43,7 @@
enum transfer_kind {
TRUSTY_SHARE = 0,
TRUSTY_LEND = 1,
+ TRUSTY_SEND_SECURE = 2,
};
/**
diff --git a/trusty/test/driver/Android.bp b/trusty/test/driver/Android.bp
new file mode 100644
index 0000000..b813a04
--- /dev/null
+++ b/trusty/test/driver/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 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.
+
+package {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+python_test {
+ name: "trusty_driver_test",
+ srcs: [
+ "**/*.py",
+ ],
+ test_suites: ["general-tests"],
+ version: {
+ py3: {
+ embedded_launcher: true,
+ enabled: true,
+ },
+ },
+}
diff --git a/trusty/test/driver/trusty_driver_test.py b/trusty/test/driver/trusty_driver_test.py
new file mode 100644
index 0000000..608fd47
--- /dev/null
+++ b/trusty/test/driver/trusty_driver_test.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+#
+# Copyright 2022 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.
+
+"""Test cases for Trusty Linux Driver."""
+
+import os
+import unittest
+
+def ReadFile(file_path):
+ with open(file_path, 'r') as f:
+ # Strip all trailing spaces, newline and null characters.
+ return f.read().rstrip(' \n\x00')
+
+def WriteFile(file_path, s):
+ with open(file_path, 'w') as f:
+ f.write(s)
+
+def IsTrustySupported():
+ return os.path.exists("/dev/trusty-ipc-dev0")
+
+@unittest.skipIf(not IsTrustySupported(), "Device does not support Trusty")
+class TrustyDriverTest(unittest.TestCase):
+ def testIrqDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/unbind", "trusty:irq")
+ WriteFile("/sys/bus/platform/drivers/trusty-irq/bind", "trusty:irq")
+
+ def testLogDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-log/unbind", "trusty:log")
+ WriteFile("/sys/bus/platform/drivers/trusty-log/bind", "trusty:log")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testVirtioDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/unbind",
+ "trusty:virtio")
+ WriteFile("/sys/bus/platform/drivers/trusty-virtio/bind",
+ "trusty:virtio")
+
+ @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
+ def testTrustyDriverBinding(self):
+ WriteFile("/sys/bus/platform/drivers/trusty/unbind", "trusty")
+ WriteFile("/sys/bus/platform/drivers/trusty/bind", "trusty")
+
+ def testTrustyDriverVersion(self):
+ ver = ReadFile("/sys/bus/platform/devices/trusty/trusty_version")
+ self.assertTrue(ver.startswith("Project:"))
+
+ def testUntaintedLinux(self):
+ tainted = ReadFile("/proc/sys/kernel/tainted")
+ self.assertEqual(tainted, "0")
+
+ # stdcall test with shared memory buffers.
+ # Each test run takes up to 4 arguments:
+ # <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>
+ #
+ # Test single 4K shared memory object.
+ # Test 10 8MB objects, shared twice, each accessed twice. (8MB non-
+ # contiguous object is large enough to need several 4KB messages to
+ # describe)
+ # Test sharing 2 8MB objects 100 times without accessing it.
+ # Test 10 4K shared memory objects, shared 10 times, each accessed
+ # 10 times.
+ def testStdCall(self):
+ test = "/sys/devices/platform/trusty/trusty:test/trusty_test_run"
+ args = "0x1000 0x800000,10,2,2 0x800000,2,100,0 0x1000,10,10,10"
+ WriteFile(test, args)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index 74106ec..3a43774 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -13,7 +13,7 @@
# limitations under the License.
PRODUCT_PACKAGES += \
- spiproxyd \
- trusty_keymaster_set_attestation_key \
keymaster_soft_attestation_keys.xml \
-
+ spiproxyd \
+ trusty_driver_test \
+ trusty_keymaster_set_attestation_key \