diff options
56 files changed, 2660 insertions, 266 deletions
diff --git a/base/Android.bp b/base/Android.bp index 835146146..b25d0df95 100644 --- a/base/Android.bp +++ b/base/Android.bp @@ -56,6 +56,7 @@ cc_defaults { "chrono_utils.cpp", "cmsg.cpp", "file.cpp", + "liblog_symbols.cpp", "logging.cpp", "mapped_file.cpp", "parsebool.cpp", @@ -68,6 +69,10 @@ cc_defaults { "test_utils.cpp", ], + static: { + cflags: ["-DNO_LIBLOG_DLSYM"], + }, + cppflags: ["-Wexit-time-destructors"], shared_libs: ["liblog"], target: { diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h index 3a9186aed..077988e1c 100644 --- a/base/include/android-base/logging.h +++ b/base/include/android-base/logging.h @@ -85,7 +85,7 @@ enum LogSeverity { INFO, WARNING, ERROR, - FATAL_WITHOUT_ABORT, + FATAL_WITHOUT_ABORT, // For loggability tests, this is considered identical to FATAL. FATAL, }; @@ -93,6 +93,8 @@ enum LogId { DEFAULT, MAIN, SYSTEM, + RADIO, + CRASH, }; using LogFunction = std::function<void(LogId, LogSeverity, const char*, const char*, @@ -209,8 +211,8 @@ struct LogAbortAfterFullExpr { #define ABORT_AFTER_LOG_FATAL_EXPR(x) ABORT_AFTER_LOG_EXPR_IF(true, x) // Defines whether the given severity will be logged or silently swallowed. -#define WOULD_LOG(severity) \ - (UNLIKELY((SEVERITY_LAMBDA(severity)) >= ::android::base::GetMinimumLogSeverity()) || \ +#define WOULD_LOG(severity) \ + (UNLIKELY(::android::base::ShouldLog(SEVERITY_LAMBDA(severity), _LOG_TAG_INTERNAL)) || \ MUST_LOG_MESSAGE(severity)) // Get an ostream that can be used for logging at the given severity and to the default @@ -442,6 +444,9 @@ LogSeverity GetMinimumLogSeverity(); // Set the minimum severity level for logging, returning the old severity. LogSeverity SetMinimumLogSeverity(LogSeverity new_severity); +// Return whether or not a log message with the associated tag should be logged. +bool ShouldLog(LogSeverity severity, const char* tag); + // Allows to temporarily change the minimum severity level for logging. class ScopedLogSeverity { public: diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp new file mode 100644 index 000000000..d0e2eaa77 --- /dev/null +++ b/base/liblog_symbols.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "liblog_symbols.h" + +#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM) +#include <dlfcn.h> +#endif + +namespace android { +namespace base { + +#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM) + +const std::optional<LibLogFunctions>& GetLibLogFunctions() { + static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> { + void* liblog_handle = dlopen("liblog.so", RTLD_NOW); + if (liblog_handle == nullptr) { + return {}; + } + + LibLogFunctions real_liblog_functions = {}; + +#define DLSYM(name) \ + real_liblog_functions.name = \ + reinterpret_cast<decltype(LibLogFunctions::name)>(dlsym(liblog_handle, #name)); \ + if (real_liblog_functions.name == nullptr) { \ + return {}; \ + } + + DLSYM(__android_log_set_logger) + DLSYM(__android_log_write_logger_data) + DLSYM(__android_log_logd_logger) + DLSYM(__android_log_stderr_logger) + DLSYM(__android_log_set_aborter) + DLSYM(__android_log_call_aborter) + DLSYM(__android_log_default_aborter) + DLSYM(__android_log_set_minimum_priority); + DLSYM(__android_log_get_minimum_priority); +#undef DLSYM + + return real_liblog_functions; + }(); + + return liblog_functions; +} + +#else + +const std::optional<LibLogFunctions>& GetLibLogFunctions() { + static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> { + return LibLogFunctions{ + .__android_log_set_logger = __android_log_set_logger, + .__android_log_write_logger_data = __android_log_write_logger_data, + .__android_log_logd_logger = __android_log_logd_logger, + .__android_log_stderr_logger = __android_log_stderr_logger, + .__android_log_set_aborter = __android_log_set_aborter, + .__android_log_call_aborter = __android_log_call_aborter, + .__android_log_default_aborter = __android_log_default_aborter, + .__android_log_set_minimum_priority = __android_log_set_minimum_priority, + .__android_log_get_minimum_priority = __android_log_get_minimum_priority, + }; + }(); + return liblog_functions; +} + +#endif + +} // namespace base +} // namespace android diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h new file mode 100644 index 000000000..c68fff680 --- /dev/null +++ b/base/liblog_symbols.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <optional> + +#include <android/log.h> + +namespace android { +namespace base { + +struct LibLogFunctions { + void (*__android_log_set_logger)(__android_logger_function logger); + void (*__android_log_write_logger_data)(struct __android_logger_data* logger_data, + const char* msg); + + void (*__android_log_logd_logger)(const struct __android_logger_data* logger_data, + const char* msg); + void (*__android_log_stderr_logger)(const struct __android_logger_data* logger_data, + const char* message); + + void (*__android_log_set_aborter)(__android_aborter_function aborter); + void (*__android_log_call_aborter)(const char* abort_message); + void (*__android_log_default_aborter)(const char* abort_message); + int (*__android_log_set_minimum_priority)(int priority); + int (*__android_log_get_minimum_priority)(); +}; + +const std::optional<LibLogFunctions>& GetLibLogFunctions(); + +} // namespace base +} // namespace android diff --git a/base/logging.cpp b/base/logging.cpp index b46abbfe6..508871d8d 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -36,15 +36,16 @@ #include <sys/uio.h> #endif +#include <atomic> #include <iostream> #include <limits> #include <mutex> +#include <optional> #include <sstream> #include <string> #include <utility> #include <vector> -// Headers for LogMessage::LogLine. #include <android/log.h> #ifdef __ANDROID__ #include <android/set_abort_message.h> @@ -59,6 +60,8 @@ #include <android-base/strings.h> #include <android-base/threads.h> +#include "liblog_symbols.h" + namespace android { namespace base { @@ -115,11 +118,84 @@ static int OpenKmsg() { } #endif +static LogId log_id_tToLogId(int buffer_id) { + switch (buffer_id) { + case LOG_ID_MAIN: + return MAIN; + case LOG_ID_SYSTEM: + return SYSTEM; + case LOG_ID_RADIO: + return RADIO; + case LOG_ID_CRASH: + return CRASH; + case LOG_ID_DEFAULT: + default: + return DEFAULT; + } +} + +static int LogIdTolog_id_t(LogId log_id) { + switch (log_id) { + case MAIN: + return LOG_ID_MAIN; + case SYSTEM: + return LOG_ID_SYSTEM; + case RADIO: + return LOG_ID_RADIO; + case CRASH: + return LOG_ID_CRASH; + case DEFAULT: + default: + return LOG_ID_DEFAULT; + } +} + +static LogSeverity PriorityToLogSeverity(int priority) { + switch (priority) { + case ANDROID_LOG_DEFAULT: + return INFO; + case ANDROID_LOG_VERBOSE: + return VERBOSE; + case ANDROID_LOG_DEBUG: + return DEBUG; + case ANDROID_LOG_INFO: + return INFO; + case ANDROID_LOG_WARN: + return WARNING; + case ANDROID_LOG_ERROR: + return ERROR; + case ANDROID_LOG_FATAL: + return FATAL; + default: + return FATAL; + } +} + +static android_LogPriority LogSeverityToPriority(LogSeverity severity) { + switch (severity) { + case VERBOSE: + return ANDROID_LOG_VERBOSE; + case DEBUG: + return ANDROID_LOG_DEBUG; + case INFO: + return ANDROID_LOG_INFO; + case WARNING: + return ANDROID_LOG_WARN; + case ERROR: + return ANDROID_LOG_ERROR; + case FATAL_WITHOUT_ABORT: + case FATAL: + default: + return ANDROID_LOG_FATAL; + } +} + static std::mutex& LoggingLock() { static auto& logging_lock = *new std::mutex(); return logging_lock; } +// Only used for Q fallback. static LogFunction& Logger() { #ifdef __ANDROID__ static auto& logger = *new LogFunction(LogdLogger()); @@ -129,6 +205,7 @@ static LogFunction& Logger() { return logger; } +// Only used for Q fallback. static AbortFunction& Aborter() { static auto& aborter = *new AbortFunction(DefaultAborter); return aborter; @@ -158,6 +235,8 @@ void SetDefaultTag(const std::string& tag) { } static bool gInitialized = false; + +// Only used for Q fallback. static LogSeverity gMinimumLogSeverity = INFO; #if defined(__linux__) @@ -218,8 +297,13 @@ void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file static_assert(arraysize(log_characters) - 1 == FATAL + 1, "Mismatch in size of log_characters and values in LogSeverity"); char severity_char = log_characters[severity]; - fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char, - timestamp, getpid(), GetThreadId(), file, line, message); + if (file != nullptr) { + fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char, + timestamp, getpid(), GetThreadId(), file, line, message); + } else { + fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char, + timestamp, getpid(), GetThreadId(), message); + } } void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/, @@ -248,29 +332,25 @@ LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) { void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { - static constexpr android_LogPriority kLogSeverityToAndroidLogPriority[] = { - ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, - ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, - ANDROID_LOG_FATAL, - }; - static_assert(arraysize(kLogSeverityToAndroidLogPriority) == FATAL + 1, - "Mismatch in size of kLogSeverityToAndroidLogPriority and values in LogSeverity"); - - int priority = kLogSeverityToAndroidLogPriority[severity]; + android_LogPriority priority = LogSeverityToPriority(severity); if (id == DEFAULT) { id = default_log_id_; } - static constexpr log_id kLogIdToAndroidLogId[] = { - LOG_ID_MAX, LOG_ID_MAIN, LOG_ID_SYSTEM, - }; - static_assert(arraysize(kLogIdToAndroidLogId) == SYSTEM + 1, - "Mismatch in size of kLogIdToAndroidLogId and values in LogId"); - log_id lg_id = kLogIdToAndroidLogId[id]; + int lg_id = LogIdTolog_id_t(id); + + char log_message[1024]; + if (priority == ANDROID_LOG_FATAL && file != nullptr) { + snprintf(log_message, sizeof(log_message), "%s:%u] %s", file, line, message); + } else { + snprintf(log_message, sizeof(log_message), "%s", message); + } - if (priority == ANDROID_LOG_FATAL) { - __android_log_buf_print(lg_id, priority, tag, "%s:%u] %s", file, line, - message); + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + __android_logger_data logger_data = {sizeof(__android_logger_data), lg_id, priority, tag, + static_cast<const char*>(nullptr), 0}; + liblog_functions->__android_log_logd_logger(&logger_data, log_message); } else { __android_log_buf_print(lg_id, priority, tag, "%s", message); } @@ -305,27 +385,27 @@ void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { if (spec.size() == 3 && StartsWith(spec, "*:")) { switch (spec[2]) { case 'v': - gMinimumLogSeverity = VERBOSE; + SetMinimumLogSeverity(VERBOSE); continue; case 'd': - gMinimumLogSeverity = DEBUG; + SetMinimumLogSeverity(DEBUG); continue; case 'i': - gMinimumLogSeverity = INFO; + SetMinimumLogSeverity(INFO); continue; case 'w': - gMinimumLogSeverity = WARNING; + SetMinimumLogSeverity(WARNING); continue; case 'e': - gMinimumLogSeverity = ERROR; + SetMinimumLogSeverity(ERROR); continue; case 'f': - gMinimumLogSeverity = FATAL_WITHOUT_ABORT; + SetMinimumLogSeverity(FATAL_WITHOUT_ABORT); continue; // liblog will even suppress FATAL if you say 's' for silent, but that's // crazy! case 's': - gMinimumLogSeverity = FATAL_WITHOUT_ABORT; + SetMinimumLogSeverity(FATAL_WITHOUT_ABORT); continue; } } @@ -335,13 +415,46 @@ void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) { } void SetLogger(LogFunction&& logger) { - std::lock_guard<std::mutex> lock(LoggingLock()); - Logger() = std::move(logger); + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + // We need to atomically swap the old and new pointers since other threads may be logging. + // We know all threads will be using the new logger after __android_log_set_logger() returns, + // so we can delete it then. + // This leaks one std::function<> per instance of libbase if multiple copies of libbase within a + // single process call SetLogger(). That is the same cost as having a static + // std::function<>, which is the not-thread-safe alternative. + static std::atomic<LogFunction*> logger_function(nullptr); + auto* old_logger_function = logger_function.exchange(new LogFunction(logger)); + liblog_functions->__android_log_set_logger([](const struct __android_logger_data* logger_data, + const char* message) { + auto log_id = log_id_tToLogId(logger_data->buffer_id); + auto severity = PriorityToLogSeverity(logger_data->priority); + + auto& function = *logger_function.load(std::memory_order_acquire); + function(log_id, severity, logger_data->tag, logger_data->file, logger_data->line, message); + }); + delete old_logger_function; + } else { + std::lock_guard<std::mutex> lock(LoggingLock()); + Logger() = std::move(logger); + } } void SetAborter(AbortFunction&& aborter) { - std::lock_guard<std::mutex> lock(LoggingLock()); - Aborter() = std::move(aborter); + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + // See the comment in SetLogger(). + static std::atomic<AbortFunction*> abort_function(nullptr); + auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter)); + __android_log_set_aborter([](const char* abort_message) { + auto& function = *abort_function.load(std::memory_order_acquire); + function(abort_message); + }); + delete old_abort_function; + } else { + std::lock_guard<std::mutex> lock(LoggingLock()); + Aborter() = std::move(aborter); + } } // This indirection greatly reduces the stack impact of having lots of @@ -444,7 +557,12 @@ LogMessage::~LogMessage() { // Abort if necessary. if (data_->GetSeverity() == FATAL) { - Aborter()(msg.c_str()); + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + liblog_functions->__android_log_call_aborter(msg.c_str()); + } else { + Aborter()(msg.c_str()); + } } } @@ -454,25 +572,65 @@ std::ostream& LogMessage::stream() { void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag, const char* message) { + static auto& liblog_functions = GetLibLogFunctions(); + auto priority = LogSeverityToPriority(severity); if (tag == nullptr) { std::lock_guard<std::recursive_mutex> lock(TagLock()); if (gDefaultTag == nullptr) { gDefaultTag = new std::string(getprogname()); } - Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message); + + if (liblog_functions) { + __android_logger_data logger_data = {sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, + gDefaultTag->c_str(), file, line}; + __android_log_write_logger_data(&logger_data, message); + } else { + Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message); + } } else { - Logger()(DEFAULT, severity, tag, file, line, message); + if (liblog_functions) { + __android_logger_data logger_data = { + sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line}; + __android_log_write_logger_data(&logger_data, message); + } else { + Logger()(DEFAULT, severity, tag, file, line, message); + } } } LogSeverity GetMinimumLogSeverity() { + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority()); + } else { return gMinimumLogSeverity; + } +} + +bool ShouldLog(LogSeverity severity, const char* tag) { + static auto& liblog_functions = GetLibLogFunctions(); + // Even though we're not using the R liblog functions in this function, if we're running on Q, + // we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not + // take into consideration the value from SetMinimumLogSeverity(). + if (liblog_functions) { + // TODO: It is safe to pass nullptr for tag, but it will be better to use the default log tag. + int priority = LogSeverityToPriority(severity); + return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO); + } else { + return severity >= gMinimumLogSeverity; + } } LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) { - LogSeverity old_severity = gMinimumLogSeverity; - gMinimumLogSeverity = new_severity; - return old_severity; + static auto& liblog_functions = GetLibLogFunctions(); + if (liblog_functions) { + auto priority = LogSeverityToPriority(new_severity); + return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority)); + } else { + LogSeverity old_severity = gMinimumLogSeverity; + gMinimumLogSeverity = new_severity; + return old_severity; + } } ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) { diff --git a/base/logging_test.cpp b/base/logging_test.cpp index 3113fb47a..8f4619639 100644 --- a/base/logging_test.cpp +++ b/base/logging_test.cpp @@ -140,10 +140,6 @@ TEST(logging, WOULD_LOG_FATAL) { CHECK_WOULD_LOG_ENABLED(FATAL); } -TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_disabled) { - CHECK_WOULD_LOG_DISABLED(FATAL_WITHOUT_ABORT); -} - TEST(logging, WOULD_LOG_FATAL_WITHOUT_ABORT_enabled) { CHECK_WOULD_LOG_ENABLED(FATAL_WITHOUT_ABORT); } @@ -266,10 +262,6 @@ static void CheckMessage(CapturedStderr& cap, android::base::LogSeverity severit CheckMessage(cap2, android::base::severity, "foobar"); \ } \ -TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_disabled) { - CHECK_LOG_STREAM_DISABLED(FATAL_WITHOUT_ABORT); -} - TEST(logging, LOG_STREAM_FATAL_WITHOUT_ABORT_enabled) { ASSERT_NO_FATAL_FAILURE(CHECK_LOG_STREAM_ENABLED(FATAL_WITHOUT_ABORT)); } @@ -352,10 +344,6 @@ TEST(logging, LOG_FATAL) { ASSERT_DEATH({SuppressAbortUI(); LOG(::android::base::FATAL) << "foobar";}, "foobar"); } -TEST(logging, LOG_FATAL_WITHOUT_ABORT_disabled) { - CHECK_LOG_DISABLED(FATAL_WITHOUT_ABORT); -} - TEST(logging, LOG_FATAL_WITHOUT_ABORT_enabled) { ASSERT_NO_FATAL_FAILURE(CHECK_LOG_ENABLED(FATAL_WITHOUT_ABORT)); } @@ -508,10 +496,6 @@ TEST(logging, PLOG_FATAL) { ASSERT_DEATH({SuppressAbortUI(); PLOG(::android::base::FATAL) << "foobar";}, "foobar"); } -TEST(logging, PLOG_FATAL_WITHOUT_ABORT_disabled) { - CHECK_PLOG_DISABLED(FATAL_WITHOUT_ABORT); -} - TEST(logging, PLOG_FATAL_WITHOUT_ABORT_enabled) { ASSERT_NO_FATAL_FAILURE(CHECK_PLOG_ENABLED(FATAL_WITHOUT_ABORT)); } diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index 6e0128993..f8192b52d 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -592,19 +592,20 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { g_callbacks = *callbacks; } - void* thread_stack_allocation = - mmap(nullptr, PAGE_SIZE * 3, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + size_t thread_stack_pages = 8; + void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (thread_stack_allocation == MAP_FAILED) { fatal_errno("failed to allocate debuggerd thread stack"); } char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE; - if (mprotect(stack, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) { + if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) { fatal_errno("failed to mprotect debuggerd thread stack"); } // Stack grows negatively, set it to the last byte in the page... - stack = (stack + PAGE_SIZE - 1); + stack = (stack + thread_stack_pages * PAGE_SIZE - 1); // and align it. stack -= 15; pseudothread_stack = stack; diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp index 5066046a2..9c8076528 100644 --- a/fastboot/device/usb_client.cpp +++ b/fastboot/device/usb_client.cpp @@ -297,3 +297,7 @@ int ClientUsbTransport::Close() { CloseFunctionFs(handle_.get()); return 0; } + +int ClientUsbTransport::Reset() { + return 0; +} diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h index 3694f9a20..e6a1a8b8c 100644 --- a/fastboot/device/usb_client.h +++ b/fastboot/device/usb_client.h @@ -29,6 +29,7 @@ class ClientUsbTransport : public Transport { ssize_t Read(void* data, size_t len) override; ssize_t Write(const void* data, size_t len) override; int Close() override; + int Reset() override; private: std::unique_ptr<usb_handle> handle_; diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp index d48cfa97d..bb54fd983 100644 --- a/fastboot/fuzzy_fastboot/Android.bp +++ b/fastboot/fuzzy_fastboot/Android.bp @@ -5,7 +5,7 @@ cc_test_host { srcs: [ "main.cpp", "extensions.cpp", - "usb_transport_sniffer.cpp", + "transport_sniffer.cpp", "fixtures.cpp", "test_utils.cpp", ], diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp index bc13a8c4e..bd76ff4ee 100644 --- a/fastboot/fuzzy_fastboot/fixtures.cpp +++ b/fastboot/fuzzy_fastboot/fixtures.cpp @@ -48,12 +48,13 @@ #include <gtest/gtest.h> #include "fastboot_driver.h" +#include "tcp.h" #include "usb.h" #include "extensions.h" #include "fixtures.h" #include "test_utils.h" -#include "usb_transport_sniffer.h" +#include "transport_sniffer.h" using namespace std::literals::chrono_literals; @@ -74,7 +75,14 @@ int FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_ser return 0; } +bool FastBootTest::IsFastbootOverTcp() { + // serial contains ":" is treated as host ip and port number + return (device_serial.find(":") != std::string::npos); +} + bool FastBootTest::UsbStillAvailible() { + if (IsFastbootOverTcp()) return true; + // For some reason someone decided to prefix the path with "usb:" std::string prefix("usb:"); if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) { @@ -113,15 +121,19 @@ void FastBootTest::SetUp() { ASSERT_TRUE(UsbStillAvailible()); // The device disconnected } - const auto matcher = [](usb_ifc_info* info) -> int { - return MatchFastboot(info, device_serial); - }; - for (int i = 0; i < MAX_USB_TRIES && !transport; i++) { - std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT)); - if (usb) - transport = std::unique_ptr<UsbTransportSniffer>( - new UsbTransportSniffer(std::move(usb), serial_port)); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + if (IsFastbootOverTcp()) { + ConnectTcpFastbootDevice(); + } else { + const auto matcher = [](usb_ifc_info* info) -> int { + return MatchFastboot(info, device_serial); + }; + for (int i = 0; i < MAX_USB_TRIES && !transport; i++) { + std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT)); + if (usb) + transport = std::unique_ptr<TransportSniffer>( + new TransportSniffer(std::move(usb), serial_port)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } } ASSERT_TRUE(transport); // no nullptr @@ -154,6 +166,8 @@ void FastBootTest::TearDown() { // TODO, this should eventually be piped to a file instead of stdout void FastBootTest::TearDownSerial() { + if (IsFastbootOverTcp()) return; + if (!transport) return; // One last read from serial transport->ProcessSerial(); @@ -167,9 +181,34 @@ void FastBootTest::TearDownSerial() { } } +void FastBootTest::ConnectTcpFastbootDevice() { + std::size_t found = device_serial.find(":"); + if (found != std::string::npos) { + for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) { + std::string error; + std::unique_ptr<Transport> tcp( + tcp::Connect(device_serial.substr(0, found), tcp::kDefaultPort, &error) + .release()); + if (tcp) + transport = + std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0)); + if (transport != nullptr) break; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } +} + void FastBootTest::ReconnectFastbootDevice() { fb.reset(); transport.reset(); + + if (IsFastbootOverTcp()) { + ConnectTcpFastbootDevice(); + device_path = cb_scratch; + fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true)); + return; + } + while (UsbStillAvailible()) ; printf("WAITING FOR DEVICE\n"); @@ -180,8 +219,8 @@ void FastBootTest::ReconnectFastbootDevice() { while (!transport) { std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT)); if (usb) { - transport = std::unique_ptr<UsbTransportSniffer>( - new UsbTransportSniffer(std::move(usb), serial_port)); + transport = std::unique_ptr<TransportSniffer>( + new TransportSniffer(std::move(usb), serial_port)); } std::this_thread::sleep_for(1s); } diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h index c71c89766..246886890 100644 --- a/fastboot/fuzzy_fastboot/fixtures.h +++ b/fastboot/fuzzy_fastboot/fixtures.h @@ -31,7 +31,7 @@ #include "fastboot_driver.h" #include "extensions.h" -#include "usb_transport_sniffer.h" +#include "transport_sniffer.h" namespace fastboot { @@ -45,11 +45,14 @@ class FastBootTest : public testing::Test { static int serial_port; static std::string device_serial; static constexpr int MAX_USB_TRIES = 10; + static constexpr int MAX_TCP_TRIES = 6000; static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = ""); + static bool IsFastbootOverTcp(); bool UsbStillAvailible(); bool UserSpaceFastboot(); void ReconnectFastbootDevice(); + void ConnectTcpFastbootDevice(); protected: RetCode DownloadCommand(uint32_t size, std::string* response = nullptr, @@ -64,7 +67,7 @@ class FastBootTest : public testing::Test { void TearDownSerial(); void SetLockState(bool unlock, bool assert_change = true); - std::unique_ptr<UsbTransportSniffer> transport; + std::unique_ptr<TransportSniffer> transport; std::unique_ptr<FastBootDriver> fb; private: diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp index a1d69d272..b9784fef4 100644 --- a/fastboot/fuzzy_fastboot/main.cpp +++ b/fastboot/fuzzy_fastboot/main.cpp @@ -54,7 +54,7 @@ #include "extensions.h" #include "fixtures.h" #include "test_utils.h" -#include "usb_transport_sniffer.h" +#include "transport_sniffer.h" namespace fastboot { @@ -1756,16 +1756,19 @@ int main(int argc, char** argv) { } setbuf(stdout, NULL); // no buffering - printf("<Waiting for Device>\n"); - const auto matcher = [](usb_ifc_info* info) -> int { - return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial); - }; - Transport* transport = nullptr; - while (!transport) { - transport = usb_open(matcher); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + if (!fastboot::FastBootTest::IsFastbootOverTcp()) { + printf("<Waiting for Device>\n"); + const auto matcher = [](usb_ifc_info* info) -> int { + return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial); + }; + Transport* transport = nullptr; + while (!transport) { + transport = usb_open(matcher); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + transport->Close(); } - transport->Close(); if (args.find("serial_port") != args.end()) { fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at("serial_port")); diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp b/fastboot/fuzzy_fastboot/transport_sniffer.cpp index 7c595f47d..b55ffd31a 100644 --- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.cpp +++ b/fastboot/fuzzy_fastboot/transport_sniffer.cpp @@ -1,4 +1,4 @@ -#include "usb_transport_sniffer.h" +#include "transport_sniffer.h" #include <android-base/stringprintf.h> #include <sys/select.h> #include <sys/time.h> @@ -8,15 +8,15 @@ namespace fastboot { -UsbTransportSniffer::UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, +TransportSniffer::TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd) : transport_(std::move(transport)), serial_fd_(serial_fd) {} -UsbTransportSniffer::~UsbTransportSniffer() { +TransportSniffer::~TransportSniffer() { Close(); } -ssize_t UsbTransportSniffer::Read(void* data, size_t len) { +ssize_t TransportSniffer::Read(void* data, size_t len) { ProcessSerial(); ssize_t ret = transport_->Read(data, len); @@ -37,7 +37,7 @@ ssize_t UsbTransportSniffer::Read(void* data, size_t len) { return ret; } -ssize_t UsbTransportSniffer::Write(const void* data, size_t len) { +ssize_t TransportSniffer::Write(const void* data, size_t len) { ProcessSerial(); size_t ret = transport_->Write(data, len); @@ -58,11 +58,11 @@ ssize_t UsbTransportSniffer::Write(const void* data, size_t len) { return ret; } -int UsbTransportSniffer::Close() { +int TransportSniffer::Close() { return transport_->Close(); } -int UsbTransportSniffer::Reset() { +int TransportSniffer::Reset() { ProcessSerial(); int ret = transport_->Reset(); std::vector<char> buf; @@ -72,7 +72,7 @@ int UsbTransportSniffer::Reset() { return ret; } -const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() { +const std::vector<TransportSniffer::Event> TransportSniffer::Transfers() { return transfers_; } @@ -81,7 +81,7 @@ const std::vector<UsbTransportSniffer::Event> UsbTransportSniffer::Transfers() { * the failure. This method will look through its log of captured events, and * create a clean printable string of everything that happened. */ -std::string UsbTransportSniffer::CreateTrace() { +std::string TransportSniffer::CreateTrace() { std::string ret; const auto no_print = [](char c) -> bool { return !isprint(c); }; @@ -158,7 +158,7 @@ std::string UsbTransportSniffer::CreateTrace() { // This is a quick call to flush any UART logs the device might have sent // to our internal event log. It will wait up to 10ms for data to appear -void UsbTransportSniffer::ProcessSerial() { +void TransportSniffer::ProcessSerial() { if (serial_fd_ <= 0) return; fd_set set; diff --git a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h b/fastboot/fuzzy_fastboot/transport_sniffer.h index 8119aea39..2cbb9feb2 100644 --- a/fastboot/fuzzy_fastboot/usb_transport_sniffer.h +++ b/fastboot/fuzzy_fastboot/transport_sniffer.h @@ -42,12 +42,12 @@ namespace fastboot { /* A special class for sniffing reads and writes * * A useful debugging tool is to see the raw fastboot transactions going between - * the host and device. This class wraps the UsbTransport class, and snoops and saves + * the host and device. This class is a special subclass of Transport that snoops and saves * all the transactions going on. Additionally, if there is a console serial port * from the device, this class can monitor it as well and capture the interleaving of * transport transactions and UART log messages. */ -class UsbTransportSniffer : public UsbTransport { +class TransportSniffer : public Transport { public: enum EventType { READ, @@ -67,8 +67,8 @@ class UsbTransportSniffer : public UsbTransport { const std::vector<char> buf; }; - UsbTransportSniffer(std::unique_ptr<UsbTransport> transport, const int serial_fd = 0); - ~UsbTransportSniffer() override; + TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd = 0); + ~TransportSniffer() override; virtual ssize_t Read(void* data, size_t len) override; virtual ssize_t Write(const void* data, size_t len) override; @@ -81,7 +81,7 @@ class UsbTransportSniffer : public UsbTransport { private: std::vector<Event> transfers_; - std::unique_ptr<UsbTransport> transport_; + std::unique_ptr<Transport> transport_; const int serial_fd_; }; diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp index dd6fbf828..dca306f56 100644 --- a/fastboot/tcp.cpp +++ b/fastboot/tcp.cpp @@ -64,6 +64,7 @@ class TcpTransport : public Transport { ssize_t Read(void* data, size_t length) override; ssize_t Write(const void* data, size_t length) override; int Close() override; + int Reset() override; private: explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {} @@ -178,6 +179,10 @@ int TcpTransport::Close() { return result; } +int TcpTransport::Reset() { + return 0; +} + std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) { return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error), error); diff --git a/fastboot/transport.h b/fastboot/transport.h index 96b90d2fe..de0cc9272 100644 --- a/fastboot/transport.h +++ b/fastboot/transport.h @@ -36,6 +36,8 @@ class Transport { // Closes the underlying transport. Returns 0 on success. virtual int Close() = 0; + virtual int Reset() = 0; + // Blocks until the transport disconnects. Transports that don't support // this will return immediately. Returns 0 on success. virtual int WaitForDisconnect() { return 0; } diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp index 53fb3472f..308c96c05 100644 --- a/fastboot/udp.cpp +++ b/fastboot/udp.cpp @@ -109,6 +109,7 @@ class UdpTransport : public Transport { ssize_t Read(void* data, size_t length) override; ssize_t Write(const void* data, size_t length) override; int Close() override; + int Reset() override; private: explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {} @@ -370,6 +371,10 @@ int UdpTransport::Close() { return result; } +int UdpTransport::Reset() { + return 0; +} + std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) { return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error), error); diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 9e67c66b5..c67e33d02 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -75,6 +75,7 @@ filegroup { "snapshot.cpp", "snapshot_metadata_updater.cpp", "partition_cow_creator.cpp", + "return.cpp", "utility.cpp", ], } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/return.h b/fs_mgr/libsnapshot/include/libsnapshot/return.h new file mode 100644 index 000000000..dedc44501 --- /dev/null +++ b/fs_mgr/libsnapshot/include/libsnapshot/return.h @@ -0,0 +1,59 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include <stdint.h> +#include <string.h> + +#include <libfiemap/fiemap_status.h> + +namespace android::snapshot { + +// SnapshotManager functions return either bool or Return objects. "Return" types provides +// more information about the reason of the failure. +class Return { + using FiemapStatus = android::fiemap::FiemapStatus; + + public: + enum class ErrorCode : int32_t { + SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS), + ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR), + NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE), + }; + ErrorCode error_code() const { return error_code_; } + bool is_ok() const { return error_code() == ErrorCode::SUCCESS; } + operator bool() const { return is_ok(); } + // Total required size on /userdata. + uint64_t required_size() const { return required_size_; } + std::string string() const; + + static Return Ok() { return Return(ErrorCode::SUCCESS); } + static Return Error() { return Return(ErrorCode::ERROR); } + static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); } + // Does not set required_size_ properly even when status.error_code() == NO_SPACE. + explicit Return(const FiemapStatus& status) + : error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {} + + private: + ErrorCode error_code_; + uint64_t required_size_; + Return(ErrorCode error_code, uint64_t required_size = 0) + : error_code_(error_code), required_size_(required_size) {} + + // FiemapStatus::ErrorCode -> ErrorCode + static ErrorCode FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code); +}; + +} // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 61946f706..e503ec3a2 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -35,6 +35,7 @@ #include <update_engine/update_metadata.pb.h> #include <libsnapshot/auto_device.h> +#include <libsnapshot/return.h> #ifndef FRIEND_TEST #define FRIEND_TEST(test_set_name, individual_test) \ @@ -91,27 +92,6 @@ class SnapshotManager final { using FiemapStatus = android::fiemap::FiemapStatus; public: - // SnapshotManager functions return either bool or Return objects. "Return" types provides - // more information about the reason of the failure. - class Return : public FiemapStatus { - public: - // Total required size on /userdata. - uint64_t required_size() const { return required_size_; } - - static Return Ok() { return Return(FiemapStatus::ErrorCode::SUCCESS); } - static Return Error() { return Return(FiemapStatus::ErrorCode::ERROR); } - static Return NoSpace(uint64_t size) { - return Return(FiemapStatus::ErrorCode::NO_SPACE, size); - } - // Does not set required_size_ properly even when status.error_code() == NO_SPACE. - explicit Return(const FiemapStatus& status) : Return(status.error_code()) {} - - private: - uint64_t required_size_; - Return(FiemapStatus::ErrorCode code, uint64_t required_size = 0) - : FiemapStatus(code), required_size_(required_size) {} - }; - // Dependency injection for testing. class IDeviceInfo { public: diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h index 11de6edde..98bf56af6 100644 --- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h @@ -42,7 +42,6 @@ using chromeos_update_engine::PartitionUpdate; using testing::_; using testing::AssertionResult; using testing::NiceMock; -using testing::Return; using namespace android::storage_literals; using namespace std::string_literals; @@ -117,6 +116,7 @@ class TestDeviceInfo : public SnapshotManager::IDeviceInfo { class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher { public: SnapshotTestPropertyFetcher(const std::string& slot_suffix) { + using testing::Return; ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix)); ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _)) .WillByDefault(Return(true)); diff --git a/fs_mgr/libsnapshot/return.cpp b/fs_mgr/libsnapshot/return.cpp new file mode 100644 index 000000000..cc64af5cb --- /dev/null +++ b/fs_mgr/libsnapshot/return.cpp @@ -0,0 +1,44 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <libsnapshot/return.h> + +#include <string.h> + +using android::fiemap::FiemapStatus; + +namespace android::snapshot { + +std::string Return::string() const { + switch (error_code()) { + case ErrorCode::ERROR: + return "Error"; + case ErrorCode::SUCCESS: + [[fallthrough]]; + case ErrorCode::NO_SPACE: + return strerror(-static_cast<int>(error_code())); + } +} + +Return::ErrorCode Return::FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code) { + switch (error_code) { + case FiemapStatus::ErrorCode::SUCCESS: + case FiemapStatus::ErrorCode::ERROR: + case FiemapStatus::ErrorCode::NO_SPACE: + return static_cast<ErrorCode>(error_code); + default: + return ErrorCode::ERROR; + } +} +} // namespace android::snapshot diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 70a69a578..c731cb052 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -290,7 +290,7 @@ bool SnapshotManager::CreateSnapshot(LockedFile* lock, SnapshotStatus* status) { return true; } -SnapshotManager::Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) { +Return SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) { CHECK(lock); CHECK(lock->lock_mode() == LOCK_EX); if (!EnsureImageManager()) return Return::Error(); @@ -1249,15 +1249,45 @@ UpdateState SnapshotManager::GetUpdateState(double* progress) { return UpdateState::None; } - auto state = ReadUpdateState(lock.get()); - if (progress) { - *progress = 0.0; - if (state == UpdateState::Merging) { - // :TODO: When merging is implemented, set progress_val. - } else if (state == UpdateState::MergeCompleted) { - *progress = 100.0; - } + SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get()); + auto state = update_status.state(); + if (progress == nullptr) { + return state; + } + + if (state == UpdateState::MergeCompleted) { + *progress = 100.0; + return state; + } + + *progress = 0.0; + if (state != UpdateState::Merging) { + return state; } + + // Sum all the snapshot states as if the system consists of a single huge + // snapshots device, then compute the merge completion percentage of that + // device. + std::vector<std::string> snapshots; + if (!ListSnapshots(lock.get(), &snapshots)) { + LOG(ERROR) << "Could not list snapshots"; + return state; + } + + DmTargetSnapshot::Status fake_snapshots_status = {}; + for (const auto& snapshot : snapshots) { + DmTargetSnapshot::Status current_status; + + if (!QuerySnapshotStatus(snapshot, nullptr, ¤t_status)) continue; + + fake_snapshots_status.sectors_allocated += current_status.sectors_allocated; + fake_snapshots_status.total_sectors += current_status.total_sectors; + fake_snapshots_status.metadata_sectors += current_status.metadata_sectors; + } + + *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status, + update_status.sectors_allocated()); + return state; } @@ -1890,21 +1920,19 @@ static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) { } } -static SnapshotManager::Return AddRequiredSpace( - SnapshotManager::Return orig, - const std::map<std::string, SnapshotStatus>& all_snapshot_status) { - if (orig.error_code() != SnapshotManager::Return::ErrorCode::NO_SPACE) { +static Return AddRequiredSpace(Return orig, + const std::map<std::string, SnapshotStatus>& all_snapshot_status) { + if (orig.error_code() != Return::ErrorCode::NO_SPACE) { return orig; } uint64_t sum = 0; for (auto&& [name, status] : all_snapshot_status) { sum += status.cow_file_size(); } - return SnapshotManager::Return::NoSpace(sum); + return Return::NoSpace(sum); } -SnapshotManager::Return SnapshotManager::CreateUpdateSnapshots( - const DeltaArchiveManifest& manifest) { +Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) { auto lock = LockExclusive(); if (!lock) return Return::Error(); @@ -1998,7 +2026,7 @@ SnapshotManager::Return SnapshotManager::CreateUpdateSnapshots( return Return::Ok(); } -SnapshotManager::Return SnapshotManager::CreateUpdateSnapshotsInternal( +Return SnapshotManager::CreateUpdateSnapshotsInternal( LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator, AutoDeviceList* created_devices, std::map<std::string, SnapshotStatus>* all_snapshot_status) { @@ -2131,7 +2159,7 @@ SnapshotManager::Return SnapshotManager::CreateUpdateSnapshotsInternal( return Return::Ok(); } -SnapshotManager::Return SnapshotManager::InitializeUpdateSnapshots( +Return SnapshotManager::InitializeUpdateSnapshots( LockedFile* lock, MetadataBuilder* target_metadata, const LpMetadata* exported_target_metadata, const std::string& target_suffix, const std::map<std::string, SnapshotStatus>& all_snapshot_status) { @@ -2286,9 +2314,19 @@ UpdateState SnapshotManager::InitiateMergeAndWait() { } } + unsigned int last_progress = 0; + auto callback = [&]() -> void { + double progress; + GetUpdateState(&progress); + if (last_progress < static_cast<unsigned int>(progress)) { + last_progress = progress; + LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%."; + } + }; + LOG(INFO) << "Waiting for any previous merge request to complete. " << "This can take up to several minutes."; - auto state = ProcessUpdateState(); + auto state = ProcessUpdateState(callback); if (state == UpdateState::None) { LOG(INFO) << "Can't find any snapshot to merge."; return state; @@ -2300,7 +2338,8 @@ UpdateState SnapshotManager::InitiateMergeAndWait() { } // All other states can be handled by ProcessUpdateState. LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes."; - state = ProcessUpdateState(); + last_progress = 0; + state = ProcessUpdateState(callback); } LOG(INFO) << "Merge finished with state \"" << state << "\"."; diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index cea9d69f3..47ac474e2 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -1604,7 +1604,7 @@ TEST_F(SnapshotUpdateTest, LowSpace) { ASSERT_TRUE(sm->BeginUpdate()); auto res = sm->CreateUpdateSnapshots(manifest_); ASSERT_FALSE(res); - ASSERT_EQ(SnapshotManager::Return::ErrorCode::NO_SPACE, res.error_code()); + ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code()); ASSERT_GE(res.required_size(), 14_MiB); ASSERT_LT(res.required_size(), 15_MiB); } diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp index f01500f4d..3a64448b8 100644 --- a/fs_mgr/libsnapshot/utility.cpp +++ b/fs_mgr/libsnapshot/utility.cpp @@ -86,9 +86,7 @@ AutoDeleteSnapshot::~AutoDeleteSnapshot() { } } -SnapshotManager::Return InitializeCow(const std::string& device) { - using Return = SnapshotManager::Return; - +Return InitializeCow(const std::string& device) { // When the kernel creates a persistent dm-snapshot, it requires a CoW file // to store the modifications. The kernel interface does not specify how // the CoW is used, and there is no standard associated. diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h index 0453256a7..ad46090b9 100644 --- a/fs_mgr/libsnapshot/utility.h +++ b/fs_mgr/libsnapshot/utility.h @@ -111,7 +111,7 @@ std::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix( android::fs_mgr::MetadataBuilder* builder, const std::string& suffix); // Initialize a device before using it as the COW device for a dm-snapshot device. -SnapshotManager::Return InitializeCow(const std::string& device); +Return InitializeCow(const std::string& device); // "Atomically" write string to file. This is done by a series of actions: // 1. Write to path + ".tmp" diff --git a/init/init.cpp b/init/init.cpp index 5f97e443b..a25bf6cdf 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -730,8 +730,8 @@ int SecondStageMain(int argc, char** argv) { } am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups"); - am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict"); + am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux"); am.QueueEventTrigger("early-init"); // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev... diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp index b811622c5..1a474fbf3 100644 --- a/init/mount_namespace.cpp +++ b/init/mount_namespace.cpp @@ -107,7 +107,7 @@ static bool IsApexUpdatable() { } static Result<void> MountDir(const std::string& path, const std::string& mount_path) { - if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && ret != EEXIST) { + if (int ret = mkdir(mount_path.c_str(), 0755); ret != 0 && errno != EEXIST) { return ErrnoError() << "Could not create mount point " << mount_path; } if (mount(path.c_str(), mount_path.c_str(), nullptr, MS_BIND, nullptr) != 0) { diff --git a/init/security.cpp b/init/security.cpp index 586d0c72b..6cbe642c0 100644 --- a/init/security.cpp +++ b/init/security.cpp @@ -18,14 +18,19 @@ #include <errno.h> #include <fcntl.h> +#include <linux/perf_event.h> +#include <sys/ioctl.h> +#include <sys/syscall.h> #include <unistd.h> #include <fstream> #include <android-base/logging.h> +#include <android-base/properties.h> #include <android-base/unique_fd.h> using android::base::unique_fd; +using android::base::SetProperty; namespace android { namespace init { @@ -197,5 +202,61 @@ Result<void> SetKptrRestrictAction(const BuiltinArguments&) { return {}; } +// Test for whether the kernel has SELinux hooks for the perf_event_open() +// syscall. If the hooks are present, we can stop using the other permission +// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to +// control access to the syscall. The hooks are expected on all Android R +// release kernels, but might be absent on devices that upgrade while keeping an +// older kernel. +// +// There is no direct/synchronous way of finding out that a syscall failed due +// to SELinux. Therefore we test for a combination of a success and a failure +// that are explained by the platform's SELinux policy for the "init" domain: +// * cpu-scoped perf_event is allowed +// * ioctl() on the event fd is disallowed with EACCES +// +// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide +// perf_event_paranoid sysctl. +// +// If the SELinux hooks are detected, a special sysprop +// (sys.init.perf_lsm_hooks) is set, which translates to a modification of +// perf_event_paranoid (through init.rc sysprop actions). +// +// TODO(b/137092007): this entire test can be removed once the platform stops +// supporting kernels that precede the perf_event_open hooks (Android common +// kernels 4.4 and 4.9). +Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) { + // Use a trivial event that will be configured, but not started. + struct perf_event_attr pe = { + .type = PERF_TYPE_SOFTWARE, + .size = sizeof(struct perf_event_attr), + .config = PERF_COUNT_SW_TASK_CLOCK, + .disabled = 1, + .exclude_kernel = 1, + }; + + // Open the above event targeting cpu 0. (EINTR not possible.) + unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1, + /*cpu=*/0, + /*group_fd=*/-1, /*flags=*/0))); + if (fd == -1) { + PLOG(ERROR) << "Unexpected perf_event_open error"; + return {}; + } + + int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET); + if (ioctl_ret != -1) { + // Success implies that the kernel doesn't have the hooks. + return {}; + } else if (errno != EACCES) { + PLOG(ERROR) << "Unexpected perf_event ioctl error"; + return {}; + } + + // Conclude that the SELinux hooks are present. + SetProperty("sys.init.perf_lsm_hooks", "1"); + return {}; +} + } // namespace init } // namespace android diff --git a/init/security.h b/init/security.h index b081a0549..43c273969 100644 --- a/init/security.h +++ b/init/security.h @@ -29,6 +29,7 @@ namespace init { Result<void> MixHwrngIntoLinuxRngAction(const BuiltinArguments&); Result<void> SetMmapRndBitsAction(const BuiltinArguments&); Result<void> SetKptrRestrictAction(const BuiltinArguments&); +Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&); } // namespace init } // namespace android diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp index 2582cea21..f1147dc2b 100644 --- a/liblog/fake_log_device.cpp +++ b/liblog/fake_log_device.cpp @@ -517,13 +517,16 @@ void FakeClose() { } int __android_log_is_loggable(int prio, const char*, int def) { - int logLevel = def; - return logLevel >= 0 && prio >= logLevel; + int minimum_priority = __android_log_get_minimum_priority(); + if (minimum_priority == ANDROID_LOG_DEFAULT) { + return prio >= def; + } else { + return prio >= minimum_priority; + } } int __android_log_is_loggable_len(int prio, const char*, size_t, int def) { - int logLevel = def; - return logLevel >= 0 && prio >= logLevel; + return __android_log_is_loggable(prio, nullptr, def); } int __android_log_is_debuggable() { diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h index 7290789af..65307046b 100644 --- a/liblog/include/android/log.h +++ b/liblog/include/android/log.h @@ -55,6 +55,7 @@ */ #include <stdarg.h> +#include <stddef.h> #ifdef __cplusplus extern "C" { @@ -152,6 +153,12 @@ typedef enum log_id { } log_id_t; /** + * Let the logging function choose the best log target. + * This is not part of the enum since adding either -1 or 0xFFFFFFFF forces the enum to be signed or + * unsigned, which breaks unfortunately common arithmetic against LOG_ID_MIN and LOG_ID_MAX. */ +#define LOG_ID_DEFAULT -1 + +/** * Writes the constant string `text` to the log buffer `id`, * with priority `prio` and tag `tag`. * @@ -170,6 +177,101 @@ int __android_log_buf_write(int bufID, int prio, const char* tag, const char* te int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fmt, ...) __attribute__((__format__(printf, 4, 5))); +/** + * Logger data struct used for writing log messages to liblog via __android_log_write_logger_data() + * and sending log messages to user defined loggers specified in __android_log_set_logger(). + */ +struct __android_logger_data { + size_t struct_size; /* Must be set to sizeof(__android_logger_data) and is used for versioning. */ + int buffer_id; /* log_id_t or -1 to represent 'default'. */ + int priority; /* android_LogPriority values. */ + const char* tag; + const char* file; /* Optional file name, may be set to nullptr. */ + unsigned int line; /* Optional line number, ignore if file is nullptr. */ +}; + +/** + * Writes the log message specified with logger_data and msg to the log. logger_data includes + * additional file name and line number information that a logger may use. logger_data is versioned + * for backwards compatibility. + */ +void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg); + +/** + * Prototype for the 'logger' function that is called for every log message. + */ +typedef void (*__android_logger_function)(const struct __android_logger_data* logger_data, + const char* message); + +/** + * Sets a user defined logger function. All log messages sent to liblog will be set to the + * function pointer specified by logger for processing. + */ +void __android_log_set_logger(__android_logger_function logger); + +/** + * Writes the log message to logd. This is an __android_logger_function and can be provided to + * __android_log_set_logger(). It is the default logger when running liblog on a device. + */ +void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg); + +/** + * Writes the log message to stderr. This is an __android_logger_function and can be provided to + * __android_log_set_logger(). It is the default logger when running liblog on host. + */ +void __android_log_stderr_logger(const struct __android_logger_data* logger_data, + const char* message); + +/** + * Prototype for the 'abort' function that is called when liblog will abort due to + * __android_log_assert() failures. + */ +typedef void (*__android_aborter_function)(const char* abort_message); + +/** + * Sets a user defined aborter function that is called for __android_log_assert() failures. + */ +void __android_log_set_aborter(__android_aborter_function aborter); + +/** + * Calls the stored aborter function. This allows for other logging libraries to use the same + * aborter function by calling this function in liblog. + */ +void __android_log_call_aborter(const char* abort_message); + +/** + * Sets android_set_abort_message() on device then aborts(). This is the default aborter. + */ +void __android_log_default_aborter(const char* abort_message); + +/** + * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from + * __android_log_set_minimum_priority() to determine if a log message with a given prio and tag will + * be printed. A non-zero result indicates yes, zero indicates false. + * + * If both a priority for a tag and a minimum priority are set by + * __android_log_set_minimum_priority(), then the lowest of the two values are to determine the + * minimum priority needed to log. If only one is set, then that value is used to determine the + * minimum priority needed. If none are set, then default_priority is used. + * + * prio is ANDROID_LOG_VERBOSE to ANDROID_LOG_FATAL. + */ +int __android_log_is_loggable(int prio, const char* tag, int default_prio); +int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio); + +/** + * Sets the minimum priority that will be logged for this process. + * + * This returns the previous set minimum priority, or ANDROID_LOG_DEFAULT if none was set. + */ +int __android_log_set_minimum_priority(int priority); + +/** + * Gets the minimum priority that will be logged for this process. If none has been set by a + * previous __android_log_set_minimum_priority() call, this returns ANDROID_LOG_DEFAULT. + */ +int __android_log_get_minimum_priority(); + #ifdef __cplusplus } #endif diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt index 65194cedd..62b9805fc 100644 --- a/liblog/liblog.map.txt +++ b/liblog/liblog.map.txt @@ -67,7 +67,16 @@ LIBLOG_Q { # introduced=29 LIGLOG_R { # introduced=30 global: + __android_log_call_aborter; + __android_log_default_aborter; + __android_log_get_minimum_priority; + __android_log_logd_logger; __android_log_security_bswrite; # apex + __android_log_set_aborter; + __android_log_set_logger; + __android_log_set_minimum_priority; + __android_log_stderr_logger; + __android_log_write_logger_data; }; LIBLOG_PRIVATE { diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp index d6ef951f7..61b6fce2c 100644 --- a/liblog/logger_write.cpp +++ b/liblog/logger_write.cpp @@ -15,6 +15,7 @@ */ #include <errno.h> +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> @@ -23,11 +24,15 @@ #include <android/set_abort_message.h> #endif +#include <shared_mutex> + #include <android-base/macros.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> +#include "android/log.h" #include "logger.h" +#include "rwlock.h" #include "uio.h" #if (FAKE_LOG_DEVICE == 0) @@ -37,6 +42,14 @@ #include "fake_log_device.h" #endif +#if defined(__APPLE__) +#include <pthread.h> +#elif defined(__linux__) && !defined(__ANDROID__) +#include <syscall.h> +#elif defined(_WIN32) +#include <windows.h> +#endif + #define LOG_BUF_SIZE 1024 #if defined(__ANDROID__) @@ -93,6 +106,52 @@ void __android_log_close() { #endif } +static int minimum_log_priority = ANDROID_LOG_DEFAULT; +int __android_log_set_minimum_priority(int priority) { + int old_minimum_log_priority = minimum_log_priority; + minimum_log_priority = priority; + return old_minimum_log_priority; +} + +int __android_log_get_minimum_priority() { + return minimum_log_priority; +} + +#ifdef __ANDROID__ +static __android_logger_function logger_function = __android_log_logd_logger; +#else +static __android_logger_function logger_function = __android_log_stderr_logger; +#endif +static RwLock logger_function_lock; + +void __android_log_set_logger(__android_logger_function logger) { + auto lock = std::unique_lock{logger_function_lock}; + logger_function = logger; +} + +void __android_log_default_aborter(const char* abort_message) { +#ifdef __ANDROID__ + android_set_abort_message(abort_message); +#else + UNUSED(abort_message); +#endif + abort(); +} + +static __android_aborter_function aborter_function = __android_log_default_aborter; +static RwLock aborter_function_lock; + +void __android_log_set_aborter(__android_aborter_function aborter) { + auto lock = std::unique_lock{aborter_function_lock}; + aborter_function = aborter; +} + +void __android_log_call_aborter(const char* abort_message) { + auto lock = std::shared_lock{aborter_function_lock}; + aborter_function(abort_message); +} + +#ifdef __ANDROID__ static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) { int ret, save_errno; struct timespec ts; @@ -103,7 +162,6 @@ static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) { return -EINVAL; } -#if defined(__ANDROID__) clock_gettime(android_log_clockid(), &ts); if (log_id == LOG_ID_SECURITY) { @@ -137,51 +195,106 @@ static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr) { return -EPERM; } } + + ret = LogdWrite(log_id, &ts, vec, nr); + PmsgWrite(log_id, &ts, vec, nr); + + errno = save_errno; + return ret; +} #else - /* simulate clock_gettime(CLOCK_REALTIME, &ts); */ - { - struct timeval tv; - gettimeofday(&tv, NULL); - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - } +static int write_to_log(log_id_t, struct iovec*, size_t) { + // Non-Android text logs should go to __android_log_stderr_logger, not here. + // Non-Android binary logs are always dropped. + return 1; +} #endif - ret = 0; +// Copied from base/threads.cpp +static uint64_t GetThreadId() { +#if defined(__BIONIC__) + return gettid(); +#elif defined(__APPLE__) + uint64_t tid; + pthread_threadid_np(NULL, &tid); + return tid; +#elif defined(__linux__) + return syscall(__NR_gettid); +#elif defined(_WIN32) + return GetCurrentThreadId(); +#endif +} -#if (FAKE_LOG_DEVICE == 0) - ret = LogdWrite(log_id, &ts, vec, nr); - PmsgWrite(log_id, &ts, vec, nr); +void __android_log_stderr_logger(const struct __android_logger_data* logger_data, + const char* message) { + struct tm now; + time_t t = time(nullptr); + +#if defined(_WIN32) + localtime_s(&now, &t); #else - ret = FakeWrite(log_id, &ts, vec, nr); + localtime_r(&t, &now); #endif - errno = save_errno; - return ret; + char timestamp[32]; + strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now); + + static const char log_characters[] = "XXVDIWEF"; + static_assert(arraysize(log_characters) - 1 == ANDROID_LOG_SILENT, + "Mismatch in size of log_characters and values in android_LogPriority"); + int priority = + logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority; + char priority_char = log_characters[priority]; + uint64_t tid = GetThreadId(); + + if (logger_data->file != nullptr) { + fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", + logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(), + tid, logger_data->file, logger_data->line, message); + } else { + fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", + logger_data->tag ? logger_data->tag : "nullptr", priority_char, timestamp, getpid(), + tid, message); + } +} + +void __android_log_logd_logger(const struct __android_logger_data* logger_data, + const char* message) { + int buffer_id = logger_data->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : logger_data->buffer_id; + + struct iovec vec[3]; + vec[0].iov_base = + const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&logger_data->priority)); + vec[0].iov_len = 1; + vec[1].iov_base = const_cast<void*>(static_cast<const void*>(logger_data->tag)); + vec[1].iov_len = strlen(logger_data->tag) + 1; + vec[2].iov_base = const_cast<void*>(static_cast<const void*>(message)); + vec[2].iov_len = strlen(message) + 1; + + write_to_log(static_cast<log_id_t>(buffer_id), vec, 3); } int __android_log_write(int prio, const char* tag, const char* msg) { return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg); } -int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) { - if (!tag) tag = ""; +void __android_log_write_logger_data(__android_logger_data* logger_data, const char* msg) { + if (logger_data->tag == nullptr) logger_data->tag = ""; #if __BIONIC__ - if (prio == ANDROID_LOG_FATAL) { + if (logger_data->priority == ANDROID_LOG_FATAL) { android_set_abort_message(msg); } #endif - struct iovec vec[3]; - vec[0].iov_base = (unsigned char*)&prio; - vec[0].iov_len = 1; - vec[1].iov_base = (void*)tag; - vec[1].iov_len = strlen(tag) + 1; - vec[2].iov_base = (void*)msg; - vec[2].iov_len = strlen(msg) + 1; + auto lock = std::shared_lock{logger_function_lock}; + logger_function(logger_data, msg); +} - return write_to_log(static_cast<log_id_t>(bufID), vec, 3); +int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) { + __android_logger_data logger_data = {sizeof(__android_logger_data), bufID, prio, tag, nullptr, 0}; + __android_log_write_logger_data(&logger_data, msg); + return 1; } int __android_log_vprint(int prio, const char* tag, const char* fmt, va_list ap) { @@ -239,8 +352,8 @@ void __android_log_assert(const char* cond, const char* tag, const char* fmt, .. TEMP_FAILURE_RETRY(write(2, "\n", 1)); __android_log_write(ANDROID_LOG_FATAL, tag, buf); - abort(); /* abort so we have a chance to debug the situation */ - /* NOTREACHED */ + __android_log_call_aborter(buf); + abort(); } int __android_log_bwrite(int32_t tag, const void* payload, size_t len) { diff --git a/liblog/properties.cpp b/liblog/properties.cpp index 2b2327c58..a53c92b81 100644 --- a/liblog/properties.cpp +++ b/liblog/properties.cpp @@ -24,6 +24,8 @@ #include <sys/_system_properties.h> #include <unistd.h> +#include <algorithm> + #include <private/android_logger.h> static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER; @@ -87,7 +89,7 @@ static void refresh_cache(struct cache_char* cache, const char* key) { } } -static int __android_log_level(const char* tag, size_t len, int default_prio) { +static int __android_log_level(const char* tag, size_t len) { /* sizeof() is used on this array below */ static const char log_namespace[] = "persist.log.tag."; static const size_t base_offset = 8; /* skip "persist." */ @@ -256,20 +258,30 @@ static int __android_log_level(const char* tag, size_t len, int default_prio) { case 'F': /* FALLTHRU */ /* Not officially supported */ case 'A': return ANDROID_LOG_FATAL; case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */ - case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ + case 'S': return ANDROID_LOG_SILENT; /* clang-format on */ } - return default_prio; + return -1; } int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) { - int logLevel = __android_log_level(tag, len, default_prio); - return logLevel >= 0 && prio >= logLevel; + int minimum_log_priority = __android_log_get_minimum_priority(); + int property_log_level = __android_log_level(tag, len); + + if (property_log_level >= 0 && minimum_log_priority != ANDROID_LOG_DEFAULT) { + return prio >= std::min(property_log_level, minimum_log_priority); + } else if (property_log_level >= 0) { + return prio >= property_log_level; + } else if (minimum_log_priority != ANDROID_LOG_DEFAULT) { + return prio >= minimum_log_priority; + } else { + return prio >= default_prio; + } } int __android_log_is_loggable(int prio, const char* tag, int default_prio) { - int logLevel = __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio); - return logLevel >= 0 && prio >= logLevel; + auto len = tag ? strlen(tag) : 0; + return __android_log_is_loggable_len(prio, tag, len, default_prio); } int __android_log_is_debuggable() { diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp index f58c5240a..328895476 100644 --- a/liblog/tests/Android.bp +++ b/liblog/tests/Android.bp @@ -54,6 +54,7 @@ cc_defaults { ], srcs: [ "libc_test.cpp", + "liblog_global_state.cpp", "liblog_test.cpp", "log_id_test.cpp", "log_radio_test.cpp", diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp new file mode 100644 index 000000000..8d73bb886 --- /dev/null +++ b/liblog/tests/liblog_global_state.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "global_state_test_tag" + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/log.h> + +#include <gtest/gtest.h> + +TEST(liblog_global_state, libbase_logs_with_libbase_SetLogger) { + using namespace android::base; + bool message_seen = false; + LogSeverity expected_severity = WARNING; + std::string expected_file = Basename(__FILE__); + unsigned int expected_line; + std::string expected_message = "libbase test message"; + + auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file, + unsigned int line, const char* message) { + message_seen = true; + EXPECT_EQ(DEFAULT, log_id); + EXPECT_EQ(expected_severity, severity); + EXPECT_STREQ(LOG_TAG, tag); + EXPECT_EQ(expected_file, file); + EXPECT_EQ(expected_line, line); + EXPECT_EQ(expected_message, message); + }; + + SetLogger(LoggerFunction); + + expected_line = __LINE__ + 1; + LOG(expected_severity) << expected_message; + EXPECT_TRUE(message_seen); +} + +TEST(liblog_global_state, libbase_logs_with_liblog_set_logger) { + using namespace android::base; + // These must be static since they're used by the liblog logger function, which only accepts + // lambdas without captures. The items used by the libbase logger are explicitly not static, to + // ensure that lambdas with captures do work there. + static bool message_seen = false; + static std::string expected_file = Basename(__FILE__); + static unsigned int expected_line; + static std::string expected_message = "libbase test message"; + + auto liblog_logger_function = [](const struct __android_logger_data* logger_data, + const char* message) { + message_seen = true; + EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size); + EXPECT_EQ(LOG_ID_DEFAULT, logger_data->buffer_id); + EXPECT_EQ(ANDROID_LOG_WARN, logger_data->priority); + EXPECT_STREQ(LOG_TAG, logger_data->tag); + EXPECT_EQ(expected_file, logger_data->file); + EXPECT_EQ(expected_line, logger_data->line); + EXPECT_EQ(expected_message, message); + }; + + __android_log_set_logger(liblog_logger_function); + + expected_line = __LINE__ + 1; + LOG(WARNING) << expected_message; + EXPECT_TRUE(message_seen); +} + +TEST(liblog_global_state, liblog_logs_with_libbase_SetLogger) { + using namespace android::base; + bool message_seen = false; + std::string expected_message = "libbase test message"; + + auto LoggerFunction = [&](LogId log_id, LogSeverity severity, const char* tag, const char* file, + unsigned int line, const char* message) { + message_seen = true; + EXPECT_EQ(MAIN, log_id); + EXPECT_EQ(WARNING, severity); + EXPECT_STREQ(LOG_TAG, tag); + EXPECT_EQ(nullptr, file); + EXPECT_EQ(0U, line); + EXPECT_EQ(expected_message, message); + }; + + SetLogger(LoggerFunction); + + __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_WARN, LOG_TAG, expected_message.c_str()); + EXPECT_TRUE(message_seen); + message_seen = false; +} + +TEST(liblog_global_state, liblog_logs_with_liblog_set_logger) { + using namespace android::base; + // These must be static since they're used by the liblog logger function, which only accepts + // lambdas without captures. The items used by the libbase logger are explicitly not static, to + // ensure that lambdas with captures do work there. + static bool message_seen = false; + static int expected_buffer_id = LOG_ID_MAIN; + static int expected_priority = ANDROID_LOG_WARN; + static std::string expected_message = "libbase test message"; + + auto liblog_logger_function = [](const struct __android_logger_data* logger_data, + const char* message) { + message_seen = true; + EXPECT_EQ(sizeof(__android_logger_data), logger_data->struct_size); + EXPECT_EQ(expected_buffer_id, logger_data->buffer_id); + EXPECT_EQ(expected_priority, logger_data->priority); + EXPECT_STREQ(LOG_TAG, logger_data->tag); + EXPECT_STREQ(nullptr, logger_data->file); + EXPECT_EQ(0U, logger_data->line); + EXPECT_EQ(expected_message, message); + }; + + __android_log_set_logger(liblog_logger_function); + + __android_log_buf_write(expected_buffer_id, expected_priority, LOG_TAG, expected_message.c_str()); + EXPECT_TRUE(message_seen); +} + +TEST(liblog_global_state, SetAborter_with_liblog) { + using namespace android::base; + + std::string expected_message = "libbase test message"; + static bool message_seen = false; + auto aborter_function = [&](const char* message) { + message_seen = true; + EXPECT_EQ(expected_message, message); + }; + + SetAborter(aborter_function); + LOG(FATAL) << expected_message; + EXPECT_TRUE(message_seen); + message_seen = false; + + static std::string expected_message_static = "libbase test message"; + auto liblog_aborter_function = [](const char* message) { + message_seen = true; + EXPECT_EQ(expected_message_static, message); + }; + __android_log_set_aborter(liblog_aborter_function); + LOG(FATAL) << expected_message_static; + EXPECT_TRUE(message_seen); + message_seen = false; +} + +TEST(liblog_global_state, is_loggable_both_default) { + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); +} + +TEST(liblog_global_state, is_loggable_minimum_log_priority_only) { + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); +} + +TEST(liblog_global_state, is_loggable_tag_log_priority_only) { + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + auto log_tag_property = std::string("log.tag.") + LOG_TAG; + android::base::SetProperty(log_tag_property, "d"); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + android::base::SetProperty(log_tag_property, "w"); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + android::base::SetProperty(log_tag_property, ""); +} + +TEST(liblog_global_state, is_loggable_both_set) { + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + // When both a tag and a minimum priority are set, we use the lower value of the two. + + // tag = warning, minimum_priority = debug, expect 'debug' + auto log_tag_property = std::string("log.tag.") + LOG_TAG; + android::base::SetProperty(log_tag_property, "w"); + EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + // tag = warning, minimum_priority = warning, expect 'warning' + EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + // tag = debug, minimum_priority = warning, expect 'debug' + android::base::SetProperty(log_tag_property, "d"); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + // tag = debug, minimum_priority = debug, expect 'debug' + EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO)); + EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO)); + + android::base::SetProperty(log_tag_property, ""); +} diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp index c402e20fa..75a26bfc2 100644 --- a/liblog/tests/liblog_test.cpp +++ b/liblog/tests/liblog_test.cpp @@ -1074,7 +1074,6 @@ TEST(liblog, too_big_payload) { // Once we've found our expected entry, break. if (len == LOGGER_ENTRY_MAX_PAYLOAD - sizeof(big_payload_tag)) { - EXPECT_EQ(ret, len + static_cast<ssize_t>(sizeof(big_payload_tag))); *found = true; } }; @@ -1259,14 +1258,10 @@ TEST(liblog, is_loggable) { int level; char type; } levels[] = { - { ANDROID_LOG_VERBOSE, 'v' }, - { ANDROID_LOG_DEBUG, 'd' }, - { ANDROID_LOG_INFO, 'i' }, - { ANDROID_LOG_WARN, 'w' }, - { ANDROID_LOG_ERROR, 'e' }, - { ANDROID_LOG_FATAL, 'a' }, - { -1, 's' }, - { -2, 'g' }, // Illegal value, resort to default + {ANDROID_LOG_VERBOSE, 'v'}, {ANDROID_LOG_DEBUG, 'd'}, + {ANDROID_LOG_INFO, 'i'}, {ANDROID_LOG_WARN, 'w'}, + {ANDROID_LOG_ERROR, 'e'}, {ANDROID_LOG_FATAL, 'a'}, + {ANDROID_LOG_SILENT, 's'}, {-2, 'g'}, // Illegal value, resort to default }; // Set up initial test condition diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp index 9194cf3d0..2efd49c37 100644 --- a/libprocinfo/process.cpp +++ b/libprocinfo/process.cpp @@ -59,7 +59,6 @@ static ProcessState parse_state(const char* state) { case 'Z': return kProcessStateZombie; default: - LOG(ERROR) << "unknown process state: " << *state; return kProcessStateUnknown; } } diff --git a/libutils/StrongPointer_test.cpp b/libutils/StrongPointer_test.cpp index 153cf9683..7b2e37f27 100644 --- a/libutils/StrongPointer_test.cpp +++ b/libutils/StrongPointer_test.cpp @@ -56,3 +56,18 @@ TEST(StrongPointer, move) { } ASSERT_TRUE(isDeleted) << "foo was leaked!"; } + +TEST(StrongPointer, NullptrComparison) { + sp<SPFoo> foo; + ASSERT_EQ(foo, nullptr); + ASSERT_EQ(nullptr, foo); +} + +TEST(StrongPointer, PointerComparison) { + bool isDeleted; + sp<SPFoo> foo = new SPFoo(&isDeleted); + ASSERT_EQ(foo.get(), foo); + ASSERT_EQ(foo, foo.get()); + ASSERT_NE(nullptr, foo); + ASSERT_NE(foo, nullptr); +} diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h index 100e507db..6f4fb4788 100644 --- a/libutils/include/utils/StrongPointer.h +++ b/libutils/include/utils/StrongPointer.h @@ -27,43 +27,6 @@ template<typename T> class wp; // --------------------------------------------------------------------------- -// TODO: Maybe remove sp<> ? wp<> comparison? These are dangerous: If the wp<> -// was created before the sp<>, and they point to different objects, they may -// compare equal even if they are entirely unrelated. E.g. CameraService -// currently performa such comparisons. - -#define COMPARE_STRONG(_op_) \ -template<typename U> \ -inline bool operator _op_ (const sp<U>& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template<typename U> \ -inline bool operator _op_ (const U* o) const { \ - return m_ptr _op_ o; \ -} \ -/* Needed to handle type inference for nullptr: */ \ -inline bool operator _op_ (const T* o) const { \ - return m_ptr _op_ o; \ -} - -template<template<typename C> class comparator, typename T, typename U> -static inline bool _sp_compare_(T* a, U* b) { - return comparator<typename std::common_type<T*, U*>::type>()(a, b); -} - -// Use std::less and friends to avoid undefined behavior when ordering pointers -// to different objects. -#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \ -template<typename U> \ -inline bool operator _op_ (const sp<U>& o) const { \ - return _sp_compare_<_compare_>(m_ptr, o.m_ptr); \ -} \ -template<typename U> \ -inline bool operator _op_ (const U* o) const { \ - return _sp_compare_<_compare_>(m_ptr, o); \ -} -// --------------------------------------------------------------------------- - template<typename T> class sp { public: @@ -102,15 +65,6 @@ public: inline T* get() const { return m_ptr; } inline explicit operator bool () const { return m_ptr != nullptr; } - // Operators - - COMPARE_STRONG(==) - COMPARE_STRONG(!=) - COMPARE_STRONG_FUNCTIONAL(>, std::greater) - COMPARE_STRONG_FUNCTIONAL(<, std::less) - COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal) - COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal) - // Punt these to the wp<> implementation. template<typename U> inline bool operator == (const wp<U>& o) const { @@ -130,13 +84,69 @@ private: T* m_ptr; }; -// For code size reasons, we do not want these inlined or templated. -void sp_report_race(); -void sp_report_stack_pointer(); +#define COMPARE_STRONG(_op_) \ + template <typename T, typename U> \ + static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \ + return t.get() _op_ u.get(); \ + } \ + template <typename T, typename U> \ + static inline bool operator _op_(const T* t, const sp<U>& u) { \ + return t _op_ u.get(); \ + } \ + template <typename T, typename U> \ + static inline bool operator _op_(const sp<T>& t, const U* u) { \ + return t.get() _op_ u; \ + } \ + template <typename T> \ + static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \ + return t.get() _op_ nullptr; \ + } \ + template <typename T> \ + static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \ + return nullptr _op_ t.get(); \ + } + +template <template <typename C> class comparator, typename T, typename U> +static inline bool _sp_compare_(T* a, U* b) { + return comparator<typename std::common_type<T*, U*>::type>()(a, b); +} + +#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_) \ + template <typename T, typename U> \ + static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \ + return _sp_compare_<_compare_>(t.get(), u.get()); \ + } \ + template <typename T, typename U> \ + static inline bool operator _op_(const T* t, const sp<U>& u) { \ + return _sp_compare_<_compare_>(t, u.get()); \ + } \ + template <typename T, typename U> \ + static inline bool operator _op_(const sp<T>& t, const U* u) { \ + return _sp_compare_<_compare_>(t.get(), u); \ + } \ + template <typename T> \ + static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \ + return _sp_compare_<_compare_>(t.get(), nullptr); \ + } \ + template <typename T> \ + static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \ + return _sp_compare_<_compare_>(nullptr, t.get()); \ + } + +COMPARE_STRONG(==) +COMPARE_STRONG(!=) +COMPARE_STRONG_FUNCTIONAL(>, std::greater) +COMPARE_STRONG_FUNCTIONAL(<, std::less) +COMPARE_STRONG_FUNCTIONAL(<=, std::less_equal) +COMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal) #undef COMPARE_STRONG #undef COMPARE_STRONG_FUNCTIONAL +// For code size reasons, we do not want these inlined or templated. +void sp_report_race(); +void sp_report_stack_pointer(); + // --------------------------------------------------------------------------- // No user serviceable parts below here. diff --git a/rootdir/init.rc b/rootdir/init.rc index 0b8990930..6a6169c8a 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -945,14 +945,33 @@ on property:sys.sysctl.extra_free_kbytes=* on property:sys.sysctl.tcp_def_init_rwnd=* write /proc/sys/net/ipv4/tcp_default_init_rwnd ${sys.sysctl.tcp_def_init_rwnd} -on property:security.perf_harden=0 +# perf_event_open syscall security: +# Newer kernels have the ability to control the use of the syscall via SELinux +# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the +# kernel has the hooks. In this case, the system-wide perf_event_paranoid +# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for +# controlling access. On older kernels, the paranoid value is the only means of +# controlling access. It is normally 3 (allow only root), but the shell user +# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden. +on property:sys.init.perf_lsm_hooks=1 + write /proc/sys/kernel/perf_event_paranoid -1 +on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks="" write /proc/sys/kernel/perf_event_paranoid 1 +on property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks="" + write /proc/sys/kernel/perf_event_paranoid 3 + +# Additionally, simpleperf profiler uses debug.* and security.perf_harden +# sysprops to be able to indirectly set these sysctls. +on property:security.perf_harden=0 write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000} write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25} write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516} - +# Default values. on property:security.perf_harden=1 - write /proc/sys/kernel/perf_event_paranoid 3 + write /proc/sys/kernel/perf_event_max_sample_rate 100000 + write /proc/sys/kernel/perf_cpu_time_max_percent 25 + write /proc/sys/kernel/perf_event_mlock_kb 516 + # on shutdown # In device's init.rc, this trigger can be used to do device-specific actions diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md index d391cc17c..3bee87507 100644 --- a/shell_and_utilities/README.md +++ b/shell_and_utilities/README.md @@ -218,25 +218,28 @@ BSD: fsck\_msdos newfs\_msdos bzip2: bzcat bzip2 bunzip2 +gavinhoward/bc: bc + one-true-awk: awk toolbox: getevent getprop setprop start stop -toybox: 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 +toybox: acpi base64 basename 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 readlink realpath renice restorecon rev -rfkill rm rmdir rmmod 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 -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 +printf prlimit ps pwd pwdx readelf readlink realpath renice restorecon +rev rfkill rm rmdir rmmod 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 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 diff --git a/trusty/confirmationui/.clang-format b/trusty/confirmationui/.clang-format new file mode 100644 index 000000000..b0dc94c1d --- /dev/null +++ b/trusty/confirmationui/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: true +IndentCaseLabels: false +ColumnLimit: 100 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2 diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp new file mode 100644 index 000000000..60e0e71b2 --- /dev/null +++ b/trusty/confirmationui/Android.bp @@ -0,0 +1,95 @@ +// Copyright (C) 2020 The Android Open-Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// WARNING: Everything listed here will be built on ALL platforms, +// including x86, the emulator, and the SDK. Modules must be uniquely +// named (liblights.panda), and must build everywhere, or limit themselves +// to only building on ARM if they include assembly. Individual makefiles +// are responsible for having their own logic, for fine-grained control. + +cc_binary { + name: "android.hardware.confirmationui@1.0-service.trusty", + relative_install_path: "hw", + vendor: true, + shared_libs: [ + "android.hardware.confirmationui@1.0", + "android.hardware.confirmationui.not-so-secure-input", + "android.hardware.confirmationui@1.0-lib.trusty", + "libbase", + "libhidlbase", + "libutils", + ], + + init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"], + + vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"], + + srcs: [ + "service.cpp", + ], + + cflags: [ + "-Wall", + "-Werror", + "-DTEEUI_USE_STD_VECTOR", + ], +} + +cc_library { + name: "android.hardware.confirmationui@1.0-lib.trusty", + vendor: true, + shared_libs: [ + "android.hardware.confirmationui@1.0", + "android.hardware.keymaster@4.0", + "libbase", + "libhidlbase", + "libteeui_hal_support", + "libtrusty", + "libutils", + ], + + export_include_dirs: ["include"], + + srcs: [ + "TrustyApp.cpp", + "TrustyConfirmationUI.cpp", + ], + + cflags: [ + "-Wall", + "-Werror", + "-DTEEUI_USE_STD_VECTOR", + ], +} + +cc_library { + name: "android.hardware.confirmationui.not-so-secure-input", + vendor: true, + shared_libs: [ + "libbase", + "libcrypto", + "libteeui_hal_support", + ], + + srcs: [ + "NotSoSecureInput.cpp", + ], + + cflags: [ + "-Wall", + "-Werror", + "-DTEEUI_USE_STD_VECTOR", + ], +}
\ No newline at end of file diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp new file mode 100644 index 000000000..3d9a2d6e5 --- /dev/null +++ b/trusty/confirmationui/NotSoSecureInput.cpp @@ -0,0 +1,207 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <endian.h> +#include <memory> +#include <openssl/hmac.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +#include <secure_input/evdev.h> +#include <secure_input/secure_input_device.h> +#include <teeui/utils.h> + +#include <initializer_list> + +using namespace secure_input; + +using teeui::AuthTokenKey; +using teeui::ByteBufferProxy; +using teeui::Hmac; +using teeui::optional; +using teeui::ResponseCode; +using teeui::TestKeyBits; + +constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE)); + +class SecureInputHMacer { + public: + static optional<Hmac> hmac256(const AuthTokenKey& key, + std::initializer_list<ByteBufferProxy> buffers) { + HMAC_CTX hmacCtx; + HMAC_CTX_init(&hmacCtx); + if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) { + return {}; + } + for (auto& buffer : buffers) { + if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) { + return {}; + } + } + Hmac result; + if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) { + return {}; + } + return result; + } +}; + +using HMac = teeui::HMac<SecureInputHMacer>; + +Nonce generateNonce() { + /* + * Completely random nonce. + * Running the secure input protocol from the HAL service is not secure + * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So + * using a constant "nonce" here does not weaken security. If this code runs + * on a truly trustworthy source of input events this function needs to return + * hight entropy nonces. + * As of this writing the call to RAND_bytes is commented, because the + * emulator this HAL service runs on does not have a good source of entropy. + * It would block the call to RAND_bytes indefinitely. + */ + Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}; + // RAND_bytes(result.data(), result.size()); + return result; +} + +/** + * This is an implementation of the SecureInput protocol in unserspace. This is + * just an example and should not be used as is. The protocol implemented her + * should be used by a trusted input device that can assert user events with + * high assurance even if the HLOS kernel is compromised. A confirmationui HAL + * that links directly against this implementation is not secure and shal not be + * used on a production device. + */ +class NotSoSecureInput : public SecureInput { + public: + NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb, + InputResultCb inputResultCb) + : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb}, + inputResultCb_{inputResultCb}, discardEvents_{true} {} + + operator bool() const override { return true; } + + void handleEvent(const EventDev& evdev) override { + bool gotEvent; + input_event evt; + std::tie(gotEvent, evt) = evdev.readEvent(); + while (gotEvent) { + if (!(discardEvents_) && evt.type == EV_KEY && + (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) && + evt.value == 1) { + DTupKeyEvent event = DTupKeyEvent::RESERVED; + + // Translate the event code into DTupKeyEvent which the TA understands. + switch (evt.code) { + case KEY_POWER: + event = DTupKeyEvent::PWR; + break; + case KEY_VOLUMEDOWN: + event = DTupKeyEvent::VOL_DOWN; + break; + case KEY_VOLUMEUP: + event = DTupKeyEvent::VOL_UP; + break; + } + + // The event goes into the HMAC in network byte order. + uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event)); + auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel, + teeui::bytesCast(keyEventBE), nCi_); + + teeui::ResponseCode rc; + InputResponse ir; + auto response = std::tie(rc, ir); + if (event != DTupKeyEvent::RESERVED) { + response = deliverEventCb_(event, *signature); + if (rc != ResponseCode::OK) { + LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc); + inputResultCb_(rc); + } else { + switch (ir) { + case InputResponse::OK: + inputResultCb_(rc); + break; + case InputResponse::PENDING_MORE: + rc = performDTUPHandshake(); + if (rc != ResponseCode::OK) { + inputResultCb_(rc); + } + break; + case InputResponse::TIMED_OUT: + inputResultCb_(rc); + break; + } + } + } + } + std::tie(gotEvent, evt) = evdev.readEvent(); + } + } + + void start() override { + auto rc = performDTUPHandshake(); + if (rc != ResponseCode::OK) { + inputResultCb_(rc); + } + discardEvents_ = false; + }; + + private: + teeui::ResponseCode performDTUPHandshake() { + ResponseCode rc; + LOG(INFO) << "Start handshake"; + Nonce nCo; + std::tie(rc, nCo) = hsBeginCb_(); + if (rc != ResponseCode::OK) { + LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")"; + return rc; + } + + nCi_ = generateNonce(); + rc = + hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_); + + if (rc != ResponseCode::OK) { + LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")"; + return rc; + } + return ResponseCode::OK; + } + + HsBeginCb hsBeginCb_; + HsFinalizeCb hsFinalizeCb_; + DeliverEventCb deliverEventCb_; + InputResultCb inputResultCb_; + + std::atomic_bool discardEvents_; + Nonce nCi_; +}; + +namespace secure_input { + +std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb, + SecureInput::HsFinalizeCb hsFinalizeCb, + SecureInput::DeliverEventCb deliverEventCb, + SecureInput::InputResultCb inputResultCb) { + return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb, + inputResultCb); +} + +} // namespace secure_input diff --git a/trusty/confirmationui/README b/trusty/confirmationui/README new file mode 100644 index 000000000..45d4e7650 --- /dev/null +++ b/trusty/confirmationui/README @@ -0,0 +1,20 @@ +## Secure UI Architecture + +To implement confirmationui a secure UI architecture is required. This entails a way +to display the confirmation dialog driven by a reduced trusted computing base, typically +a trusted execution environment (TEE), without having to rely on Linux and the Android +system for integrity and authenticity of input events. This implementation provides +neither. But it provides most of the functionlity required to run a full Android Protected +Confirmation feature when integrated into a secure UI architecture. + +## Secure input (NotSoSecureInput) + +This implementation does not provide any security guaranties. +The input method (NotSoSecureInput) runs a cryptographic protocols that is +sufficiently secure IFF the end point is implemented on a trustworthy +secure input device. But since the endpoint is currently in the HAL +service itself this implementation is not secure. + +NOTE that a secure input device end point needs a good source of entropy +for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce) +uses a constant nonce.
\ No newline at end of file diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp new file mode 100644 index 000000000..e4c68f955 --- /dev/null +++ b/trusty/confirmationui/TrustyApp.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TrustyApp.h" + +#include <android-base/logging.h> +#include <sys/uio.h> +#include <trusty/tipc.h> + +namespace android { +namespace trusty { + +// 0x1000 is the message buffer size but we need to leave some space for a protocol header. +// This assures that packets can always be read/written in one read/write operation. +static constexpr const uint32_t kPacketSize = 0x1000 - 32; + +enum class PacketType : uint32_t { + SND, + RCV, + ACK, +}; + +struct PacketHeader { + PacketType type; + uint32_t remaining; +}; + +const char* toString(PacketType t) { + switch (t) { + case PacketType::SND: + return "SND"; + case PacketType::RCV: + return "RCV"; + case PacketType::ACK: + return "ACK"; + default: + return "UNKNOWN"; + } +} + +static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader); +static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize; + +ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, + uint8_t* iend) { + while (obegin != oend) { + PacketHeader header = { + .type = PacketType::SND, + .remaining = uint32_t(oend - obegin), + }; + uint32_t body_size = std::min(kPayloadSize, header.remaining); + iovec iov[] = { + { + .iov_base = &header, + .iov_len = kHeaderSize, + }, + { + .iov_base = const_cast<uint8_t*>(obegin), + .iov_len = body_size, + }, + }; + int rc = writev(handle, iov, 2); + if (!rc) { + PLOG(ERROR) << "Error sending SND message. " << rc; + return rc; + } + + obegin += body_size; + + rc = read(handle, &header, kHeaderSize); + if (!rc) { + PLOG(ERROR) << "Error reading ACK. " << rc; + return rc; + } + + if (header.type != PacketType::ACK || header.remaining != oend - obegin) { + LOG(ERROR) << "malformed ACK"; + return -1; + } + } + + ssize_t remaining = 0; + auto begin = ibegin; + do { + PacketHeader header = { + .type = PacketType::RCV, + .remaining = 0, + }; + + iovec iov[] = { + { + .iov_base = &header, + .iov_len = kHeaderSize, + }, + { + .iov_base = begin, + .iov_len = uint32_t(iend - begin), + }, + }; + + ssize_t rc = writev(handle, iov, 1); + if (!rc) { + PLOG(ERROR) << "Error sending RCV message. " << rc; + return rc; + } + + rc = readv(handle, iov, 2); + if (rc < 0) { + PLOG(ERROR) << "Error reading response. " << rc; + return rc; + } + + uint32_t body_size = std::min(kPayloadSize, header.remaining); + if (body_size != rc - kHeaderSize) { + LOG(ERROR) << "Unexpected amount of data: " << rc; + return -1; + } + + remaining = header.remaining - body_size; + begin += body_size; + } while (remaining); + + return begin - ibegin; +} + +TrustyApp::TrustyApp(const std::string& path, const std::string& appname) + : handle_(kInvalidHandle) { + handle_ = tipc_connect(path.c_str(), appname.c_str()); + if (handle_ == kInvalidHandle) { + LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:" + << "\"" << path << "\""; + } + LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\""; +} +TrustyApp::~TrustyApp() { + if (handle_ != kInvalidHandle) { + tipc_close(handle_); + } + LOG(INFO) << "Done shutting down TrustyApp"; +} + +} // namespace trusty +} // namespace android diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h new file mode 100644 index 000000000..05a25f61d --- /dev/null +++ b/trusty/confirmationui/TrustyApp.h @@ -0,0 +1,155 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/logging.h> +#include <errno.h> +#include <poll.h> +#include <stdio.h> +#include <sys/eventfd.h> +#include <sys/stat.h> +#include <teeui/msg_formatting.h> +#include <trusty/tipc.h> +#include <unistd.h> + +#include <fstream> +#include <functional> +#include <future> +#include <iostream> +#include <sstream> +#include <thread> +#include <vector> + +#define AT __FILE__ ":" << __LINE__ << ": " + +namespace android { +namespace trusty { + +using ::teeui::Message; +using ::teeui::msg2tuple_t; +using ::teeui::ReadStream; +using ::teeui::WriteStream; + +#ifndef TEEUI_USE_STD_VECTOR +/* + * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and + * teeui::MsgVector be aliases for std::vector. This is required for thread safe + * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in + * CFLAGS of the HAL service. + */ +#error "Must be compiled with -DTEEUI_USE_STD_VECTOR." +#endif + +enum class TrustyAppError : int32_t { + OK, + ERROR = -1, + MSG_TOO_LONG = -2, +}; + +/* + * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol + * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign + * mode of operation. + */ +static constexpr const size_t kSendBufferSize = 0x2000; + +ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, + uint8_t* iend); + +class TrustyApp { + private: + int handle_; + static constexpr const int kInvalidHandle = -1; + /* + * This mutex serializes communication with the trusted app, not handle_. + * Calling issueCmd during construction or deletion is undefined behavior. + */ + std::mutex mutex_; + + public: + TrustyApp(const std::string& path, const std::string& appname); + ~TrustyApp(); + + template <typename Request, typename Response, typename... T> + std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) { + std::lock_guard<std::mutex> lock(mutex_); + + if (handle_ == kInvalidHandle) { + LOG(ERROR) << "TrustyApp not connected"; + return {TrustyAppError::ERROR, {}}; + } + + uint8_t buffer[kSendBufferSize]; + WriteStream out(buffer); + + out = write(Request(), out, args...); + if (!out) { + LOG(ERROR) << AT << "send command failed: message formatting"; + return {TrustyAppError::MSG_TOO_LONG, {}}; + } + + auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0], + &buffer[kSendBufferSize]); + if (rc < 0) return {TrustyAppError::ERROR, {}}; + + ReadStream in(&buffer[0], rc); + auto result = read(Response(), in); + if (!std::get<0>(result)) { + LOG(ERROR) << "send command failed: message parsing"; + return {TrustyAppError::ERROR, {}}; + } + + return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR, + tuple_tail(std::move(result))}; + } + + template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) { + std::lock_guard<std::mutex> lock(mutex_); + + if (handle_ == kInvalidHandle) { + LOG(ERROR) << "TrustyApp not connected"; + return TrustyAppError::ERROR; + } + + uint8_t buffer[kSendBufferSize]; + WriteStream out(buffer); + + out = write(Request(), out, args...); + if (!out) { + LOG(ERROR) << AT << "send command failed: message formatting"; + return TrustyAppError::MSG_TOO_LONG; + } + + auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0], + &buffer[kSendBufferSize]); + if (rc < 0) { + LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")"; + return TrustyAppError::ERROR; + } + + if (rc > 0) { + LOG(ERROR) << "Unexpected non zero length response"; + return TrustyAppError::ERROR; + } + return TrustyAppError::OK; + } + + operator bool() const { return handle_ != kInvalidHandle; } +}; + +} // namespace trusty +} // namespace android diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp new file mode 100644 index 000000000..6b25893b5 --- /dev/null +++ b/trusty/confirmationui/TrustyConfirmationUI.cpp @@ -0,0 +1,513 @@ +/* + * + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TrustyConfirmationUI.h" + +#include <android-base/logging.h> +#include <android/hardware/confirmationui/1.0/types.h> +#include <android/hardware/keymaster/4.0/types.h> +#include <fcntl.h> +#include <linux/input.h> +#include <poll.h> +#include <pthread.h> +#include <secure_input/evdev.h> +#include <secure_input/secure_input_device.h> +#include <secure_input/secure_input_proto.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <teeui/msg_formatting.h> +#include <teeui/utils.h> +#include <time.h> + +#include <atomic> +#include <functional> +#include <memory> +#include <thread> +#include <tuple> +#include <vector> + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +using namespace secure_input; + +using ::android::trusty::TrustyAppError; + +using ::teeui::AbortMsg; +using ::teeui::DeliverTestCommandMessage; +using ::teeui::DeliverTestCommandResponse; +using ::teeui::FetchConfirmationResult; +using ::teeui::MsgString; +using ::teeui::MsgVector; +using ::teeui::PromptUserConfirmationMsg; +using ::teeui::PromptUserConfirmationResponse; +using ::teeui::ResultMsg; + +using ::secure_input::createSecureInput; + +using ::android::hardware::keymaster::V4_0::HardwareAuthToken; + +using ::std::tie; + +using TeeuiRc = ::teeui::ResponseCode; + +constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0"; +constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui"; + +namespace { + +class Finalize { + private: + std::function<void()> f_; + + public: + Finalize(std::function<void()> f) : f_(f) {} + ~Finalize() { + if (f_) f_(); + } + void release() { f_ = {}; } +}; + +ResponseCode convertRc(TeeuiRc trc) { + static_assert( + uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) && + uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) && + uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) && + uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) && + uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) && + uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) && + uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) && + uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) && + uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) && + uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) && + uint32_t(TeeuiRc::UIErrorMessageTooLong) == + uint32_t(ResponseCode::UIErrorMessageTooLong) && + uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) == + uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding), + "teeui::ResponseCode and " + "::android::hardware::confirmationui::V1_0::Responsecude are out of " + "sync"); + return ResponseCode(trc); +} + +teeui::UIOption convertUIOption(UIOption uio) { + static_assert(uint32_t(UIOption::AccessibilityInverted) == + uint32_t(teeui::UIOption::AccessibilityInverted) && + uint32_t(UIOption::AccessibilityMagnified) == + uint32_t(teeui::UIOption::AccessibilityMagnified), + "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption " + "anre out of sync"); + return teeui::UIOption(uio); +} + +inline MsgString hidl2MsgString(const hidl_string& s) { + return {s.c_str(), s.c_str() + s.size()}; +} +template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) { + return {v}; +} + +inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) { + MsgVector<teeui::UIOption> result(v.size()); + for (unsigned int i = 0; i < v.size(); ++i) { + result[i] = convertUIOption(v[i]); + } + return result; +} + +} // namespace + +TrustyConfirmationUI::TrustyConfirmationUI() + : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {} + +TrustyConfirmationUI::~TrustyConfirmationUI() { + ListenerState state = listener_state_; + if (state == ListenerState::SetupDone || state == ListenerState::Interactive) { + abort(); + } + if (state != ListenerState::None) { + callback_thread_.join(); + } +} + +std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> +TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText, + const MsgVector<uint8_t>& extraData, + const MsgString& locale, + const MsgVector<teeui::UIOption>& uiOptions) { + std::unique_lock<std::mutex> stateLock(listener_state_lock_); + /* + * This is the main listener thread function. The listener thread life cycle + * is equivalent to the life cycle of a single confirmation request. The life + * cycle is devided in four phases. + * * The starting phase: + * * The Trusted App gets loaded and/or the connection to it gets established. + * * A connection to the secure input device is established. + * * The prompt is initiated. This sends all information required by the + * confirmation dialog to the TA. The dialog is not yet displayed. + * * An event loop is created. + * * The event loop listens for user input events, fetches them from the + * secure input device, and delivers them to the TA. + * * All evdev devices are grabbed to give confirmationui exclusive access + * to user input. + * + * Note: During the starting phase the hwbinder service thread is blocked and + * waiting for possible Errors. If the setup phase concludes sucessfully, the + * hwbinder service thread gets unblocked and returns successfully. Errors + * that occur after the first phase are delivered by callback interface. + * + * * The 2nd phase - non interactive phase + * * The event loop thread is started. + * * After a grace period: + * * A handshake between the secure input device SecureInput and the TA + * is performed. + * * The input event handler are armed to process user input events. + * + * * The 3rd phase - interactive phase + * * We wait to any external event + * * Abort + * * Secure user input asserted + * * Secure input delivered (for non interactive VTS testing) + * * The result is fetched from the TA. + * + * * The 4th phase - cleanup + * The cleanup phase is given by the scope of automatic variables created + * in this function. The cleanup commences in reverse order of their creation. + * Here is a list of more complex items in the order in which they go out of + * scope + * * finalizeSecureTouch - signals and joins the secure touch thread. + * * eventloop - signals and joins the event loop thread. The event + * handlers also own all EventDev instances which ungrab the event devices. + * When the eventloop goes out of scope the EventDevs get destroyed + * relinquishing the exclusive hold on the event devices. + * * finalizeConfirmationPrompt - calls abort on the TA, making sure a + * pending operation gets canceled. If the prompt concluded successfully this + * is a spurious call but semantically a no op. + * * secureInput - shuts down the connection to the secure input device + * SecureInput. + * * app - disconnects the TA. Since app is a shared pointer this may not + * unload the app here. It is possible that more instances of the shared + * pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and + * TrustyConfirmationUI::abort. But these instances are extremely short lived + * and it is safe if they are destroyed by either. + * * stateLock - unlocks the listener_state_lock_ if it happens to be held + * at the time of return. + */ + + std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result; + TeeuiRc& rc = std::get<TeeuiRc>(result); + rc = TeeuiRc::SystemError; + + listener_state_ = ListenerState::Starting; + + auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName); + if (!app) return result; // TeeuiRc::SystemError + + app_ = app; + + auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> { + auto [error, result] = + app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>(); + auto& [rc, nCo] = result; + + if (error != TrustyAppError::OK || rc != TeeuiRc::OK) { + LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/" + << uint32_t(rc) << ")"; + rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc; + } + return result; + }; + + auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc { + auto [error, finalizeResponse] = + app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>( + nCi, sig); + auto& [rc] = finalizeResponse; + if (error != TrustyAppError::OK || rc != TeeuiRc::OK) { + LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/" + << uint32_t(rc) << ")"; + rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc; + } + return rc; + }; + + auto deliverInput = [&](DTupKeyEvent event, + const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> { + auto [error, result] = + app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig); + auto& [rc, ir] = result; + if (error != TrustyAppError::OK) { + LOG(ERROR) << "Failed to deliver input command"; + rc = TeeuiRc::SystemError; + } + return result; + }; + + std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending; + auto inputResult = [&](TeeuiRc rc) { + TeeuiRc expected = TeeuiRc::OperationPending; + if (eventRC.compare_exchange_strong(expected, rc)) { + listener_state_condv_.notify_all(); + } + }; + + // create Secure Input device. + auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult); + if (!secureInput || !(*secureInput)) { + LOG(ERROR) << "Failed to open secure input device"; + return result; // TeeuiRc::SystemError; + } + + Finalize finalizeConfirmationPrompt([app] { + LOG(INFO) << "Calling abort for cleanup"; + app->issueCmd<AbortMsg>(); + }); + + // initiate prompt + LOG(INFO) << "Initiating prompt"; + TrustyAppError error; + auto initResponse = std::tie(rc); + std::tie(error, initResponse) = + app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>( + promptText, extraData, locale, uiOptions); + if (error == TrustyAppError::MSG_TOO_LONG) { + LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long"; + rc = TeeuiRc::UIErrorMessageTooLong; + return result; + } else if (error != TrustyAppError::OK) { + LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error); + return result; // TeeuiRc::SystemError; + } + if (rc != TeeuiRc::OK) { + LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc); + return result; + } + + LOG(INFO) << "Grabbing event devices"; + EventLoop eventloop; + bool grabbed = + grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) { + if (!(flags & POLLIN)) return; + secureInput->handleEvent(evDev); + }); + + if (!grabbed) { + rc = TeeuiRc::SystemError; + return result; + } + + abort_called_ = false; + secureInputDelivered_ = false; + + // ############################## Start 2nd Phase ############################################# + listener_state_ = ListenerState::SetupDone; + stateLock.unlock(); + listener_state_condv_.notify_all(); + + if (!eventloop.start()) { + rc = TeeuiRc::SystemError; + return result; + } + + stateLock.lock(); + + LOG(INFO) << "going to sleep for the grace period"; + auto then = std::chrono::system_clock::now() + + std::chrono::milliseconds(kUserPreInputGracePeriodMillis) + + std::chrono::microseconds(50); + listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; }); + LOG(INFO) << "waking up"; + + if (abort_called_) { + LOG(ERROR) << "Abort called"; + result = {TeeuiRc::Aborted, {}, {}}; + return result; + } + + LOG(INFO) << "Arming event poller"; + // tell the event poller to act on received input events from now on. + secureInput->start(); + + // ############################## Start 3rd Phase - interactive phase ######################### + LOG(INFO) << "Transition to Interactive"; + listener_state_ = ListenerState::Interactive; + stateLock.unlock(); + listener_state_condv_.notify_all(); + + stateLock.lock(); + listener_state_condv_.wait(stateLock, [&]() { + return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_; + }); + LOG(INFO) << "Listener waking up"; + if (abort_called_) { + LOG(ERROR) << "Abort called"; + result = {TeeuiRc::Aborted, {}, {}}; + return result; + } + + if (!secureInputDelivered_) { + if (eventRC != TeeuiRc::OK) { + LOG(ERROR) << "Bad input response"; + result = {eventRC, {}, {}}; + return result; + } + } + + stateLock.unlock(); + + LOG(INFO) << "Fetching Result"; + std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>(); + LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc); + if (error != TrustyAppError::OK) { + result = {TeeuiRc::SystemError, {}, {}}; + } + return result; + + // ############################## Start 4th Phase - cleanup ################################## +} + +// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI +// follow. +Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation( + const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText, + const hidl_vec<uint8_t>& extraData, const hidl_string& locale, + const hidl_vec<UIOption>& uiOptions) { + std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock); + if (!stateLock.try_lock()) { + return ResponseCode::OperationPending; + } + switch (listener_state_) { + case ListenerState::None: + break; + case ListenerState::Starting: + case ListenerState::SetupDone: + case ListenerState::Interactive: + return ResponseCode::OperationPending; + case ListenerState::Terminating: + callback_thread_.join(); + listener_state_ = ListenerState::None; + break; + default: + return ResponseCode::Unexpected; + } + + assert(listener_state_ == ListenerState::None); + + callback_thread_ = std::thread( + [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText, + hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) { + auto [trc, msg, token] = + promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData), + hidl2MsgString(locale), hidl2MsgVector(uiOptions)); + bool do_callback = (listener_state_ == ListenerState::Interactive || + listener_state_ == ListenerState::SetupDone) && + resultCB; + prompt_result_ = convertRc(trc); + listener_state_ = ListenerState::Terminating; + if (do_callback) { + auto error = resultCB->result(prompt_result_, msg, token); + if (!error.isOk()) { + LOG(ERROR) << "Result callback failed " << error.description(); + } + } else { + listener_state_condv_.notify_all(); + } + }, + resultCB, promptText, extraData, locale, uiOptions); + + listener_state_condv_.wait(stateLock, [this] { + return listener_state_ == ListenerState::SetupDone || + listener_state_ == ListenerState::Interactive || + listener_state_ == ListenerState::Terminating; + }); + if (listener_state_ == ListenerState::Terminating) { + callback_thread_.join(); + listener_state_ = ListenerState::None; + return prompt_result_; + } + return ResponseCode::OK; +} + +Return<ResponseCode> +TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) { + ResponseCode rc = ResponseCode::Ignored; + { + /* + * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct + * implementation responds with a mock confirmation token signed with a test key. The + * problem is that the non interactive grace period was not formalized in the HAL spec, + * so that the VTS test does not account for the grace period. (It probably should.) + * This means we can only pass the VTS test if we block until the grace period is over + * (SetupDone -> Interactive) before we deliver the input event. + * + * The true secure input is delivered by a different mechanism and gets ignored - + * not queued - until the grace period is over. + * + */ + std::unique_lock<std::mutex> stateLock(listener_state_lock_); + listener_state_condv_.wait(stateLock, + [this] { return listener_state_ != ListenerState::SetupDone; }); + + if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored; + auto sapp = app_.lock(); + if (!sapp) return ResponseCode::Ignored; + auto [error, response] = + sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>( + static_cast<teeui::TestModeCommands>(secureInputToken.challenge)); + if (error != TrustyAppError::OK) return ResponseCode::SystemError; + auto& [trc] = response; + if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true; + rc = convertRc(trc); + } + if (secureInputDelivered_) listener_state_condv_.notify_all(); + // VTS test expect an OK response if the event was successfully delivered. + // But since the TA returns the callback response now, we have to translate + // Canceled into OK. Canceled is only returned if the delivered event canceled + // the operation, which means that the event was successfully delivered. Thus + // we return OK. + if (rc == ResponseCode::Canceled) return ResponseCode::OK; + return rc; +} + +Return<void> TrustyConfirmationUI::abort() { + { + std::unique_lock<std::mutex> stateLock(listener_state_lock_); + if (listener_state_ == ListenerState::SetupDone || + listener_state_ == ListenerState::Interactive) { + auto sapp = app_.lock(); + if (sapp) sapp->issueCmd<AbortMsg>(); + abort_called_ = true; + } + } + listener_state_condv_.notify_all(); + return Void(); +} + +android::sp<IConfirmationUI> createTrustyConfirmationUI() { + return new TrustyConfirmationUI(); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h new file mode 100644 index 000000000..3a7c7ef4a --- /dev/null +++ b/trusty/confirmationui/TrustyConfirmationUI.h @@ -0,0 +1,104 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H +#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H + +#include <android/hardware/confirmationui/1.0/IConfirmationUI.h> +#include <android/hardware/keymaster/4.0/types.h> +#include <hidl/Status.h> + +#include <atomic> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <teeui/generic_messages.h> +#include <thread> + +#include "TrustyApp.h" + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +using ::android::sp; +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; + +using ::android::trusty::TrustyApp; + +class TrustyConfirmationUI : public IConfirmationUI { + public: + TrustyConfirmationUI(); + virtual ~TrustyConfirmationUI(); + // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI + // follow. + Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB, + const hidl_string& promptText, + const hidl_vec<uint8_t>& extraData, + const hidl_string& locale, + const hidl_vec<UIOption>& uiOptions) override; + Return<ResponseCode> deliverSecureInputEvent( + const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override; + Return<void> abort() override; + + private: + std::weak_ptr<TrustyApp> app_; + std::thread callback_thread_; + + enum class ListenerState : uint32_t { + None, + Starting, + SetupDone, + Interactive, + Terminating, + }; + + /* + * listener_state is protected by listener_state_lock. It makes transitions between phases + * of the confirmation operation atomic. + * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases) + */ + ListenerState listener_state_; + /* + * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user + * called abort. + */ + bool abort_called_; + std::mutex listener_state_lock_; + std::condition_variable listener_state_condv_; + ResponseCode prompt_result_; + bool secureInputDelivered_; + + std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>> + promptUserConfirmation_(const teeui::MsgString& promptText, + const teeui::MsgVector<uint8_t>& extraData, + const teeui::MsgString& locale, + const teeui::MsgVector<teeui::UIOption>& uiOptions); +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc new file mode 100644 index 000000000..dc7a03b6e --- /dev/null +++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc @@ -0,0 +1,4 @@ +service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty + class hal + user nobody + group drmrpc input diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml new file mode 100644 index 000000000..9008b872e --- /dev/null +++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="device"> + <hal format="hidl"> + <name>android.hardware.confirmationui</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IConfirmationUI</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h new file mode 100644 index 000000000..2ab9389b1 --- /dev/null +++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h @@ -0,0 +1,33 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/hardware/confirmationui/1.0/IConfirmationUI.h> + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +android::sp<IConfirmationUI> createTrustyConfirmationUI(); + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp new file mode 100644 index 000000000..dd7e84b44 --- /dev/null +++ b/trusty/confirmationui/service.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <hidl/HidlTransportSupport.h> + +#include <TrustyConfirmationuiHal.h> + +using android::sp; +using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI; + +int main() { + ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/); + auto service = createTrustyConfirmationUI(); + auto status = service->registerAsService(); + if (status != android::OK) { + LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")"; + return -1; + } + ::android::hardware::joinRpcThreadpool(); + return -1; +} |