diff options
40 files changed, 726 insertions, 663 deletions
diff --git a/adb/Android.bp b/adb/Android.bp index a26017fc2..6fd0767fa 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -429,7 +429,7 @@ cc_library_static { }, } -cc_library_static { +cc_library { name: "libadbd_services", defaults: ["adbd_defaults", "host_adbd_supported"], recovery_available: true, @@ -464,6 +464,7 @@ cc_library_static { "libbase", "libcrypto", "libcrypto_utils", + "libcutils_sockets", "liblog", ], @@ -515,6 +516,7 @@ cc_library { "libadb_tls_connection", "libadbd_auth", "libadbd_fs", + "libadbd_services", "libasyncio", "libbase", "libcrypto", @@ -533,7 +535,6 @@ cc_library { }, static_libs: [ - "libadbd_services", "libcutils_sockets", "libdiagnose_usb", "libmdnssd", @@ -575,10 +576,8 @@ cc_binary { "libcrypto_utils", "libcutils_sockets", "libdiagnose_usb", - "liblog", "libmdnssd", "libminijail", - "libselinux", "libssl", ], @@ -588,6 +587,8 @@ cc_binary { "libadbd_auth", "libadbd_fs", "libcrypto", + "liblog", + "libselinux", ], target: { diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp index 922f2ba4c..cc38926a4 100644 --- a/adb/client/file_sync_client.cpp +++ b/adb/client/file_sync_client.cpp @@ -29,6 +29,7 @@ #include <utime.h> #include <chrono> +#include <deque> #include <functional> #include <memory> #include <sstream> @@ -203,7 +204,7 @@ struct TransferLedger { class SyncConnection { public: - SyncConnection() : expect_done_(false) { + SyncConnection() { max = SYNC_DATA_MAX; // TODO: decide at runtime. std::string error; @@ -239,16 +240,6 @@ class SyncConnection { bool IsValid() { return fd >= 0; } - bool ReceivedError(const char* from, const char* to) { - adb_pollfd pfd = {.fd = fd.get(), .events = POLLIN}; - int rc = adb_poll(&pfd, 1, 0); - if (rc < 0) { - Error("failed to poll: %s", strerror(errno)); - return true; - } - return rc != 0; - } - void NewTransfer() { current_ledger_.Reset(); } @@ -258,6 +249,11 @@ class SyncConnection { global_ledger_.bytes_transferred += bytes; } + void RecordFileSent(std::string from, std::string to) { + RecordFilesTransferred(1); + deferred_acknowledgements_.emplace_back(std::move(from), std::move(to)); + } + void RecordFilesTransferred(size_t files) { current_ledger_.files_transferred += files; global_ledger_.files_transferred += files; @@ -283,39 +279,38 @@ class SyncConnection { } } - bool SendRequest(int id, const char* path_and_mode) { - size_t path_length = strlen(path_and_mode); - if (path_length > 1024) { - Error("SendRequest failed: path too long: %zu", path_length); + bool SendRequest(int id, const std::string& path) { + if (path.length() > 1024) { + Error("SendRequest failed: path too long: %zu", path.length()); errno = ENAMETOOLONG; return false; } // Sending header and payload in a single write makes a noticeable // difference to "adb sync" performance. - std::vector<char> buf(sizeof(SyncRequest) + path_length); + std::vector<char> buf(sizeof(SyncRequest) + path.length()); SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]); req->id = id; - req->path_length = path_length; + req->path_length = path.length(); char* data = reinterpret_cast<char*>(req + 1); - memcpy(data, path_and_mode, path_length); + memcpy(data, path.data(), path.length()); - return WriteFdExactly(fd, &buf[0], buf.size()); + return WriteFdExactly(fd, buf.data(), buf.size()); } - bool SendStat(const char* path_and_mode) { + bool SendStat(const std::string& path) { if (!have_stat_v2_) { errno = ENOTSUP; return false; } - return SendRequest(ID_STAT_V2, path_and_mode); + return SendRequest(ID_STAT_V2, path); } - bool SendLstat(const char* path_and_mode) { + bool SendLstat(const std::string& path) { if (have_stat_v2_) { - return SendRequest(ID_LSTAT_V2, path_and_mode); + return SendRequest(ID_LSTAT_V2, path); } else { - return SendRequest(ID_LSTAT_V1, path_and_mode); + return SendRequest(ID_LSTAT_V1, path); } } @@ -374,7 +369,7 @@ class SyncConnection { return true; } - bool SendLs(const char* path) { + bool SendLs(const std::string& path) { return SendRequest(have_ls_v2_ ? ID_LIST_V2 : ID_LIST_V1, path); } @@ -415,28 +410,26 @@ class SyncConnection { // Sending header, payload, and footer in a single write makes a huge // difference to "adb sync" performance. - bool SendSmallFile(const char* path_and_mode, - const char* lpath, const char* rpath, - unsigned mtime, - const char* data, size_t data_length) { - size_t path_length = strlen(path_and_mode); - if (path_length > 1024) { - Error("SendSmallFile failed: path too long: %zu", path_length); + bool SendSmallFile(const std::string& path, mode_t mode, const std::string& lpath, + const std::string& rpath, unsigned mtime, const char* data, + size_t data_length) { + std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode); + if (path_and_mode.length() > 1024) { + Error("SendSmallFile failed: path too long: %zu", path_and_mode.length()); errno = ENAMETOOLONG; return false; } - std::vector<char> buf(sizeof(SyncRequest) + path_length + - sizeof(SyncRequest) + data_length + - sizeof(SyncRequest)); + std::vector<char> buf(sizeof(SyncRequest) + path_and_mode.length() + sizeof(SyncRequest) + + data_length + sizeof(SyncRequest)); char* p = &buf[0]; SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p); req_send->id = ID_SEND; - req_send->path_length = path_length; + req_send->path_length = path_and_mode.length(); p += sizeof(SyncRequest); - memcpy(p, path_and_mode, path_length); - p += path_length; + memcpy(p, path_and_mode.data(), path_and_mode.size()); + p += path_and_mode.length(); SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p); req_data->id = ID_DATA; @@ -451,34 +444,34 @@ class SyncConnection { p += sizeof(SyncRequest); WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0])); - expect_done_ = true; - // RecordFilesTransferred gets called in CopyDone. + RecordFileSent(lpath, rpath); RecordBytesTransferred(data_length); ReportProgress(rpath, data_length, data_length); return true; } - bool SendLargeFile(const char* path_and_mode, - const char* lpath, const char* rpath, - unsigned mtime) { + bool SendLargeFile(const std::string& path, mode_t mode, const std::string& lpath, + const std::string& rpath, unsigned mtime) { + std::string path_and_mode = android::base::StringPrintf("%s,%d", path.c_str(), mode); if (!SendRequest(ID_SEND, path_and_mode)) { - Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno)); + Error("failed to send ID_SEND message '%s': %s", path_and_mode.c_str(), + strerror(errno)); return false; } struct stat st; - if (stat(lpath, &st) == -1) { - Error("cannot stat '%s': %s", lpath, strerror(errno)); + if (stat(lpath.c_str(), &st) == -1) { + Error("cannot stat '%s': %s", lpath.c_str(), strerror(errno)); return false; } uint64_t total_size = st.st_size; uint64_t bytes_copied = 0; - unique_fd lfd(adb_open(lpath, O_RDONLY)); + unique_fd lfd(adb_open(lpath.c_str(), O_RDONLY)); if (lfd < 0) { - Error("opening '%s' locally failed: %s", lpath, strerror(errno)); + Error("opening '%s' locally failed: %s", lpath.c_str(), strerror(errno)); return false; } @@ -487,7 +480,7 @@ class SyncConnection { while (true) { int bytes_read = adb_read(lfd, sbuf.data, max - sizeof(SyncRequest)); if (bytes_read == -1) { - Error("reading '%s' locally failed: %s", lpath, strerror(errno)); + Error("reading '%s' locally failed: %s", lpath.c_str(), strerror(errno)); return false; } else if (bytes_read == 0) { break; @@ -499,55 +492,53 @@ class SyncConnection { RecordBytesTransferred(bytes_read); bytes_copied += bytes_read; - // Check to see if we've received an error from the other side. - if (ReceivedError(lpath, rpath)) { - break; - } - ReportProgress(rpath, bytes_copied, total_size); } syncmsg msg; msg.data.id = ID_DONE; msg.data.size = mtime; - expect_done_ = true; - - // RecordFilesTransferred gets called in CopyDone. + RecordFileSent(lpath, rpath); return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data)); } - bool CopyDone(const char* from, const char* to) { + bool ReadAcknowledgments() { + bool result = true; + while (!deferred_acknowledgements_.empty()) { + auto [from, to] = std::move(deferred_acknowledgements_.front()); + deferred_acknowledgements_.pop_front(); + result &= CopyDone(from, to); + } + return result; + } + + bool CopyDone(const std::string& from, const std::string& to) { syncmsg msg; if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) { - Error("failed to copy '%s' to '%s': couldn't read from device", from, to); + Error("failed to copy '%s' to '%s': couldn't read from device", from.c_str(), + to.c_str()); return false; } if (msg.status.id == ID_OKAY) { - if (expect_done_) { - expect_done_ = false; - RecordFilesTransferred(1); - return true; - } else { - Error("failed to copy '%s' to '%s': received premature success", from, to); - return true; - } + return true; } if (msg.status.id != ID_FAIL) { - Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id); + Error("failed to copy '%s' to '%s': unknown reason %d", from.c_str(), to.c_str(), + msg.status.id); return false; } return ReportCopyFailure(from, to, msg); } - bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) { + bool ReportCopyFailure(const std::string& from, const std::string& to, const syncmsg& msg) { std::vector<char> buf(msg.status.msglen + 1); if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) { - Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", - from, to, strerror(errno)); + Error("failed to copy '%s' to '%s'; failed to read reason (!): %s", from.c_str(), + to.c_str(), strerror(errno)); return false; } buf[msg.status.msglen] = 0; - Error("failed to copy '%s' to '%s': remote %s", from, to, &buf[0]); + Error("failed to copy '%s' to '%s': remote %s", from.c_str(), to.c_str(), &buf[0]); return false; } @@ -616,7 +607,7 @@ class SyncConnection { size_t max; private: - bool expect_done_; + std::deque<std::pair<std::string, std::string>> deferred_acknowledgements_; FeatureSet features_; bool have_stat_v2_; bool have_ls_v2_; @@ -629,16 +620,19 @@ class SyncConnection { return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse? } - bool WriteOrDie(const char* from, const char* to, const void* data, size_t data_length) { + bool WriteOrDie(const std::string& from, const std::string& to, const void* data, + size_t data_length) { if (!WriteFdExactly(fd, data, data_length)) { if (errno == ECONNRESET) { // Assume adbd told us why it was closing the connection, and // try to read failure reason from adbd. syncmsg msg; if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) { - Error("failed to copy '%s' to '%s': no response: %s", from, to, strerror(errno)); + Error("failed to copy '%s' to '%s': no response: %s", from.c_str(), to.c_str(), + strerror(errno)); } else if (msg.status.id != ID_FAIL) { - Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from, to, msg.status.id); + Error("failed to copy '%s' to '%s': not ID_FAIL: %d", from.c_str(), to.c_str(), + msg.status.id); } else { ReportCopyFailure(from, to, msg); } @@ -651,20 +645,20 @@ class SyncConnection { } }; -static bool sync_ls(SyncConnection& sc, const char* path, +static bool sync_ls(SyncConnection& sc, const std::string& path, const std::function<sync_ls_cb>& func) { return sc.SendLs(path) && sc.FinishLs(func); } -static bool sync_stat(SyncConnection& sc, const char* path, struct stat* st) { +static bool sync_stat(SyncConnection& sc, const std::string& path, struct stat* st) { return sc.SendStat(path) && sc.FinishStat(st); } -static bool sync_lstat(SyncConnection& sc, const char* path, struct stat* st) { +static bool sync_lstat(SyncConnection& sc, const std::string& path, struct stat* st) { return sc.SendLstat(path) && sc.FinishStat(st); } -static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat* st) { +static bool sync_stat_fallback(SyncConnection& sc, const std::string& path, struct stat* st) { if (sync_stat(sc, path, st)) { return true; } @@ -688,7 +682,7 @@ static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat struct stat tmp_st; st->st_mode &= ~S_IFMT; - if (sync_lstat(sc, dir_path.c_str(), &tmp_st)) { + if (sync_lstat(sc, dir_path, &tmp_st)) { st->st_mode |= S_IFDIR; } else { st->st_mode |= S_IFREG; @@ -697,10 +691,8 @@ static bool sync_stat_fallback(SyncConnection& sc, const char* path, struct stat return true; } -static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, unsigned mtime, - mode_t mode, bool sync) { - std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode); - +static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::string& rpath, + unsigned mtime, mode_t mode, bool sync) { if (sync) { struct stat st; if (sync_lstat(sc, rpath, &st)) { @@ -714,41 +706,40 @@ static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath, if (S_ISLNK(mode)) { #if !defined(_WIN32) char buf[PATH_MAX]; - ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1); + ssize_t data_length = readlink(lpath.c_str(), buf, PATH_MAX - 1); if (data_length == -1) { - sc.Error("readlink '%s' failed: %s", lpath, strerror(errno)); + sc.Error("readlink '%s' failed: %s", lpath.c_str(), strerror(errno)); return false; } buf[data_length++] = '\0'; - if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, buf, data_length)) { + if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length)) { return false; } - return sc.CopyDone(lpath, rpath); + return true; #endif } struct stat st; - if (stat(lpath, &st) == -1) { - sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno)); + if (stat(lpath.c_str(), &st) == -1) { + sc.Error("failed to stat local file '%s': %s", lpath.c_str(), strerror(errno)); return false; } if (st.st_size < SYNC_DATA_MAX) { std::string data; if (!android::base::ReadFileToString(lpath, &data, true)) { - sc.Error("failed to read all of '%s': %s", lpath, strerror(errno)); + sc.Error("failed to read all of '%s': %s", lpath.c_str(), strerror(errno)); return false; } - if (!sc.SendSmallFile(path_and_mode.c_str(), lpath, rpath, mtime, - data.data(), data.size())) { + if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, data.data(), data.size())) { return false; } } else { - if (!sc.SendLargeFile(path_and_mode.c_str(), lpath, rpath, mtime)) { + if (!sc.SendLargeFile(rpath, mode, lpath, rpath, mtime)) { return false; } } - return sc.CopyDone(lpath, rpath); + return true; } static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, @@ -943,7 +934,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, if (check_timestamps) { for (const copyinfo& ci : file_list) { - if (!sc.SendLstat(ci.rpath.c_str())) { + if (!sc.SendLstat(ci.rpath)) { sc.Error("failed to send lstat"); return false; } @@ -965,7 +956,7 @@ static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath, if (list_only) { sc.Println("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str()); } else { - if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode, false)) { + if (!sync_send(sc, ci.lpath, ci.rpath, ci.time, ci.mode, false)) { return false; } } @@ -1069,6 +1060,7 @@ bool do_sync_push(const std::vector<const char*>& srcs, const char* dst, bool sy sc.ReportTransferRate(src_path, TransferDirection::push); } + success &= sc.ReadAcknowledgments(); sc.ReportOverallTransferRate(TransferDirection::push); return success; } diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp index 6499d4603..3ceb3748a 100644 --- a/adb/client/incremental.cpp +++ b/adb/client/incremental.cpp @@ -45,33 +45,29 @@ static inline int32_t read_int32(borrowed_fd fd) { return result; } -static inline int32_t read_be_int32(borrowed_fd fd) { - return int32_t(be32toh(read_int32(fd))); -} - static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) { - int32_t be_val = read_int32(fd); + int32_t le_val = read_int32(fd); auto old_size = bytes->size(); - bytes->resize(old_size + sizeof(be_val)); - memcpy(bytes->data() + old_size, &be_val, sizeof(be_val)); + bytes->resize(old_size + sizeof(le_val)); + memcpy(bytes->data() + old_size, &le_val, sizeof(le_val)); } static inline void append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes) { - int32_t be_size = read_int32(fd); - int32_t size = int32_t(be32toh(be_size)); + int32_t le_size = read_int32(fd); + int32_t size = int32_t(le32toh(le_size)); auto old_size = bytes->size(); - bytes->resize(old_size + sizeof(be_size) + size); - memcpy(bytes->data() + old_size, &be_size, sizeof(be_size)); - ReadFully(fd, bytes->data() + old_size + sizeof(be_size), size); + bytes->resize(old_size + sizeof(le_size) + size); + memcpy(bytes->data() + old_size, &le_size, sizeof(le_size)); + ReadFully(fd, bytes->data() + old_size + sizeof(le_size), size); } static inline std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) { std::vector<char> result; append_int(fd, &result); // version - append_bytes_with_size(fd, &result); // verityRootHash - append_bytes_with_size(fd, &result); // v3Digest - append_bytes_with_size(fd, &result); // pkcs7SignatureBlock - auto tree_size = read_be_int32(fd); // size of the verity tree + append_bytes_with_size(fd, &result); // hashingInfo + append_bytes_with_size(fd, &result); // signingInfo + auto le_tree_size = read_int32(fd); + auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree return {std::move(result), tree_size}; } diff --git a/base/liblog_symbols.cpp b/base/liblog_symbols.cpp index d5dfcd28b..8d5917907 100644 --- a/base/liblog_symbols.cpp +++ b/base/liblog_symbols.cpp @@ -16,14 +16,20 @@ #include "liblog_symbols.h" -#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM) +#if defined(__ANDROID__) +#if !defined(NO_LIBLOG_DLSYM) || defined(__ANDROID_APEX__) +#define USE_DLSYM +#endif +#endif + +#ifdef USE_DLSYM #include <dlfcn.h> #endif namespace android { namespace base { -#if defined(__ANDROID__) && !defined(NO_LIBLOG_DLSYM) +#ifdef USE_DLSYM const std::optional<LibLogFunctions>& GetLibLogFunctions() { static std::optional<LibLogFunctions> liblog_functions = []() -> std::optional<LibLogFunctions> { diff --git a/base/liblog_symbols.h b/base/liblog_symbols.h index d3134e9c5..b4ab06a88 100644 --- a/base/liblog_symbols.h +++ b/base/liblog_symbols.h @@ -36,8 +36,8 @@ struct LibLogFunctions { 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)(); + int32_t (*__android_log_set_minimum_priority)(int32_t priority); + int32_t (*__android_log_get_minimum_priority)(); void (*__android_log_set_default_tag)(const char* tag); }; diff --git a/base/logging.cpp b/base/logging.cpp index 9360a56d5..9a6e0fb7e 100644 --- a/base/logging.cpp +++ b/base/logging.cpp @@ -118,7 +118,7 @@ static int OpenKmsg() { } #endif -static LogId log_id_tToLogId(int buffer_id) { +static LogId log_id_tToLogId(int32_t buffer_id) { switch (buffer_id) { case LOG_ID_MAIN: return MAIN; @@ -134,7 +134,7 @@ static LogId log_id_tToLogId(int buffer_id) { } } -static int LogIdTolog_id_t(LogId log_id) { +static int32_t LogIdTolog_id_t(LogId log_id) { switch (log_id) { case MAIN: return LOG_ID_MAIN; @@ -171,7 +171,7 @@ static LogSeverity PriorityToLogSeverity(int priority) { } } -static android_LogPriority LogSeverityToPriority(LogSeverity severity) { +static int32_t LogSeverityToPriority(LogSeverity severity) { switch (severity) { case VERBOSE: return ANDROID_LOG_VERBOSE; @@ -333,12 +333,12 @@ 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) { - android_LogPriority priority = LogSeverityToPriority(severity); + int32_t priority = LogSeverityToPriority(severity); if (id == DEFAULT) { id = default_log_id_; } - int lg_id = LogIdTolog_id_t(id); + int32_t lg_id = LogIdTolog_id_t(id); char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK. if (priority == ANDROID_LOG_FATAL && file != nullptr) { @@ -574,7 +574,7 @@ 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); + int32_t priority = LogSeverityToPriority(severity); if (liblog_functions) { __android_logger_data logger_data = { sizeof(__android_logger_data), LOG_ID_DEFAULT, priority, tag, file, line}; @@ -608,7 +608,7 @@ bool ShouldLog(LogSeverity severity, const char* tag) { // 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) { - int priority = LogSeverityToPriority(severity); + int32_t priority = LogSeverityToPriority(severity); return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO); } else { return severity >= gMinimumLogSeverity; @@ -618,7 +618,7 @@ bool ShouldLog(LogSeverity severity, const char* tag) { LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) { static auto& liblog_functions = GetLibLogFunctions(); if (liblog_functions) { - auto priority = LogSeverityToPriority(new_severity); + int32_t priority = LogSeverityToPriority(new_severity); return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority)); } else { LogSeverity old_severity = gMinimumLogSeverity; diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h index 4f24360dc..665d24a7f 100644 --- a/debuggerd/include/debuggerd/handler.h +++ b/debuggerd/include/debuggerd/handler.h @@ -19,7 +19,9 @@ #include <bionic/reserved_signals.h> #include <signal.h> #include <stdint.h> +#include <string.h> #include <sys/cdefs.h> +#include <sys/system_properties.h> #include <sys/types.h> __BEGIN_DECLS @@ -50,16 +52,21 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks); #define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) { - sigaction(SIGABRT, action, nullptr); - sigaction(SIGBUS, action, nullptr); - sigaction(SIGFPE, action, nullptr); - sigaction(SIGILL, action, nullptr); - sigaction(SIGSEGV, action, nullptr); -#if defined(SIGSTKFLT) - sigaction(SIGSTKFLT, action, nullptr); -#endif - sigaction(SIGSYS, action, nullptr); - sigaction(SIGTRAP, action, nullptr); + char value[PROP_VALUE_MAX] = ""; + bool enabled = + !(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") && + __system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1")); + if (enabled) { + sigaction(SIGABRT, action, nullptr); + sigaction(SIGBUS, action, nullptr); + sigaction(SIGFPE, action, nullptr); + sigaction(SIGILL, action, nullptr); + sigaction(SIGSEGV, action, nullptr); + sigaction(SIGSTKFLT, action, nullptr); + sigaction(SIGSYS, action, nullptr); + sigaction(SIGTRAP, action, nullptr); + } + sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr); } diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp index 59cae61d7..46018b955 100644 --- a/fs_mgr/fs_mgr.cpp +++ b/fs_mgr/fs_mgr.cpp @@ -96,6 +96,7 @@ using android::base::Basename; using android::base::GetBoolProperty; +using android::base::Readlink; using android::base::Realpath; using android::base::SetProperty; using android::base::StartsWith; @@ -736,15 +737,33 @@ static int __mount(const std::string& source, const std::string& target, const F unsigned long mountflags = entry.flags; int ret = 0; int save_errno = 0; + int gc_allowance = 0; + std::string opts; + bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0; + Timer t; + do { + if (save_errno == EINVAL && try_f2fs_gc_allowance) { + PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without."; + try_f2fs_gc_allowance = false; + } + if (try_f2fs_gc_allowance) { + opts = entry.fs_options + entry.fs_checkpoint_opts + ":" + + std::to_string(gc_allowance) + "%"; + } else { + opts = entry.fs_options; + } if (save_errno == EAGAIN) { PINFO << "Retrying mount (source=" << source << ",target=" << target - << ",type=" << entry.fs_type << ")=" << ret << "(" << save_errno << ")"; + << ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret + << "(" << save_errno << ")"; } ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags, - entry.fs_options.c_str()); + opts.c_str()); save_errno = errno; - } while (ret && save_errno == EAGAIN); + if (try_f2fs_gc_allowance) gc_allowance += 10; + } while ((ret && save_errno == EAGAIN && gc_allowance <= 100) || + (ret && save_errno == EINVAL && try_f2fs_gc_allowance)); const char* target_missing = ""; const char* source_missing = ""; if (save_errno == ENOENT) { @@ -760,6 +779,8 @@ static int __mount(const std::string& source, const std::string& target, const F if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { fs_mgr_set_blk_ro(source); } + android::base::SetProperty("ro.boottime.init.mount." + Basename(target), + std::to_string(t.duration().count())); errno = save_errno; return ret; } @@ -1074,7 +1095,7 @@ class CheckpointManager { bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) { if (entry->fs_mgr_flags.checkpoint_fs) { if (is_f2fs(entry->fs_type)) { - entry->fs_options += ",checkpoint=disable"; + entry->fs_checkpoint_opts = ",checkpoint=disable"; } else { LERROR << entry->fs_type << " does not implement checkpoints."; } @@ -1588,6 +1609,61 @@ static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) { } } +static bool UnwindDmDeviceStack(const std::string& block_device, + std::vector<std::string>* dm_stack) { + if (!StartsWith(block_device, "/dev/block/")) { + LWARNING << block_device << " is not a block device"; + return false; + } + std::string current = block_device; + DeviceMapper& dm = DeviceMapper::Instance(); + while (true) { + dm_stack->push_back(current); + if (!dm.IsDmBlockDevice(current)) { + break; + } + auto parent = dm.GetParentBlockDeviceByPath(current); + if (!parent) { + return false; + } + current = *parent; + } + return true; +} + +FstabEntry* fs_mgr_get_mounted_entry_for_userdata(Fstab* fstab, const FstabEntry& mounted_entry) { + if (mounted_entry.mount_point != "/data") { + LERROR << mounted_entry.mount_point << " is not /data"; + return nullptr; + } + std::vector<std::string> dm_stack; + if (!UnwindDmDeviceStack(mounted_entry.blk_device, &dm_stack)) { + LERROR << "Failed to unwind dm-device stack for " << mounted_entry.blk_device; + return nullptr; + } + for (auto& entry : *fstab) { + if (entry.mount_point != "/data") { + continue; + } + std::string block_device; + if (entry.fs_mgr_flags.logical) { + if (!fs_mgr_update_logical_partition(&entry)) { + LERROR << "Failed to update logic partition " << entry.blk_device; + continue; + } + block_device = entry.blk_device; + } else if (!Readlink(entry.blk_device, &block_device)) { + PWARNING << "Failed to read link " << entry.blk_device; + block_device = entry.blk_device; + } + if (std::find(dm_stack.begin(), dm_stack.end(), block_device) != dm_stack.end()) { + return &entry; + } + } + LERROR << "Didn't find entry that was used to mount /data onto " << mounted_entry.blk_device; + return nullptr; +} + // TODO(b/143970043): return different error codes based on which step failed. int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) { Fstab proc_mounts; @@ -1596,16 +1672,13 @@ int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) { return -1; } std::string block_device; - if (auto entry = GetEntryForMountPoint(&proc_mounts, "/data"); entry != nullptr) { - // Note: we don't care about a userdata wrapper here, since it's safe - // to remount on top of the bow device instead, there will be no - // conflicts. - block_device = entry->blk_device; - } else { + auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data"); + if (mounted_entry == nullptr) { LERROR << "/data is not mounted"; return -1; } - auto fstab_entry = GetMountedEntryForUserdata(fstab); + block_device = mounted_entry->blk_device; + auto fstab_entry = fs_mgr_get_mounted_entry_for_userdata(fstab, *mounted_entry); if (fstab_entry == nullptr) { LERROR << "Can't find /data in fstab"; return -1; diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp index a36934a3a..f3f1cb724 100644 --- a/fs_mgr/fs_mgr_fstab.cpp +++ b/fs_mgr/fs_mgr_fstab.cpp @@ -814,89 +814,6 @@ std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string return entries; } -static std::string ResolveBlockDevice(const std::string& block_device) { - if (!StartsWith(block_device, "/dev/block/")) { - LWARNING << block_device << " is not a block device"; - return block_device; - } - std::string name = block_device.substr(5); - if (!StartsWith(name, "block/dm-")) { - // Not a dm-device, but might be a symlink. Optimistically try to readlink. - std::string result; - if (Readlink(block_device, &result)) { - return result; - } else if (errno == EINVAL) { - // After all, it wasn't a symlink. - return block_device; - } else { - LERROR << "Failed to readlink " << block_device; - return ""; - } - } - // It's a dm-device, let's find what's inside! - std::string sys_dir = "/sys/" + name; - while (true) { - std::string slaves_dir = sys_dir + "/slaves"; - std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir); - if (!dir) { - LERROR << "Failed to open " << slaves_dir; - return ""; - } - std::string sub_device_name = ""; - for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) { - if (entry->d_type != DT_LNK) continue; - if (!sub_device_name.empty()) { - LERROR << "Too many slaves in " << slaves_dir; - return ""; - } - sub_device_name = entry->d_name; - } - if (sub_device_name.empty()) { - LERROR << "No slaves in " << slaves_dir; - return ""; - } - if (!StartsWith(sub_device_name, "dm-")) { - // Not a dm-device! We can stop now. - return "/dev/block/" + sub_device_name; - } - // Still a dm-device, keep digging. - sys_dir = "/sys/block/" + sub_device_name; - } -} - -FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) { - Fstab mounts; - if (!ReadFstabFromFile("/proc/mounts", &mounts)) { - LERROR << "Failed to read /proc/mounts"; - return nullptr; - } - auto mounted_entry = GetEntryForMountPoint(&mounts, "/data"); - if (mounted_entry == nullptr) { - LWARNING << "/data is not mounted"; - return nullptr; - } - std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device); - if (resolved_block_device.empty()) { - return nullptr; - } - LINFO << "/data is mounted on " << resolved_block_device; - for (auto& entry : *fstab) { - if (entry.mount_point != "/data") { - continue; - } - std::string block_device; - if (!Readlink(entry.blk_device, &block_device)) { - LWARNING << "Failed to readlink " << entry.blk_device; - block_device = entry.blk_device; - } - if (block_device == resolved_block_device) { - return &entry; - } - } - LERROR << "Didn't find entry that was used to mount /data"; - return nullptr; -} - std::set<std::string> GetBootDevices() { // First check the kernel commandline, then try the device tree otherwise std::string dt_file_name = get_android_dt_dir() + "/boot_devices"; diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 9bc38f97b..3d556c9e5 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -107,6 +107,10 @@ enum FsMgrUmountStatus : int { // it destroys verity devices from device mapper after the device is unmounted. int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab); +// Finds a entry in |fstab| that was used to mount a /data |mounted_entry| from +// /proc/mounts. +android::fs_mgr::FstabEntry* fs_mgr_get_mounted_entry_for_userdata( + android::fs_mgr::Fstab* fstab, const android::fs_mgr::FstabEntry& mounted_entry); int fs_mgr_remount_userdata_into_checkpointing(android::fs_mgr::Fstab* fstab); // Finds the dm_bow device on which this block device is stacked, or returns diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h index c94d7acd1..7cf4f8997 100644 --- a/fs_mgr/include_fstab/fstab/fstab.h +++ b/fs_mgr/include_fstab/fstab/fstab.h @@ -36,6 +36,7 @@ struct FstabEntry { std::string fs_type; unsigned long flags = 0; std::string fs_options; + std::string fs_checkpoint_opts; std::string key_loc; std::string metadata_key_dir; std::string metadata_encryption; @@ -102,7 +103,6 @@ bool SkipMountingPartitions(Fstab* fstab); FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path); // The Fstab can contain multiple entries for the same mount point with different configurations. std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path); -FstabEntry* GetMountedEntryForUserdata(Fstab* fstab); // This method builds DSU fstab entries and transfer the fstab. // diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index 254fbed24..673e145f1 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -120,7 +120,7 @@ bool DeviceMapper::DeleteDevice(const std::string& name, return false; } if (!WaitForFileDeleted(unique_path, timeout_ms)) { - LOG(ERROR) << "Timeout out waiting for " << unique_path << " to be deleted"; + LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted"; return false; } return true; @@ -161,7 +161,7 @@ bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, s return true; } if (!WaitForFile(unique_path, timeout_ms)) { - LOG(ERROR) << "Timed out waiting for device path: " << unique_path; + LOG(ERROR) << "Failed waiting for device path: " << unique_path; DeleteDevice(name); return false; } diff --git a/fs_mgr/libdm/utility.cpp b/fs_mgr/libdm/utility.cpp index f252565fd..0eb59ab49 100644 --- a/fs_mgr/libdm/utility.cpp +++ b/fs_mgr/libdm/utility.cpp @@ -19,6 +19,8 @@ #include <thread> +#include <android-base/logging.h> + using namespace std::literals; namespace android { @@ -45,7 +47,11 @@ bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeo // If the file exists but returns EPERM or something, we consider the // condition met. if (access(path.c_str(), F_OK) != 0) { - if (errno == ENOENT) return WaitResult::Wait; + if (errno == ENOENT) { + return WaitResult::Wait; + } + PLOG(ERROR) << "access failed: " << path; + return WaitResult::Fail; } return WaitResult::Done; }; @@ -54,9 +60,13 @@ bool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeo bool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) { auto condition = [&]() -> WaitResult { - if (access(path.c_str(), F_OK) == 0 || errno != ENOENT) { + if (access(path.c_str(), F_OK) == 0) { return WaitResult::Wait; } + if (errno != ENOENT) { + PLOG(ERROR) << "access failed: " << path; + return WaitResult::Fail; + } return WaitResult::Done; }; return WaitForCondition(condition, timeout_ms); diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 0a0a21ddb..d670ca064 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -96,16 +96,6 @@ cc_library_static { static_libs: [ "libfs_mgr_binder" ], - - shared_libs: [ - // TODO(b/148818798): remove when parent bug is fixed - "libutilscallstack", - ], - cflags: [ - "-g", - "-O0", - "-DLIBSNAPSHOT_USE_CALLSTACK", - ], } cc_library_static { @@ -179,9 +169,6 @@ cc_defaults { "libsparse", "libutils", "libz", - - // TODO(b/148818798): remove when parent bug is fixed - "libutilscallstack", ], static_libs: [ "libfs_mgr", @@ -231,8 +218,5 @@ cc_binary { "libprotobuf-cpp-lite", "libstatslog", "libutils", - - // TODO(b/148818798): remove when parent bug is fixed. - "libutilscallstack", ], } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 32345d26f..957c26c10 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -74,7 +74,8 @@ class SnapshotStatus; static constexpr const std::string_view kCowGroupName = "cow"; -bool SourceCopyOperationIsClone(const chromeos_update_engine::InstallOperation& operation); +bool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation, + chromeos_update_engine::InstallOperation* optimized); enum class CreateResult : unsigned int { ERROR, @@ -125,6 +126,9 @@ class SnapshotManager final { // might be needed to perform first-stage mounts. static bool IsSnapshotManagerNeeded(); + // Helper function for second stage init to restorecon on the rollback indicator. + static std::string GetGlobalRollbackIndicatorPath(); + // Begin an update. This must be called before creating any snapshots. It // will fail if GetUpdateState() != None. bool BeginUpdate(); @@ -175,16 +179,6 @@ class SnapshotManager final { UpdateState ProcessUpdateState(const std::function<bool()>& callback = {}, const std::function<bool()>& before_cancel = {}); - // Initiate the merge if necessary, then wait for the merge to finish. - // See InitiateMerge() and ProcessUpdateState() for details. - // Returns: - // - None if no merge to initiate - // - Unverified if called on the source slot - // - MergeCompleted if merge is completed - // - other states indicating an error has occurred - UpdateState InitiateMergeAndWait(SnapshotMergeReport* report = nullptr, - const std::function<bool()>& before_cancel = {}); - // Find the status of the current update, if any. // // |progress| depends on the returned status: diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp index 61f5c0cab..efdb59f78 100644 --- a/fs_mgr/libsnapshot/partition_cow_creator.cpp +++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp @@ -62,17 +62,68 @@ bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) { return false; } -bool SourceCopyOperationIsClone(const InstallOperation& operation) { - using ChromeOSExtent = chromeos_update_engine::Extent; - if (operation.src_extents().size() != operation.dst_extents().size()) { +bool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) { + if (operation.type() != InstallOperation::SOURCE_COPY) { return false; } - return std::equal(operation.src_extents().begin(), operation.src_extents().end(), - operation.dst_extents().begin(), - [](const ChromeOSExtent& src, const ChromeOSExtent& dst) { - return src.start_block() == dst.start_block() && - src.num_blocks() == dst.num_blocks(); - }); + + optimized->Clear(); + optimized->set_type(InstallOperation::SOURCE_COPY); + + const auto& src_extents = operation.src_extents(); + const auto& dst_extents = operation.dst_extents(); + + // If input is empty, skip by returning an empty result. + if (src_extents.empty() && dst_extents.empty()) { + return true; + } + + auto s_it = src_extents.begin(); + auto d_it = dst_extents.begin(); + uint64_t s_offset = 0; // offset within *s_it + uint64_t d_offset = 0; // offset within *d_it + bool is_optimized = false; + + while (s_it != src_extents.end() || d_it != dst_extents.end()) { + if (s_it == src_extents.end() || d_it == dst_extents.end()) { + LOG(ERROR) << "number of blocks do not equal in src_extents and dst_extents"; + return false; + } + if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) { + LOG(ERROR) << "Offset goes out of bounds."; + return false; + } + + // Check the next |step| blocks, where |step| is the min of remaining blocks in the current + // source extent and current destination extent. + auto s_step = s_it->num_blocks() - s_offset; + auto d_step = d_it->num_blocks() - d_offset; + auto step = std::min(s_step, d_step); + + bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset; + if (moved) { + // If the next |step| blocks are not copied to the same location, add them to result. + AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step); + AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step); + } else { + // The next |step| blocks are optimized out. + is_optimized = true; + } + + // Advance offsets by |step|, and go to the next non-empty extent if the current extent is + // depleted. + s_offset += step; + d_offset += step; + while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) { + ++s_it; + s_offset = 0; + } + while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) { + ++d_it; + d_offset = 0; + } + } + return is_optimized; } void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de, @@ -101,12 +152,15 @@ uint64_t PartitionCowCreator::GetCowSize() { if (operations == nullptr) return sc.cow_size_bytes(); for (const auto& iop : *operations) { - // Do not allocate space for operations that are going to be skipped + const InstallOperation* written_op = &iop; + InstallOperation buf; + // Do not allocate space for extents that are going to be skipped // during OTA application. - if (iop.type() == InstallOperation::SOURCE_COPY && SourceCopyOperationIsClone(iop)) - continue; + if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) { + written_op = &buf; + } - for (const auto& de : iop.dst_extents()) { + for (const auto& de : written_op->dst_extents()) { WriteExtent(&sc, de, sectors_per_block); } } diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp index 9da3f0525..526f8749a 100644 --- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp +++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <optional> +#include <tuple> + #include <gmock/gmock.h> #include <gtest/gtest.h> #include <libdm/dm.h> @@ -26,6 +29,13 @@ using namespace android::fs_mgr; +using chromeos_update_engine::InstallOperation; +using UeExtent = chromeos_update_engine::Extent; +using google::protobuf::RepeatedPtrField; +using ::testing::Matches; +using ::testing::Pointwise; +using ::testing::Truly; + namespace android { namespace snapshot { @@ -213,5 +223,76 @@ TEST(DmSnapshotInternals, CowSizeCalculator) { } } +void BlocksToExtents(const std::vector<uint64_t>& blocks, + google::protobuf::RepeatedPtrField<UeExtent>* extents) { + for (uint64_t block : blocks) { + AppendExtent(extents, block, 1); + } +} + +template <typename T> +std::vector<uint64_t> ExtentsToBlocks(const T& extents) { + std::vector<uint64_t> blocks; + for (const auto& extent : extents) { + for (uint64_t offset = 0; offset < extent.num_blocks(); ++offset) { + blocks.push_back(extent.start_block() + offset); + } + } + return blocks; +} + +InstallOperation CreateCopyOp(const std::vector<uint64_t>& src_blocks, + const std::vector<uint64_t>& dst_blocks) { + InstallOperation op; + op.set_type(InstallOperation::SOURCE_COPY); + BlocksToExtents(src_blocks, op.mutable_src_extents()); + BlocksToExtents(dst_blocks, op.mutable_dst_extents()); + return op; +} + +// ExtentEqual(tuple<UeExtent, UeExtent>) +MATCHER(ExtentEqual, "") { + auto&& [a, b] = arg; + return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks(); +} + +struct OptimizeOperationTestParam { + InstallOperation input; + std::optional<InstallOperation> expected_output; +}; + +class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {}; +TEST_P(OptimizeOperationTest, Test) { + InstallOperation actual_output; + EXPECT_EQ(GetParam().expected_output.has_value(), + OptimizeSourceCopyOperation(GetParam().input, &actual_output)) + << "OptimizeSourceCopyOperation should " + << (GetParam().expected_output.has_value() ? "succeed" : "fail"); + if (!GetParam().expected_output.has_value()) return; + EXPECT_THAT(actual_output.src_extents(), + Pointwise(ExtentEqual(), GetParam().expected_output->src_extents())); + EXPECT_THAT(actual_output.dst_extents(), + Pointwise(ExtentEqual(), GetParam().expected_output->dst_extents())); +} + +std::vector<OptimizeOperationTestParam> GetOptimizeOperationTestParams() { + return { + {CreateCopyOp({}, {}), CreateCopyOp({}, {})}, + {CreateCopyOp({1, 2, 4}, {1, 2, 4}), CreateCopyOp({}, {})}, + {CreateCopyOp({1, 2, 3}, {4, 5, 6}), std::nullopt}, + {CreateCopyOp({3, 2}, {1, 2}), CreateCopyOp({3}, {1})}, + {CreateCopyOp({5, 6, 3, 4, 1, 2}, {1, 2, 3, 4, 5, 6}), + CreateCopyOp({5, 6, 1, 2}, {1, 2, 5, 6})}, + {CreateCopyOp({1, 2, 3, 5, 5, 6}, {5, 6, 3, 4, 1, 2}), + CreateCopyOp({1, 2, 5, 5, 6}, {5, 6, 4, 1, 2})}, + {CreateCopyOp({1, 2, 5, 6, 9, 10}, {1, 4, 5, 6, 7, 8}), + CreateCopyOp({2, 9, 10}, {4, 7, 8})}, + {CreateCopyOp({2, 3, 3, 4, 4}, {1, 2, 3, 4, 5}), CreateCopyOp({2, 3, 4}, {1, 2, 5})}, + }; +} + +INSTANTIATE_TEST_CASE_P(Snapshot, OptimizeOperationTest, + ::testing::ValuesIn(GetOptimizeOperationTestParams())); + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 61fc2dff6..7e84c48bd 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -21,7 +21,6 @@ #include <sys/unistd.h> #include <optional> -#include <sstream> #include <thread> #include <unordered_set> @@ -38,10 +37,6 @@ #include <libfiemap/image_manager.h> #include <liblp/liblp.h> -#ifdef LIBSNAPSHOT_USE_CALLSTACK -#include <utils/CallStack.h> -#endif - #include <android/snapshot/snapshot.pb.h> #include <libsnapshot/snapshot_stats.h> #include "device_info.h" @@ -81,6 +76,7 @@ using namespace std::chrono_literals; using namespace std::string_literals; static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot"; +static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator"; static constexpr auto kUpdateStateCheckInterval = 2s; // Note: IImageManager is an incomplete type in the header, so the default @@ -227,25 +223,6 @@ bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function LOG(INFO) << "Removing all update state."; -#ifdef LIBSNAPSHOT_USE_CALLSTACK - LOG(WARNING) << "Logging stack; see b/148818798."; - // Do not use CallStack's log functions because snapshotctl relies on - // android-base/logging to save log to files. - // TODO(b/148818798): remove this before we ship. - CallStack callstack; - callstack.update(); - auto callstack_str = callstack.toString(); - LOG(WARNING) << callstack_str.c_str(); - std::stringstream path; - path << "/data/misc/snapshotctl_log/libsnapshot." << Now() << ".log"; - std::string path_str = path.str(); - android::base::WriteStringToFile(callstack_str.c_str(), path_str); - if (chmod(path_str.c_str(), 0644) == -1) { - PLOG(WARNING) << "Unable to chmod 0644 " - << ", file maybe dropped from bugreport:" << path_str; - } -#endif - if (!RemoveAllSnapshots(lock)) { LOG(ERROR) << "Could not remove all snapshots"; return false; @@ -1012,7 +989,7 @@ std::string SnapshotManager::GetSnapshotBootIndicatorPath() { } std::string SnapshotManager::GetRollbackIndicatorPath() { - return metadata_dir_ + "/rollback-indicator"; + return metadata_dir_ + "/" + android::base::Basename(kRollbackIndicatorPath); } void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) { @@ -1469,6 +1446,10 @@ bool SnapshotManager::IsSnapshotManagerNeeded() { return access(kBootIndicatorPath, F_OK) == 0; } +std::string SnapshotManager::GetGlobalRollbackIndicatorPath() { + return kRollbackIndicatorPath; +} + bool SnapshotManager::NeedSnapshotsInFirstStageMount() { // If we fail to read, we'll wind up using CreateLogicalPartitions, which // will create devices that look like the old slot, except with extra @@ -2490,68 +2471,6 @@ std::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() { return AutoUnmountDevice::New(device_->GetMetadataDir()); } -UpdateState SnapshotManager::InitiateMergeAndWait(SnapshotMergeReport* stats_report, - const std::function<bool()>& before_cancel) { - { - auto lock = LockExclusive(); - // Sync update state from file with bootloader. - if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) { - LOG(WARNING) << "Unable to sync write update state, fastboot may " - << "reject / accept wipes incorrectly!"; - } - } - - auto merge_stats = SnapshotMergeStats::GetInstance(*this); - - unsigned int last_progress = 0; - auto callback = [&]() -> bool { - double progress; - GetUpdateState(&progress); - if (last_progress < static_cast<unsigned int>(progress)) { - last_progress = progress; - LOG(INFO) << "Waiting for merge to complete: " << last_progress << "%."; - } - return true; // continue - }; - - LOG(INFO) << "Waiting for any previous merge request to complete. " - << "This can take up to several minutes."; - merge_stats->Start(); - auto state = ProcessUpdateState(callback, before_cancel); - merge_stats->set_state(state); - if (state == UpdateState::None) { - LOG(INFO) << "Can't find any snapshot to merge."; - return state; - } - if (state == UpdateState::Unverified) { - if (GetCurrentSlot() != Slot::Target) { - LOG(INFO) << "Cannot merge until device reboots."; - return state; - } - - if (!InitiateMerge()) { - LOG(ERROR) << "Failed to initiate merge."; - return state; - } - // All other states can be handled by ProcessUpdateState. - LOG(INFO) << "Waiting for merge to complete. This can take up to several minutes."; - last_progress = 0; - state = ProcessUpdateState(callback, before_cancel); - merge_stats->set_state(state); - } - - LOG(INFO) << "Merge finished with state \"" << state << "\"."; - if (stats_report) { - auto result = merge_stats->Finish(); - if (result) { - *stats_report = result->report(); - } else { - LOG(WARNING) << "SnapshotMergeStatus::Finish failed."; - } - } - return state; -} - bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) { if (!device_->IsRecovery()) { LOG(ERROR) << "Data wipes are only allowed in recovery."; diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 7d16ec280..855451d82 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -1027,7 +1027,8 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) { } // Initiate the merge and wait for it to be completed. - ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait()); + ASSERT_TRUE(init->InitiateMerge()); + ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState()); // Check that the target partitions have the same content after the merge. for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) { @@ -1201,7 +1202,8 @@ TEST_F(SnapshotUpdateTest, ReclaimCow) { // Initiate the merge and wait for it to be completed. auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b")); - ASSERT_EQ(UpdateState::MergeCompleted, new_sm->InitiateMergeAndWait()); + ASSERT_TRUE(new_sm->InitiateMerge()); + ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState()); // Execute the second update. ASSERT_TRUE(new_sm->BeginUpdate()); @@ -1341,7 +1343,8 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) { ASSERT_GE(fd, 0); // COW cannot be removed due to open fd, so expect a soft failure. - ASSERT_EQ(UpdateState::MergeNeedsReboot, init->InitiateMergeAndWait()); + ASSERT_TRUE(init->InitiateMerge()); + ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState()); // Simulate shutting down the device. fd.reset(); @@ -1354,7 +1357,7 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) { ASSERT_FALSE(sm->IsSnapshotDevice("sys_b", nullptr)); // Merge should be able to complete now. - ASSERT_EQ(UpdateState::MergeCompleted, init->InitiateMergeAndWait()); + ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState()); } class MetadataMountedTest : public SnapshotUpdateTest { @@ -1691,7 +1694,8 @@ TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) { // There should be no snapshot to merge. auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix)); - ASSERT_EQ(UpdateState::Cancelled, new_sm->InitiateMergeAndWait()); + // update_enigne calls ProcessUpdateState first -- should see Cancelled. + ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState()); // Next OTA calls CancelUpdate no matter what. ASSERT_TRUE(new_sm->CancelUpdate()); diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp index aa5e9c1fa..a44de8419 100644 --- a/fs_mgr/libsnapshot/snapshotctl.cpp +++ b/fs_mgr/libsnapshot/snapshotctl.cpp @@ -24,12 +24,8 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/unique_fd.h> -#include <android/snapshot/snapshot.pb.h> -#include <libsnapshot/snapshot.h> -#include <libsnapshot/snapshot_stats.h> -#include <statslog.h> -#include "utility.h" +#include <libsnapshot/snapshot.h> using namespace std::string_literals; @@ -39,146 +35,22 @@ int Usage() { "Actions:\n" " dump\n" " Print snapshot states.\n" - " merge [--logcat] [--log-to-file] [--report] [--dry-run]\n" - " Initialize merge and wait for it to be completed.\n" - " If --logcat is specified, log to logcat.\n" - " If --log-to-file is specified, log to /data/misc/snapshotctl_log/.\n" - " If both specified, log to both. If none specified, log to stdout.\n" - " If --report is specified, send merge statistics to statsd.\n" - " If --dry-run flag, no real merge operation is is triggered, and\n" - " sample statistics are sent to statsd for testing purpose.\n"; + " merge\n" + " Deprecated.\n"; return EX_USAGE; } namespace android { namespace snapshot { -static SnapshotMergeReport GetDummySnapshotMergeReport() { - SnapshotMergeReport fake_report; - - fake_report.set_state(UpdateState::MergeCompleted); - fake_report.set_resume_count(56); - - return fake_report; -} - bool DumpCmdHandler(int /*argc*/, char** argv) { android::base::InitLogging(argv, &android::base::StderrLogger); return SnapshotManager::New()->Dump(std::cout); } -class FileLogger { - public: - FileLogger() { - static constexpr const char* kLogFilePath = "/data/misc/snapshotctl_log/"; - std::stringstream ss; - ss << kLogFilePath << "snapshotctl." << Now() << ".log"; - fd_.reset(TEMP_FAILURE_RETRY( - open(ss.str().c_str(), - O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_SYNC, 0644))); - if (fd_ == -1) { - PLOG(ERROR) << "Cannot open persistent log " << ss.str(); - return; - } - // Explicitly chmod again because mode in open() may be masked by umask. - if (fchmod(fd_.get(), 0644) == -1) { - PLOG(ERROR) << "Cannot chmod 0644 persistent log " << ss.str(); - return; - } - } - // Copy-contuctor needed to be converted to std::function. - FileLogger(const FileLogger& other) { fd_.reset(dup(other.fd_)); } - void operator()(android::base::LogId, android::base::LogSeverity, const char* /*tag*/, - const char* /*file*/, unsigned int /*line*/, const char* message) { - if (fd_ == -1) return; - std::stringstream ss; - ss << Now() << ":" << message << "\n"; - (void)android::base::WriteStringToFd(ss.str(), fd_); - } - - private: - android::base::unique_fd fd_; -}; - -class MergeCmdLogger { - public: - MergeCmdLogger(int argc, char** argv) { - for (int i = 0; i < argc; ++i) { - if (argv[i] == "--logcat"s) { - loggers_.push_back(android::base::LogdLogger()); - } - if (argv[i] == "--log-to-file"s) { - loggers_.push_back(std::move(FileLogger())); - } - } - if (loggers_.empty()) { - loggers_.push_back(&android::base::StdioLogger); - } - } - void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag, - const char* file, unsigned int line, const char* message) { - for (auto&& logger : loggers_) { - logger(id, severity, tag, file, line, message); - } - } - - private: - std::vector<android::base::LogFunction> loggers_; -}; - -bool MergeCmdHandler(int argc, char** argv) { - std::chrono::milliseconds passed_ms; - - bool report_to_statsd = false; - bool dry_run = false; - for (int i = 2; i < argc; ++i) { - if (argv[i] == "--report"s) { - report_to_statsd = true; - } else if (argv[i] == "--dry-run"s) { - dry_run = true; - } - } - - // 'snapshotctl merge' is stripped away from arguments to - // Logger. - android::base::InitLogging(argv); - android::base::SetLogger(MergeCmdLogger(argc - 2, argv + 2)); - - UpdateState state; - SnapshotMergeReport merge_report; - if (dry_run) { - merge_report = GetDummySnapshotMergeReport(); - state = merge_report.state(); - passed_ms = std::chrono::milliseconds(1234); - } else { - auto begin = std::chrono::steady_clock::now(); - - state = SnapshotManager::New()->InitiateMergeAndWait(&merge_report); - - // We could wind up in the Unverified state if the device rolled back or - // hasn't fully rebooted. Ignore this. - if (state == UpdateState::None || state == UpdateState::Unverified) { - return true; - } - - auto end = std::chrono::steady_clock::now(); - passed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin); - } - - if (report_to_statsd) { - android::util::stats_write(android::util::SNAPSHOT_MERGE_REPORTED, - static_cast<int32_t>(merge_report.state()), - static_cast<int64_t>(passed_ms.count()), - static_cast<int32_t>(merge_report.resume_count())); - } - - if (state == UpdateState::MergeCompleted) { - LOG(INFO) << "Snapshot merged in " << passed_ms.count() << " ms."; - return true; - } - - LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\"."; - +bool MergeCmdHandler(int /*argc*/, char** argv) { + android::base::InitLogging(argv, &android::base::StderrLogger); + LOG(WARNING) << "Deprecated. Call update_engine_client --merge instead."; return false; } diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp index 3318b331e..d32b61eaa 100644 --- a/fs_mgr/libsnapshot/utility.cpp +++ b/fs_mgr/libsnapshot/utility.cpp @@ -34,6 +34,7 @@ using android::fs_mgr::GetEntryForPath; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::Partition; using android::fs_mgr::ReadDefaultFstab; +using google::protobuf::RepeatedPtrField; namespace android { namespace snapshot { @@ -166,5 +167,20 @@ std::ostream& operator<<(std::ostream& os, const Now&) { return os << std::put_time(&now, "%Y%m%d-%H%M%S"); } +void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block, + uint64_t num_blocks) { + if (extents->size() > 0) { + auto last_extent = extents->rbegin(); + auto next_block = last_extent->start_block() + last_extent->num_blocks(); + if (start_block == next_block) { + last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks); + return; + } + } + auto* new_extent = extents->Add(); + new_extent->set_start_block(start_block); + new_extent->set_num_blocks(num_blocks); +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h index 90ad0fec7..e69bdadc9 100644 --- a/fs_mgr/libsnapshot/utility.h +++ b/fs_mgr/libsnapshot/utility.h @@ -125,5 +125,9 @@ bool WriteStringToFileAtomic(const std::string& content, const std::string& path struct Now {}; std::ostream& operator<<(std::ostream& os, const Now&); +// Append to |extents|. Merged into the last element if possible. +void AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents, + uint64_t start_block, uint64_t num_blocks); + } // namespace snapshot } // namespace android diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp index 9caae3597..16e38f157 100644 --- a/fs_mgr/tests/fs_mgr_test.cpp +++ b/fs_mgr/tests/fs_mgr_test.cpp @@ -27,6 +27,7 @@ #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> +#include <fs_mgr.h> #include <fstab/fstab.h> #include <gtest/gtest.h> @@ -1001,6 +1002,10 @@ TEST(fs_mgr, UserdataMountedFromDefaultFstab) { } Fstab fstab; ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab"; - ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab)) + Fstab proc_mounts; + ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &proc_mounts)) << "Failed to read /proc/mounts"; + auto mounted_entry = GetEntryForMountPoint(&proc_mounts, "/data"); + ASSERT_NE(mounted_entry, nullptr) << "/data is not mounted"; + ASSERT_NE(nullptr, fs_mgr_get_mounted_entry_for_userdata(&fstab, *mounted_entry)) << "/data wasn't mounted from default fstab"; } diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp index 21750754e..aa368492d 100644 --- a/init/mount_namespace.cpp +++ b/init/mount_namespace.cpp @@ -323,20 +323,10 @@ bool SwitchToBootstrapMountNamespaceIfNeeded() { } if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 && IsApexUpdatable()) { - // The property service thread and its descendent threads must be in the correct mount - // namespace to call Service::Start(), however setns() only operates on a single thread and - // fails when secondary threads attempt to join the same mount namespace. Therefore, we - // must join the property service thread and its descendents before the setns() call. Those - // threads are then started again after the setns() call, and they'll be in the proper - // namespace. - PausePropertyService(); - if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) { PLOG(ERROR) << "Failed to switch to bootstrap mount namespace."; return false; } - - ResumePropertyService(); } return true; } diff --git a/init/selinux.cpp b/init/selinux.cpp index c5b7576c9..acbcbd647 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -66,6 +66,7 @@ #include <android-base/unique_fd.h> #include <fs_avb/fs_avb.h> #include <libgsi/libgsi.h> +#include <libsnapshot/snapshot.h> #include <selinux/android.h> #include "debug_ramdisk.h" @@ -78,6 +79,7 @@ using android::base::ParseInt; using android::base::Timer; using android::base::unique_fd; using android::fs_mgr::AvbHandle; +using android::snapshot::SnapshotManager; namespace android { namespace init { @@ -535,7 +537,11 @@ void SelinuxRestoreContext() { selinux_android_restorecon("/linkerconfig", 0); - selinux_android_restorecon(gsi::kDsuAvbKeyDir, SELINUX_ANDROID_RESTORECON_RECURSE); + // adb remount, snapshot-based updates, and DSUs all create files during + // first-stage init. + selinux_android_restorecon("/metadata", SELINUX_ANDROID_RESTORECON_RECURSE); + + selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0); } int SelinuxKlogCallback(int type, const char* fmt, ...) { diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp index 565f2c35e..59bd97c25 100644 --- a/libbacktrace/Android.bp +++ b/libbacktrace/Android.bp @@ -89,6 +89,9 @@ cc_library { }, android: { static_libs: ["libasync_safe"], + static: { + whole_static_libs: ["libasync_safe"], + }, }, vendor: { cflags: ["-DNO_LIBDEXFILE_SUPPORT"], diff --git a/libcutils/Android.bp b/libcutils/Android.bp index 4e93df370..fc06c1d1b 100644 --- a/libcutils/Android.bp +++ b/libcutils/Android.bp @@ -45,13 +45,9 @@ cc_library_headers { } // Socket specific parts of libcutils that are safe to statically link into an APEX. -cc_library_static { +cc_library { name: "libcutils_sockets", vendor_available: true, - vndk: { - enabled: true, - support_system_process: true, - }, recovery_available: true, host_supported: true, native_bridge_supported: true, @@ -62,6 +58,7 @@ cc_library_static { export_include_dirs: ["include"], + shared_libs: ["liblog"], srcs: ["sockets.cpp"], target: { linux_bionic: { diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp index c4e4f8574..5805a4d19 100644 --- a/libcutils/fs_config.cpp +++ b/libcutils/fs_config.cpp @@ -201,6 +201,8 @@ static const struct fs_path_config android_files[] = { CAP_MASK_LONG(CAP_SETGID), "system/bin/simpleperf_app_runner" }, { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/e2fsck" }, + { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/tune2fs" }, + { 00755, AID_ROOT, AID_ROOT, 0, "first_stage_ramdisk/system/bin/resize2fs" }, // generic defaults { 00755, AID_ROOT, AID_ROOT, 0, "bin/*" }, { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, diff --git a/liblog/Android.bp b/liblog/Android.bp index f1e511868..841037066 100644 --- a/liblog/Android.bp +++ b/liblog/Android.bp @@ -117,8 +117,12 @@ cc_library { logtags: ["event.logtags"], compile_multilib: "both", apex_available: [ - "//apex_available:anyapex", "//apex_available:platform", + // liblog is exceptionally available to the runtime APEX + // because the dynamic linker has to use it statically. + // See b/151051671 + "com.android.runtime", + // DO NOT add more apex names here ], } diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h index c98455dfa..43a91ab5b 100644 --- a/liblog/include/android/log.h +++ b/liblog/include/android/log.h @@ -56,6 +56,12 @@ #include <stdarg.h> #include <stddef.h> +#include <stdint.h> +#include <sys/cdefs.h> + +#if !defined(__BIONIC__) && !defined(__INTRODUCED_IN) +#define __INTRODUCED_IN(x) +#endif #ifdef __cplusplus extern "C" { @@ -149,14 +155,11 @@ typedef enum log_id { /** The kernel log buffer. */ LOG_ID_KERNEL = 7, - LOG_ID_MAX -} log_id_t; + LOG_ID_MAX, -/** - * 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 + /** Let the logging function choose the best log target. */ + LOG_ID_DEFAULT = 0x7FFFFFFF +} log_id_t; /** * Writes the constant string `text` to the log buffer `id`, @@ -183,14 +186,26 @@ int __android_log_buf_print(int bufID, int prio, const char* tag, const char* fm */ 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. */ + int32_t buffer_id; /* log_id_t or -1 to represent 'default'. */ + int32_t 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. */ + uint32_t line; /* Optional line number, ignore if file is nullptr. */ }; /** + * 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); +/** + * 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); + +#if !defined(__ANDROID__) || __ANDROID_API__ >= 30 +/** * 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. @@ -199,54 +214,44 @@ struct __android_logger_data { * buffers, then pass the message to liblog via this function, and therefore we do not want to * duplicate the loggability check here. */ -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); +void __android_log_write_logger_data(struct __android_logger_data* logger_data, const char* msg) + __INTRODUCED_IN(30); /** * 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); +void __android_log_set_logger(__android_logger_function logger) __INTRODUCED_IN(30); /** * 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); +void __android_log_logd_logger(const struct __android_logger_data* logger_data, const char* msg) + __INTRODUCED_IN(30); /** * 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); + const char* message) __INTRODUCED_IN(30); /** * Sets a user defined aborter function that is called for __android_log_assert() failures. */ -void __android_log_set_aborter(__android_aborter_function aborter); +void __android_log_set_aborter(__android_aborter_function aborter) __INTRODUCED_IN(30); /** * 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); +void __android_log_call_aborter(const char* abort_message) __INTRODUCED_IN(30); /** * Sets android_set_abort_message() on device then aborts(). This is the default aborter. */ -void __android_log_default_aborter(const char* abort_message); +void __android_log_default_aborter(const char* abort_message) __INTRODUCED_IN(30); /** * Use the per-tag properties "log.tag.<tagname>" along with the minimum priority from @@ -260,28 +265,30 @@ void __android_log_default_aborter(const char* abort_message); * * 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); +int __android_log_is_loggable(int prio, const char* tag, int default_prio) __INTRODUCED_IN(30); +int __android_log_is_loggable_len(int prio, const char* tag, size_t len, int default_prio) + __INTRODUCED_IN(30); /** * 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); +int32_t __android_log_set_minimum_priority(int32_t priority) __INTRODUCED_IN(30); /** * 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(void); +int32_t __android_log_get_minimum_priority(void) __INTRODUCED_IN(30); /** * Sets the default tag if no tag is provided when writing a log message. Defaults to * getprogname(). This truncates tag to the maximum log message size, though appropriate tags * should be much smaller. */ -void __android_log_set_default_tag(const char* tag); +void __android_log_set_default_tag(const char* tag) __INTRODUCED_IN(30); +#endif #ifdef __cplusplus } diff --git a/liblog/logger_name.cpp b/liblog/logger_name.cpp index 7d676f4b0..e72290ed1 100644 --- a/liblog/logger_name.cpp +++ b/liblog/logger_name.cpp @@ -41,7 +41,10 @@ const char* android_log_id_to_name(log_id_t log_id) { } static_assert(std::is_same<std::underlying_type<log_id_t>::type, uint32_t>::value, - "log_id_t must be an unsigned int"); + "log_id_t must be an uint32_t"); + +static_assert(std::is_same<std::underlying_type<android_LogPriority>::type, uint32_t>::value, + "log_id_t must be an uint32_t"); log_id_t android_name_to_log_id(const char* logName) { const char* b; diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp index cf82e0f26..b420fa024 100644 --- a/liblog/logger_write.cpp +++ b/liblog/logger_write.cpp @@ -27,6 +27,7 @@ #include <android/set_abort_message.h> #endif +#include <atomic> #include <shared_mutex> #include <android-base/errno_restorer.h> @@ -148,14 +149,12 @@ void __android_log_set_default_tag(const char* tag) { GetDefaultTag().assign(tag, 0, LOGGER_ENTRY_MAX_PAYLOAD); } -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; +static std::atomic_int32_t minimum_log_priority = ANDROID_LOG_DEFAULT; +int32_t __android_log_set_minimum_priority(int32_t priority) { + return minimum_log_priority.exchange(priority, std::memory_order_relaxed); } -int __android_log_get_minimum_priority() { +int32_t __android_log_get_minimum_priority() { return minimum_log_priority; } @@ -268,7 +267,7 @@ void __android_log_stderr_logger(const struct __android_logger_data* logger_data 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 = + int32_t priority = logger_data->priority > ANDROID_LOG_SILENT ? ANDROID_LOG_FATAL : logger_data->priority; char priority_char = log_characters[priority]; uint64_t tid = GetThreadId(); diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp index 3e695567a..f0fcff604 100644 --- a/libstats/push_compat/Android.bp +++ b/libstats/push_compat/Android.bp @@ -35,8 +35,9 @@ cc_defaults { header_libs: ["libstatssocket_headers"], static_libs: [ "libbase", + ], + shared_libs: [ "liblog", - "libutils", ], } diff --git a/libstats/push_compat/StatsEventCompat.cpp b/libstats/push_compat/StatsEventCompat.cpp index de458b3e5..e1a86ae1a 100644 --- a/libstats/push_compat/StatsEventCompat.cpp +++ b/libstats/push_compat/StatsEventCompat.cpp @@ -15,12 +15,16 @@ */ #include "include/StatsEventCompat.h" + +#include <chrono> + +#include <android-base/chrono_utils.h> #include <android-base/properties.h> #include <android/api-level.h> #include <android/log.h> #include <dlfcn.h> -#include <utils/SystemClock.h> +using android::base::boot_clock; using android::base::GetProperty; const static int kStatsEventTag = 1937006964; @@ -36,21 +40,26 @@ const bool StatsEventCompat::mPlatformAtLeastR = GetProperty("ro.build.version.codename", "") == "R" || android_get_device_api_level() > __ANDROID_API_Q__; -// definitions of static class variables +// initializations of static class variables bool StatsEventCompat::mAttemptedLoad = false; -void* StatsEventCompat::mStatsEventApi = nullptr; std::mutex StatsEventCompat::mLoadLock; +AStatsEventApi StatsEventCompat::mAStatsEventApi; + +static int64_t elapsedRealtimeNano() { + return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now()) + .time_since_epoch() + .count(); +} StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) { // guard loading because StatsEventCompat might be called from multithreaded // environment { std::lock_guard<std::mutex> lg(mLoadLock); - if (!mAttemptedLoad) { + if (!mAttemptedLoad && mPlatformAtLeastR) { void* handle = dlopen("libstatssocket.so", RTLD_NOW); if (handle) { - // mStatsEventApi = (struct AStatsEvent_apiTable*)dlsym(handle, - // "table"); + initializeApiTableLocked(handle); } else { ALOGE("dlopen failed: %s\n", dlerror()); } @@ -58,61 +67,93 @@ StatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) { mAttemptedLoad = true; } - if (mStatsEventApi) { - // mEventR = mStatsEventApi->obtain(); - } else if (!mPlatformAtLeastR) { - mEventQ << android::elapsedRealtimeNano(); + if (useRSchema()) { + mEventR = mAStatsEventApi.obtain(); + } else if (useQSchema()) { + mEventQ << elapsedRealtimeNano(); } } StatsEventCompat::~StatsEventCompat() { - // if (mStatsEventApi) mStatsEventApi->release(mEventR); + if (useRSchema()) mAStatsEventApi.release(mEventR); +} + +// Populates the AStatsEventApi struct by calling dlsym to find the address of +// each API function. +void StatsEventCompat::initializeApiTableLocked(void* handle) { + mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, "AStatsEvent_obtain"); + mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_build"); + mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_write"); + mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, "AStatsEvent_release"); + mAStatsEventApi.setAtomId = + (void (*)(AStatsEvent*, uint32_t))dlsym(handle, "AStatsEvent_setAtomId"); + mAStatsEventApi.writeInt32 = + (void (*)(AStatsEvent*, int32_t))dlsym(handle, "AStatsEvent_writeInt32"); + mAStatsEventApi.writeInt64 = + (void (*)(AStatsEvent*, int64_t))dlsym(handle, "AStatsEvent_writeInt64"); + mAStatsEventApi.writeFloat = + (void (*)(AStatsEvent*, float))dlsym(handle, "AStatsEvent_writeFloat"); + mAStatsEventApi.writeBool = + (void (*)(AStatsEvent*, bool))dlsym(handle, "AStatsEvent_writeBool"); + mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym( + handle, "AStatsEvent_writeByteArray"); + mAStatsEventApi.writeString = + (void (*)(AStatsEvent*, const char*))dlsym(handle, "AStatsEvent_writeString"); + mAStatsEventApi.writeAttributionChain = + (void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym( + handle, "AStatsEvent_writeAttributionChain"); + mAStatsEventApi.addBoolAnnotation = + (void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, "AStatsEvent_addBoolAnnotation"); + mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym( + handle, "AStatsEvent_addInt32Annotation"); + + mAStatsEventApi.initialized = true; } void StatsEventCompat::setAtomId(int32_t atomId) { - if (mStatsEventApi) { - // mStatsEventApi->setAtomId(mEventR, (uint32_t)atomId); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId); + } else if (useQSchema()) { mEventQ << atomId; } } void StatsEventCompat::writeInt32(int32_t value) { - if (mStatsEventApi) { - // mStatsEventApi->writeInt32(mEventR, value); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeInt32(mEventR, value); + } else if (useQSchema()) { mEventQ << value; } } void StatsEventCompat::writeInt64(int64_t value) { - if (mStatsEventApi) { - // mStatsEventApi->writeInt64(mEventR, value); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeInt64(mEventR, value); + } else if (useQSchema()) { mEventQ << value; } } void StatsEventCompat::writeFloat(float value) { - if (mStatsEventApi) { - // mStatsEventApi->writeFloat(mEventR, value); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeFloat(mEventR, value); + } else if (useQSchema()) { mEventQ << value; } } void StatsEventCompat::writeBool(bool value) { - if (mStatsEventApi) { - // mStatsEventApi->writeBool(mEventR, value); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeBool(mEventR, value); + } else if (useQSchema()) { mEventQ << value; } } void StatsEventCompat::writeByteArray(const char* buffer, size_t length) { - if (mStatsEventApi) { - // mStatsEventApi->writeByteArray(mEventR, (const uint8_t*)buffer, length); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length); + } else if (useQSchema()) { mEventQ.AppendCharArray(buffer, length); } } @@ -120,19 +161,19 @@ void StatsEventCompat::writeByteArray(const char* buffer, size_t length) { void StatsEventCompat::writeString(const char* value) { if (value == nullptr) value = ""; - if (mStatsEventApi) { - // mStatsEventApi->writeString(mEventR, value); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeString(mEventR, value); + } else if (useQSchema()) { mEventQ << value; } } void StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids, const vector<const char*>& tags) { - if (mStatsEventApi) { - // mStatsEventApi->writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(), - // (uint8_t)numUids); - } else if (!mPlatformAtLeastR) { + if (useRSchema()) { + mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(), + (uint8_t)numUids); + } else if (useQSchema()) { mEventQ.begin(); for (size_t i = 0; i < numUids; i++) { mEventQ.begin(); @@ -149,8 +190,8 @@ void StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map, const map<int, const char*>& stringMap, const map<int, float>& floatMap) { - // Key value pairs are not supported with AStatsEvent. - if (!mPlatformAtLeastR) { + // AStatsEvent does not support key value pairs. + if (useQSchema()) { mEventQ.begin(); writeKeyValuePairMap(int32Map); writeKeyValuePairMap(int64Map); @@ -177,34 +218,36 @@ template void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float template void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&); void StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) { - // Workaround for unused params. - (void)annotationId; - (void)value; - // if (mStatsEventApi) mStatsEventApi->addBoolAnnotation(mEventR, annotationId, value); + if (useRSchema()) { + mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value); + } // Don't do anything if on Q. } void StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) { - // Workaround for unused params. - (void)annotationId; - (void)value; - // if (mStatsEventApi) mStatsEventApi->addInt32Annotation(mEventR, annotationId, value); + if (useRSchema()) { + mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value); + } // Don't do anything if on Q. } int StatsEventCompat::writeToSocket() { - if (mStatsEventApi) { - // mStatsEventApi->build(mEventR); - // return mStatsEventApi->write(mEventR); + if (useRSchema()) { + mAStatsEventApi.build(mEventR); + return mAStatsEventApi.write(mEventR); } - if (!mPlatformAtLeastR) return mEventQ.write(LOG_ID_STATS); + if (useQSchema()) return mEventQ.write(LOG_ID_STATS); - // We reach here only if we're on R, but libstatspush_compat was unable to + // We reach here only if we're on R, but libstatssocket was unable to // be loaded using dlopen. return -ENOLINK; } -bool StatsEventCompat::usesNewSchema() { - return mStatsEventApi != nullptr; +bool StatsEventCompat::useRSchema() { + return mPlatformAtLeastR && mAStatsEventApi.initialized; +} + +bool StatsEventCompat::useQSchema() { + return !mPlatformAtLeastR; } diff --git a/libstats/push_compat/include/StatsEventCompat.h b/libstats/push_compat/include/StatsEventCompat.h index ad423a1c9..00bf48bf5 100644 --- a/libstats/push_compat/include/StatsEventCompat.h +++ b/libstats/push_compat/include/StatsEventCompat.h @@ -26,6 +26,26 @@ using std::map; using std::vector; +struct AStatsEventApi { + // Indicates whether the below function pointers have been set using dlsym. + bool initialized = false; + + AStatsEvent* (*obtain)(void); + void (*build)(AStatsEvent*); + int (*write)(AStatsEvent*); + void (*release)(AStatsEvent*); + void (*setAtomId)(AStatsEvent*, uint32_t); + void (*writeInt32)(AStatsEvent*, int32_t); + void (*writeInt64)(AStatsEvent*, int64_t); + void (*writeFloat)(AStatsEvent*, float); + void (*writeBool)(AStatsEvent*, bool); + void (*writeByteArray)(AStatsEvent*, const uint8_t*, size_t); + void (*writeString)(AStatsEvent*, const char*); + void (*writeAttributionChain)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t); + void (*addBoolAnnotation)(AStatsEvent*, uint8_t, bool); + void (*addInt32Annotation)(AStatsEvent*, uint8_t, int32_t); +}; + class StatsEventCompat { public: StatsEventCompat(); @@ -57,8 +77,7 @@ class StatsEventCompat { const static bool mPlatformAtLeastR; static bool mAttemptedLoad; static std::mutex mLoadLock; - // static struct AStatsEvent_apiTable* mStatsEventApi; - static void* mStatsEventApi; + static AStatsEventApi mAStatsEventApi; // non-static member variables AStatsEvent* mEventR = nullptr; @@ -67,6 +86,9 @@ class StatsEventCompat { template <class T> void writeKeyValuePairMap(const map<int, T>& keyValuePairMap); - bool usesNewSchema(); + void initializeApiTableLocked(void* handle); + bool useRSchema(); + bool useQSchema(); + FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading); }; diff --git a/libstats/push_compat/tests/StatsEventCompat_test.cpp b/libstats/push_compat/tests/StatsEventCompat_test.cpp index 2be24ec10..dcb37973e 100644 --- a/libstats/push_compat/tests/StatsEventCompat_test.cpp +++ b/libstats/push_compat/tests/StatsEventCompat_test.cpp @@ -29,10 +29,10 @@ using android::base::GetProperty; * * TODO(b/146019024): migrate to android_get_device_api_level() */ -const static bool mPlatformAtLeastR = GetProperty("ro.build.version.release", "") == "R" || +const static bool mPlatformAtLeastR = GetProperty("ro.build.version.codename", "") == "R" || android_get_device_api_level() > __ANDROID_API_Q__; TEST(StatsEventCompatTest, TestDynamicLoading) { StatsEventCompat event; - EXPECT_EQ(mPlatformAtLeastR, event.usesNewSchema()); + EXPECT_EQ(mPlatformAtLeastR, event.useRSchema()); } diff --git a/libstats/socket/include/stats_event.h b/libstats/socket/include/stats_event.h index ff842838b..35762989b 100644 --- a/libstats/socket/include/stats_event.h +++ b/libstats/socket/include/stats_event.h @@ -29,8 +29,9 @@ * AStatsEvent* event = AStatsEvent_obtain(); * * AStatsEvent_setAtomId(event, atomId); + * AStatsEvent_addBoolAnnotation(event, 5, false); // atom-level annotation * AStatsEvent_writeInt32(event, 24); - * AStatsEvent_addBoolAnnotation(event, 1, true); // annotations apply to the previous field + * AStatsEvent_addBoolAnnotation(event, 1, true); // annotation for preceding atom field * AStatsEvent_addInt32Annotation(event, 2, 128); * AStatsEvent_writeFloat(event, 2.0); * @@ -38,13 +39,8 @@ * AStatsEvent_write(event); * AStatsEvent_release(event); * - * Notes: - * (a) write_<type>() and add_<type>_annotation() should be called in the order that fields - * and annotations are defined in the atom. - * (b) set_atom_id() can be called anytime before stats_event_write(). - * (c) add_<type>_annotation() calls apply to the previous field. - * (d) If errors occur, stats_event_write() will write a bitmask of the errors to the socket. - * (e) All strings should be encoded using UTF8. + * Note that calls to add atom fields and annotations should be made in the + * order that they are defined in the atom. */ #ifdef __cplusplus @@ -84,7 +80,7 @@ void AStatsEvent_build(AStatsEvent* event); int AStatsEvent_write(AStatsEvent* event); /** - * Frees the memory held by this StatsEvent + * Frees the memory held by this StatsEvent. * * After calling this, the StatsEvent must not be used or modified in any way. */ @@ -92,6 +88,8 @@ void AStatsEvent_release(AStatsEvent* event); /** * Sets the atom id for this StatsEvent. + * + * This function should be called immediately after AStatsEvent_obtain. **/ void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId); diff --git a/libstats/socket/stats_event.c b/libstats/socket/stats_event.c index b045d9341..24d2ea87e 100644 --- a/libstats/socket/stats_event.c +++ b/libstats/socket/stats_event.c @@ -29,7 +29,6 @@ #define POS_NUM_ELEMENTS 1 #define POS_TIMESTAMP (POS_NUM_ELEMENTS + sizeof(uint8_t)) #define POS_ATOM_ID (POS_TIMESTAMP + sizeof(uint8_t) + sizeof(uint64_t)) -#define POS_FIRST_FIELD (POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t)) /* LIMITS */ #define MAX_ANNOTATION_COUNT 15 @@ -66,8 +65,11 @@ // within a buf. Also includes other required fields. struct AStatsEvent { uint8_t* buf; - size_t lastFieldPos; // location of last field within the buf - size_t size; // number of valid bytes within buffer + // Location of last field within the buf. Here, field denotes either a + // metadata field (e.g. timestamp) or an atom field. + size_t lastFieldPos; + // Number of valid bytes within the buffer. + size_t size; uint32_t numElements; uint32_t atomId; uint32_t errors; @@ -85,20 +87,21 @@ static int64_t get_elapsed_realtime_ns() { AStatsEvent* AStatsEvent_obtain() { AStatsEvent* event = malloc(sizeof(AStatsEvent)); event->buf = (uint8_t*)calloc(MAX_EVENT_PAYLOAD, 1); - event->buf[0] = OBJECT_TYPE; + event->lastFieldPos = 0; + event->size = 2; // reserve first two bytes for outer event type and number of elements + event->numElements = 0; event->atomId = 0; event->errors = 0; event->truncate = true; // truncate for both pulled and pushed atoms event->built = false; - // place the timestamp - uint64_t timestampNs = get_elapsed_realtime_ns(); - event->buf[POS_TIMESTAMP] = INT64_TYPE; - memcpy(&event->buf[POS_TIMESTAMP + sizeof(uint8_t)], ×tampNs, sizeof(timestampNs)); + event->buf[0] = OBJECT_TYPE; + AStatsEvent_writeInt64(event, get_elapsed_realtime_ns()); // write the timestamp - event->numElements = 1; - event->lastFieldPos = 0; // 0 since we haven't written a field yet - event->size = POS_FIRST_FIELD; + // Force client to set atom id immediately (this is required for atom-level + // annotations to be written correctly). All atom field and annotation + // writes will fail until the atom id is set because event->errors != 0. + event->errors |= ERROR_NO_ATOM_ID; return event; } @@ -109,10 +112,12 @@ void AStatsEvent_release(AStatsEvent* event) { } void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) { + if ((event->errors & ERROR_NO_ATOM_ID) == 0) return; + + // Clear the ERROR_NO_ATOM_ID bit. + event->errors &= ~ERROR_NO_ATOM_ID; event->atomId = atomId; - event->buf[POS_ATOM_ID] = INT32_TYPE; - memcpy(&event->buf[POS_ATOM_ID + sizeof(uint8_t)], &atomId, sizeof(atomId)); - event->numElements++; + AStatsEvent_writeInt32(event, atomId); } // Overwrites the timestamp populated in AStatsEvent_obtain with a custom @@ -306,23 +311,23 @@ void AStatsEvent_truncateBuffer(AStatsEvent* event, bool truncate) { void AStatsEvent_build(AStatsEvent* event) { if (event->built) return; - if (event->atomId == 0) event->errors |= ERROR_NO_ATOM_ID; - - if (event->numElements > MAX_BYTE_VALUE) { - event->errors |= ERROR_TOO_MANY_FIELDS; - } else { - event->buf[POS_NUM_ELEMENTS] = event->numElements; - } + if (event->numElements > MAX_BYTE_VALUE) event->errors |= ERROR_TOO_MANY_FIELDS; // If there are errors, rewrite buffer. if (event->errors) { - event->buf[POS_NUM_ELEMENTS] = 3; - event->buf[POS_FIRST_FIELD] = ERROR_TYPE; - memcpy(&event->buf[POS_FIRST_FIELD + sizeof(uint8_t)], &event->errors, - sizeof(event->errors)); - event->size = POS_FIRST_FIELD + sizeof(uint8_t) + sizeof(uint32_t); + // Discard everything after the atom id (including atom-level + // annotations). This leaves only two elements (timestamp and atom id). + event->numElements = 2; + // Reset number of atom-level annotations to 0. + event->buf[POS_ATOM_ID] = INT32_TYPE; + // Now, write errors to the buffer immediately after the atom id. + event->size = POS_ATOM_ID + sizeof(uint8_t) + sizeof(uint32_t); + start_field(event, ERROR_TYPE); + append_int32(event, event->errors); } + event->buf[POS_NUM_ELEMENTS] = event->numElements; + // Truncate the buffer to the appropriate length in order to limit our // memory usage. if (event->truncate) event->buf = (uint8_t*)realloc(event->buf, event->size); diff --git a/libstats/socket/tests/stats_event_test.cpp b/libstats/socket/tests/stats_event_test.cpp index 69d0a9b75..04eff3689 100644 --- a/libstats/socket/tests/stats_event_test.cpp +++ b/libstats/socket/tests/stats_event_test.cpp @@ -89,7 +89,7 @@ void checkAnnotation(uint8_t** buffer, uint8_t annotationId, uint8_t typeId, T a } void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int64_t endTime, - uint32_t atomId) { + uint32_t atomId, uint8_t numAtomLevelAnnotations = 0) { // All events start with OBJECT_TYPE id. checkTypeHeader(buffer, OBJECT_TYPE); @@ -104,7 +104,7 @@ void checkMetadata(uint8_t** buffer, uint8_t numElements, int64_t startTime, int EXPECT_LE(timestamp, endTime); // Check atom id - checkTypeHeader(buffer, INT32_TYPE); + checkTypeHeader(buffer, INT32_TYPE, numAtomLevelAnnotations); checkScalar(buffer, atomId); } @@ -240,7 +240,7 @@ TEST(StatsEventTest, TestAttributionChains) { AStatsEvent_release(event); } -TEST(StatsEventTest, TestAnnotations) { +TEST(StatsEventTest, TestFieldAnnotations) { uint32_t atomId = 100; // first element information @@ -259,7 +259,7 @@ TEST(StatsEventTest, TestAnnotations) { int64_t startTime = android::elapsedRealtimeNano(); AStatsEvent* event = AStatsEvent_obtain(); - AStatsEvent_setAtomId(event, 100); + AStatsEvent_setAtomId(event, atomId); AStatsEvent_writeBool(event, boolValue); AStatsEvent_addBoolAnnotation(event, boolAnnotation1Id, boolAnnotation1Value); AStatsEvent_addInt32Annotation(event, boolAnnotation2Id, boolAnnotation2Value); @@ -292,6 +292,45 @@ TEST(StatsEventTest, TestAnnotations) { AStatsEvent_release(event); } +TEST(StatsEventTest, TestAtomLevelAnnotations) { + uint32_t atomId = 100; + // atom-level annotation information + uint8_t boolAnnotationId = 1; + uint8_t int32AnnotationId = 2; + bool boolAnnotationValue = false; + int32_t int32AnnotationValue = 5; + + float fieldValue = -3.5; + + int64_t startTime = android::elapsedRealtimeNano(); + AStatsEvent* event = AStatsEvent_obtain(); + AStatsEvent_setAtomId(event, atomId); + AStatsEvent_addBoolAnnotation(event, boolAnnotationId, boolAnnotationValue); + AStatsEvent_addInt32Annotation(event, int32AnnotationId, int32AnnotationValue); + AStatsEvent_writeFloat(event, fieldValue); + AStatsEvent_build(event); + int64_t endTime = android::elapsedRealtimeNano(); + + size_t bufferSize; + uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize); + uint8_t* bufferEnd = buffer + bufferSize; + + checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId, + /*numAtomLevelAnnotations=*/2); + + // check atom-level annotations + checkAnnotation(&buffer, boolAnnotationId, BOOL_TYPE, boolAnnotationValue); + checkAnnotation(&buffer, int32AnnotationId, INT32_TYPE, int32AnnotationValue); + + // check first element + checkTypeHeader(&buffer, FLOAT_TYPE); + checkScalar(&buffer, fieldValue); + + EXPECT_EQ(buffer, bufferEnd); // ensure that we have read the entire buffer + EXPECT_EQ(AStatsEvent_getErrors(event), 0); + AStatsEvent_release(event); +} + TEST(StatsEventTest, TestNoAtomIdError) { AStatsEvent* event = AStatsEvent_obtain(); // Don't set the atom id in order to trigger the error. diff --git a/rootdir/init.rc b/rootdir/init.rc index c59f911c1..201fb128d 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -16,6 +16,11 @@ on early-init # Disable sysrq from keyboard write /proc/sys/kernel/sysrq 0 + # Android doesn't need kernel module autoloading, and it causes SELinux + # denials. So disable it by setting modprobe to the empty string. Note: to + # explicitly set a sysctl to an empty string, a trailing newline is needed. + write /proc/sys/kernel/modprobe \n + # Set the security context of /adb_keys if present. restorecon /adb_keys |