Merge changes I16bd2d5e,I04e9bf73
* changes:
DO NOT MERGE: Revert "init: Add more diagnostics for signalfd hangs."
DO NOT MERGE: Revert "init: Add diagnostics for signalfd epoll failures."
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 a541f6e..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",
],
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/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 23b106e..799163e 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -45,6 +45,8 @@
shared_libs: [
"libbase",
"liblog",
+ ],
+ static_libs: [
"libseccomp_policy",
],
multilib: {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index db30b8f..55490b5 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -39,6 +39,8 @@
#include "debuggerd/handler.h"
#endif
+extern "C" void android_set_abort_message(const char* msg);
+
#if defined(__arm__)
// See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
#define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
@@ -182,6 +184,8 @@
fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
+ fprintf(stderr, " abort_with_msg call abort() setting an abort message\n");
+ fprintf(stderr, " abort_with_null_msg call abort() setting a null abort message\n");
fprintf(stderr, " assert call assert() without a function\n");
fprintf(stderr, " assert2 call assert() with a function\n");
fprintf(stderr, " exit call exit(1)\n");
@@ -259,6 +263,12 @@
return crash(42);
} else if (!strcasecmp(arg, "abort")) {
maybe_abort();
+ } else if (!strcasecmp(arg, "abort_with_msg")) {
+ android_set_abort_message("Aborting due to crasher");
+ maybe_abort();
+ } else if (!strcasecmp(arg, "abort_with_null")) {
+ android_set_abort_message(nullptr);
+ maybe_abort();
} else if (!strcasecmp(arg, "assert")) {
__assert("some_file.c", 123, "false");
} else if (!strcasecmp(arg, "assert2")) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 85adbea..a5e2413 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1825,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);
}
@@ -1857,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/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_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 3e31bb7..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>
@@ -419,18 +420,29 @@
return;
}
- if (unwinder->elf_from_memory_not_file()) {
+ 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.";
- }
- unwinder->SetDisplayBuildID(true);
- for (const auto& frame : unwinder->frames()) {
- BacktraceFrame* f = thread.add_current_backtrace();
- fill_in_backtrace_frame(f, frame);
+ *backtrace_note->Add() = "Unreadable libraries:";
+ for (auto& name : unreadable_elf_files) {
+ *backtrace_note->Add() = " " + name;
+ *unreadable_elf_files_proto->Add() = name;
+ }
}
}
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 4e8fdf9..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 || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_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 4eb996e..0cb8e08 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -34,7 +34,15 @@
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 || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 22f8363..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,7 +189,7 @@
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;
}
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/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 960173a..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;
}
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 143e980..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;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 996fa5e..82b5275 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -126,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");
}
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/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/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/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 797d627..d086f29 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3205,15 +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;
status.set_io_uring_enabled(IsIouringEnabled());
- LOG(INFO) << "User-space snapshots enabled";
+ 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_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 04d228d..36abf71 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2411,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());
@@ -2422,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 0b88567..2f7775c 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -34,6 +34,17 @@
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);
}
@@ -223,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_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index 0cb41d3..c26a2cd 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -288,9 +288,6 @@
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
- // We need to make sure to reap all the I/O's submitted
- // even if there are any errors observed.
- //
// 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
@@ -300,11 +297,13 @@
if (ret) {
SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
status = false;
+ break;
}
if (cqe->res < 0) {
SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed with res: " << cqe->res;
status = false;
+ break;
}
io_uring_cqe_seen(ring_.get(), cqe);
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 7d9d392..fa2866f 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -395,9 +395,6 @@
while (pending_ios_to_complete) {
struct io_uring_cqe* cqe;
- // We need to make sure to reap all the I/O's submitted
- // even if there are any errors observed.
- //
// 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
@@ -407,11 +404,13 @@
if (ret) {
SNAP_LOG(ERROR) << "Read-ahead - 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;
status = false;
+ break;
}
io_uring_cqe_seen(ring_.get(), cqe);
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/tests/Android.bp b/fs_mgr/tests/Android.bp
index 7e3924a..d82d566 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -96,7 +96,7 @@
"device-tests",
],
test_options: {
- min_shipping_api_level: 31,
+ min_shipping_api_level: 29,
},
require_root: true,
auto_gen_config: true,
@@ -109,9 +109,9 @@
],
shared_libs: [
"libbase",
- "libfs_mgr",
],
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 1dbee75..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]);
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 415e67e..b5fac53 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -102,7 +102,7 @@
}
TEST(fs, NoDtFstab) {
- if (GetVsrLevel() <= __ANDROID_API_S__) {
+ if (GetVsrLevel() < __ANDROID_API_Q__) {
GTEST_SKIP();
}
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 c39d163..dd67d04 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -532,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",
@@ -556,7 +556,6 @@
"libprocessgroup",
"libprotobuf-cpp-lite",
],
- srcs: init_common_sources + init_host_sources,
proto: {
type: "lite",
},
@@ -574,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 c82dbfb..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
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/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..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;
@@ -285,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 {
@@ -304,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";
}
}
@@ -352,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..d5bdd12 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,7 +656,7 @@
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";
}
@@ -775,82 +790,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 +803,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 +954,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 +994,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/selinux.cpp b/init/selinux.cpp
index c89c5ab..be8c554 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -762,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);
diff --git a/init/service.cpp b/init/service.cpp
index f6dd9b9..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 "
@@ -405,6 +407,114 @@
}
}
+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_) {
@@ -442,20 +552,8 @@
return ErrnoError() << "pipe()";
}
- bool needs_console = (flags_ & SVC_CONSOLE);
- if (needs_console) {
- 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);
+ if (Result<void> result = CheckConsole(); !result.ok()) {
+ return result;
}
struct stat sb;
@@ -527,45 +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();
- }
-
- // Wait until the cgroups have been created and until the cgroup controllers have been
- // activated.
- if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
- PLOG(ERROR) << "failed to read from notification channel";
- }
- pipefd.reset();
-
- 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);
}
@@ -592,54 +652,22 @@
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 (write((*pipefd)[1], "", 1) < 0) {
+ if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
return ErrnoError() << "sending notification failed";
}
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/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/trace.h b/libcutils/include/cutils/trace.h
index 17a0070..98ae0d4 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -209,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
@@ -227,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 8e6b81c..8bb8652 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -133,6 +133,10 @@
#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 */
@@ -210,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/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/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/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 76d5e13..edda414 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);
@@ -142,6 +159,20 @@
return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
}
+// C wrapper for SetProcessProfiles.
+// No need to have this in the header file because this function is specifically for crosvm. Crosvm
+// which is written in Rust has its own declaration of this foreign function and doesn't rely on the
+// header. See
+// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
+extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
+ const char* profiles[]) {
+ std::vector<std::string> profiles_(num_profiles);
+ for (size_t i = 0; i < num_profiles; i++) {
+ profiles_.emplace_back(profiles[i]);
+ }
+ return SetProcessProfiles(uid, pid, profiles_);
+}
+
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
return StringPrintf("%s/uid_%d", cgroup, uid);
}
@@ -199,13 +230,13 @@
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) {
@@ -411,10 +442,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;
@@ -442,11 +474,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;
@@ -491,10 +523,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 dc7c368..e1c5934 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -129,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;
}
@@ -206,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;
}
@@ -625,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);
}
@@ -675,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;
}
@@ -786,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) {
@@ -794,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) {
@@ -812,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 9ee3781..df08f65 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -37,8 +37,12 @@
class ProfileAttribute : public IProfileAttribute {
public:
- ProfileAttribute(const CgroupController& controller, const std::string& file_name)
- : controller_(controller), file_name_(file_name) {}
+ // 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;
const CgroupController* controller() const override { return &controller_; }
@@ -50,6 +54,7 @@
private:
CgroupController controller_;
std::string file_name_;
+ std::string file_v2_name_;
};
// Abstract profile element
@@ -102,8 +107,8 @@
// Set attribute profile element
class SetAttributeAction : public ProfileAction {
public:
- SetAttributeAction(const IProfileAttribute* 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) {}
const char* Name() const override { return "SetAttribute"; }
bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
@@ -112,6 +117,7 @@
private:
const IProfileAttribute* attribute_;
std::string value_;
+ bool optional_;
};
// Set cgroup profile element
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 53f6065..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: {
@@ -130,6 +128,9 @@
enabled: true,
},
},
+ fuzz_config: {
+ cc: ["smoreland@google.com"],
+ },
}
cc_library {
@@ -187,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/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/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/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 11f414f..fe23b62 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -79,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,
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.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/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index c72af40..278499f 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -223,6 +223,9 @@
case APPLOADER_ERR_INVALID_VERSION:
LOG(ERROR) << "Error: invalid application version";
break;
+ case APPLOADER_ERR_POLICY_VIOLATION:
+ LOG(ERROR) << "Error: loading denied by policy engine";
+ break;
default:
LOG(ERROR) << "Unrecognized error: " << resp.error;
break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 6cda7c1..306596e 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -56,6 +56,7 @@
APPLOADER_ERR_ALREADY_EXISTS,
APPLOADER_ERR_INTERNAL,
APPLOADER_ERR_INVALID_VERSION,
+ APPLOADER_ERR_POLICY_VIOLATION,
};
/**
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/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index 2400ccd..ae24fb4 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"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/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,
};
/**