Merge "bootstat: power_on* handling"
diff --git a/adb/Android.bp b/adb/Android.bp
index b6aff3e..6558b1b 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -124,7 +124,8 @@
"adb_trace.cpp",
"adb_unique_fd.cpp",
"adb_utils.cpp",
- "fdevent.cpp",
+ "fdevent/fdevent.cpp",
+ "fdevent/fdevent_poll.cpp",
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
@@ -144,7 +145,7 @@
"adb_io_test.cpp",
"adb_listeners_test.cpp",
"adb_utils_test.cpp",
- "fdevent_test.cpp",
+ "fdevent/fdevent_test.cpp",
"socket_spec_test.cpp",
"socket_test.cpp",
"sysdeps_test.cpp",
@@ -435,7 +436,6 @@
shared_libs: [
"libbootloader_message",
"libmdnssd",
- "libext4_utils",
"libfec",
"libfs_mgr",
"libselinux",
diff --git a/adb/adb.h b/adb/adb.h
index 3a6f059..352b2fe 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -26,7 +26,7 @@
#include <android-base/macros.h>
#include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "socket.h"
#include "types.h"
#include "usb.h"
diff --git a/adb/adb_listeners_test.cpp b/adb/adb_listeners_test.cpp
index b697769..a7e2dea 100644
--- a/adb/adb_listeners_test.cpp
+++ b/adb/adb_listeners_test.cpp
@@ -21,7 +21,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
#include "transport.h"
diff --git a/adb/client/transport_mdns.cpp b/adb/client/transport_mdns.cpp
index 283fac5..1a34384 100644
--- a/adb/client/transport_mdns.cpp
+++ b/adb/client/transport_mdns.cpp
@@ -31,7 +31,7 @@
#include "adb_mdns.h"
#include "adb_trace.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
static DNSServiceRef service_ref;
diff --git a/adb/daemon/auth.cpp b/adb/daemon/auth.cpp
index 1800f84..2b8f461 100644
--- a/adb/daemon/auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -18,7 +18,8 @@
#include "adb.h"
#include "adb_auth.h"
-#include "fdevent.h"
+#include "adb_io.h"
+#include "fdevent/fdevent.h"
#include "sysdeps.h"
#include "transport.h"
diff --git a/adb/daemon/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
index 2a6418a..676f8e9 100644
--- a/adb/daemon/framebuffer_service.cpp
+++ b/adb/daemon/framebuffer_service.cpp
@@ -33,7 +33,6 @@
#include "adb.h"
#include "adb_io.h"
#include "adb_utils.h"
-#include "fdevent.h"
/* TODO:
** - sync with vsync to avoid tearing
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
deleted file mode 100644
index 32f9086..0000000
--- a/adb/fdevent.cpp
+++ /dev/null
@@ -1,553 +0,0 @@
-/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
-**
-** Copyright 2006, Brian Swetland <swetland@frotz.net>
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define TRACE_TAG FDEVENT
-
-#include "sysdeps.h"
-#include "fdevent.h"
-
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <atomic>
-#include <deque>
-#include <functional>
-#include <list>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <utility>
-#include <variant>
-#include <vector>
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/thread_annotations.h>
-#include <android-base/threads.h>
-
-#include "adb_io.h"
-#include "adb_trace.h"
-#include "adb_unique_fd.h"
-#include "adb_utils.h"
-#include "sysdeps/chrono.h"
-
-#define FDE_EVENTMASK 0x00ff
-#define FDE_STATEMASK 0xff00
-
-#define FDE_ACTIVE 0x0100
-#define FDE_PENDING 0x0200
-#define FDE_CREATED 0x0400
-
-struct PollNode {
- fdevent* fde;
- adb_pollfd pollfd;
-
- explicit PollNode(fdevent* fde) : fde(fde) {
- memset(&pollfd, 0, sizeof(pollfd));
- pollfd.fd = fde->fd.get();
-
-#if defined(__linux__)
- // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
- // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
- pollfd.events = POLLRDHUP;
-#endif
- }
-};
-
-// All operations to fdevent should happen only in the main thread.
-// That's why we don't need a lock for fdevent.
-static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
-static auto& g_pending_list = *new std::list<fdevent*>();
-static std::atomic<bool> terminate_loop(false);
-static bool main_thread_valid;
-static uint64_t main_thread_id;
-
-static uint64_t fdevent_id;
-
-static bool run_needs_flush = false;
-static auto& run_queue_notify_fd = *new unique_fd();
-static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
-
-void check_main_thread() {
- if (main_thread_valid) {
- CHECK_EQ(main_thread_id, android::base::GetThreadId());
- }
-}
-
-void set_main_thread() {
- main_thread_valid = true;
- main_thread_id = android::base::GetThreadId();
-}
-
-static std::string dump_fde(const fdevent* fde) {
- std::string state;
- if (fde->state & FDE_ACTIVE) {
- state += "A";
- }
- if (fde->state & FDE_PENDING) {
- state += "P";
- }
- if (fde->state & FDE_CREATED) {
- state += "C";
- }
- if (fde->state & FDE_READ) {
- state += "R";
- }
- if (fde->state & FDE_WRITE) {
- state += "W";
- }
- if (fde->state & FDE_ERROR) {
- state += "E";
- }
- return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
- state.c_str());
-}
-
-template <typename F>
-static fdevent* fdevent_create_impl(int fd, F func, void* arg) {
- check_main_thread();
- CHECK_GE(fd, 0);
-
- fdevent* fde = new fdevent();
- fde->id = fdevent_id++;
- fde->state = FDE_ACTIVE;
- fde->fd.reset(fd);
- fde->func = func;
- fde->arg = arg;
- if (!set_file_block_mode(fd, false)) {
- // Here is not proper to handle the error. If it fails here, some error is
- // likely to be detected by poll(), then we can let the callback function
- // to handle it.
- LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
- }
- auto pair = g_poll_node_map.emplace(fde->fd.get(), PollNode(fde));
- CHECK(pair.second) << "install existing fd " << fd;
-
- fde->state |= FDE_CREATED;
- return fde;
-}
-
-fdevent* fdevent_create(int fd, fd_func func, void* arg) {
- return fdevent_create_impl(fd, func, arg);
-}
-
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
- return fdevent_create_impl(fd, func, arg);
-}
-
-unique_fd fdevent_release(fdevent* fde) {
- check_main_thread();
- if (!fde) {
- return {};
- }
-
- if (!(fde->state & FDE_CREATED)) {
- LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
- }
-
- unique_fd result = std::move(fde->fd);
- if (fde->state & FDE_ACTIVE) {
- g_poll_node_map.erase(result.get());
-
- if (fde->state & FDE_PENDING) {
- g_pending_list.remove(fde);
- }
- fde->state = 0;
- fde->events = 0;
- }
-
- delete fde;
- return result;
-}
-
-void fdevent_destroy(fdevent* fde) {
- // Release, and then let unique_fd's destructor cleanup.
- fdevent_release(fde);
-}
-
-static void fdevent_update(fdevent* fde, unsigned events) {
- auto it = g_poll_node_map.find(fde->fd.get());
- CHECK(it != g_poll_node_map.end());
- PollNode& node = it->second;
- if (events & FDE_READ) {
- node.pollfd.events |= POLLIN;
- } else {
- node.pollfd.events &= ~POLLIN;
- }
-
- if (events & FDE_WRITE) {
- node.pollfd.events |= POLLOUT;
- } else {
- node.pollfd.events &= ~POLLOUT;
- }
- fde->state = (fde->state & FDE_STATEMASK) | events;
-}
-
-void fdevent_set(fdevent* fde, unsigned events) {
- check_main_thread();
- events &= FDE_EVENTMASK;
- if ((fde->state & FDE_EVENTMASK) == events) {
- return;
- }
- CHECK(fde->state & FDE_ACTIVE);
- fdevent_update(fde, events);
- D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
-
- if (fde->state & FDE_PENDING) {
- // If we are pending, make sure we don't signal an event that is no longer wanted.
- fde->events &= events;
- if (fde->events == 0) {
- g_pending_list.remove(fde);
- fde->state &= ~FDE_PENDING;
- }
- }
-}
-
-void fdevent_add(fdevent* fde, unsigned events) {
- check_main_thread();
- CHECK(!(events & FDE_TIMEOUT));
- fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
-}
-
-void fdevent_del(fdevent* fde, unsigned events) {
- check_main_thread();
- CHECK(!(events & FDE_TIMEOUT));
- fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
-}
-
-void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
- check_main_thread();
- fde->timeout = timeout;
- fde->last_active = std::chrono::steady_clock::now();
-}
-
-static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
- std::string result;
- for (const auto& pollfd : pollfds) {
- std::string op;
- if (pollfd.events & POLLIN) {
- op += "R";
- }
- if (pollfd.events & POLLOUT) {
- op += "W";
- }
- android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
- }
- return result;
-}
-
-static std::optional<std::chrono::milliseconds> calculate_timeout() {
- std::optional<std::chrono::milliseconds> result = std::nullopt;
- auto now = std::chrono::steady_clock::now();
- check_main_thread();
-
- for (const auto& [fd, pollnode] : g_poll_node_map) {
- UNUSED(fd);
- auto timeout_opt = pollnode.fde->timeout;
- if (timeout_opt) {
- auto deadline = pollnode.fde->last_active + *timeout_opt;
- auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
- if (time_left < std::chrono::milliseconds::zero()) {
- time_left = std::chrono::milliseconds::zero();
- }
-
- if (!result) {
- result = time_left;
- } else {
- result = std::min(*result, time_left);
- }
- }
- }
-
- return result;
-}
-
-static void fdevent_process() {
- std::vector<adb_pollfd> pollfds;
- for (const auto& pair : g_poll_node_map) {
- pollfds.push_back(pair.second.pollfd);
- }
- CHECK_GT(pollfds.size(), 0u);
- D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-
- auto timeout = calculate_timeout();
- int timeout_ms;
- if (!timeout) {
- timeout_ms = -1;
- } else {
- timeout_ms = timeout->count();
- }
-
- int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
- if (ret == -1) {
- PLOG(ERROR) << "poll(), ret = " << ret;
- return;
- }
-
- auto post_poll = std::chrono::steady_clock::now();
-
- for (const auto& pollfd : pollfds) {
- if (pollfd.revents != 0) {
- D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
- }
- unsigned events = 0;
- if (pollfd.revents & POLLIN) {
- events |= FDE_READ;
- }
- if (pollfd.revents & POLLOUT) {
- events |= FDE_WRITE;
- }
- if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
- // We fake a read, as the rest of the code assumes that errors will
- // be detected at that point.
- events |= FDE_READ | FDE_ERROR;
- }
-#if defined(__linux__)
- if (pollfd.revents & POLLRDHUP) {
- events |= FDE_READ | FDE_ERROR;
- }
-#endif
- auto it = g_poll_node_map.find(pollfd.fd);
- CHECK(it != g_poll_node_map.end());
- fdevent* fde = it->second.fde;
-
- if (events == 0) {
- // Check for timeout.
- if (fde->timeout) {
- auto deadline = fde->last_active + *fde->timeout;
- if (deadline < post_poll) {
- events |= FDE_TIMEOUT;
- }
- }
- }
-
- if (events != 0) {
- CHECK_EQ(fde->fd.get(), pollfd.fd);
- fde->events |= events;
- fde->last_active = post_poll;
- D("%s got events %x", dump_fde(fde).c_str(), events);
- fde->state |= FDE_PENDING;
- g_pending_list.push_back(fde);
- }
- }
-}
-
-template <class T>
-struct always_false : std::false_type {};
-
-static void fdevent_call_fdfunc(fdevent* fde) {
- unsigned events = fde->events;
- fde->events = 0;
- CHECK(fde->state & FDE_PENDING);
- fde->state &= (~FDE_PENDING);
- D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
- std::visit(
- [&](auto&& f) {
- using F = std::decay_t<decltype(f)>;
- if constexpr (std::is_same_v<fd_func, F>) {
- f(fde->fd.get(), events, fde->arg);
- } else if constexpr (std::is_same_v<fd_func2, F>) {
- f(fde, events, fde->arg);
- } else {
- static_assert(always_false<F>::value, "non-exhaustive visitor");
- }
- },
- fde->func);
-}
-
-static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
- // We need to be careful around reentrancy here, since a function we call can queue up another
- // function.
- while (true) {
- std::function<void()> fn;
- {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- if (run_queue.empty()) {
- break;
- }
- fn = run_queue.front();
- run_queue.pop_front();
- }
- fn();
- }
-}
-
-static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
- CHECK_GE(fd, 0);
- CHECK(ev & FDE_READ);
-
- char buf[1024];
-
- // Empty the fd.
- if (adb_read(fd, buf, sizeof(buf)) == -1) {
- PLOG(FATAL) << "failed to empty run queue notify fd";
- }
-
- // Mark that we need to flush, and then run it at the end of fdevent_loop.
- run_needs_flush = true;
-}
-
-static void fdevent_run_setup() {
- {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- CHECK(run_queue_notify_fd.get() == -1);
- int s[2];
- if (adb_socketpair(s) != 0) {
- PLOG(FATAL) << "failed to create run queue notify socketpair";
- }
-
- if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
- PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
- }
-
- run_queue_notify_fd.reset(s[0]);
- fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
- CHECK(fde != nullptr);
- fdevent_add(fde, FDE_READ);
- }
-
- fdevent_run_flush();
-}
-
-void fdevent_run_on_main_thread(std::function<void()> fn) {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- run_queue.push_back(std::move(fn));
-
- // run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
- // In that case, rely on the setup code to flush the queue without a notification being needed.
- if (run_queue_notify_fd != -1) {
- int rc = adb_write(run_queue_notify_fd.get(), "", 1);
-
- // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
- if (rc == 0) {
- PLOG(FATAL) << "run queue notify fd was closed?";
- } else if (rc == -1 && errno != EAGAIN) {
- PLOG(FATAL) << "failed to write to run queue notify fd";
- }
- }
-}
-
-static void fdevent_check_spin(uint64_t cycle) {
- // Check to see if we're spinning because we forgot about an fdevent
- // by keeping track of how long fdevents have been continuously pending.
- struct SpinCheck {
- fdevent* fde;
- android::base::boot_clock::time_point timestamp;
- uint64_t cycle;
- };
- static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
- static auto last_cycle = android::base::boot_clock::now();
-
- auto now = android::base::boot_clock::now();
- if (now - last_cycle > 10ms) {
- // We're not spinning.
- g_continuously_pending.clear();
- last_cycle = now;
- return;
- }
- last_cycle = now;
-
- for (auto* fde : g_pending_list) {
- auto it = g_continuously_pending.find(fde->id);
- if (it == g_continuously_pending.end()) {
- g_continuously_pending[fde->id] =
- SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
- } else {
- it->second.cycle = cycle;
- }
- }
-
- for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
- if (it->second.cycle != cycle) {
- it = g_continuously_pending.erase(it);
- } else {
- // Use an absurdly long window, since all we really care about is
- // getting a bugreport eventually.
- if (now - it->second.timestamp > 300s) {
- LOG(FATAL_WITHOUT_ABORT)
- << "detected spin in fdevent: " << dump_fde(it->second.fde);
-#if defined(__linux__)
- int fd = it->second.fde->fd.get();
- std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
- std::string path;
- if (!android::base::Readlink(fd_path, &path)) {
- PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
- }
- LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
-#endif
- abort();
- }
- ++it;
- }
- }
-}
-
-void fdevent_loop() {
- set_main_thread();
- fdevent_run_setup();
-
- uint64_t cycle = 0;
- while (true) {
- if (terminate_loop) {
- return;
- }
-
- D("--- --- waiting for events");
-
- fdevent_process();
-
- fdevent_check_spin(cycle++);
-
- while (!g_pending_list.empty()) {
- fdevent* fde = g_pending_list.front();
- g_pending_list.pop_front();
- fdevent_call_fdfunc(fde);
- }
-
- if (run_needs_flush) {
- fdevent_run_flush();
- run_needs_flush = false;
- }
- }
-}
-
-void fdevent_terminate_loop() {
- terminate_loop = true;
-}
-
-size_t fdevent_installed_count() {
- return g_poll_node_map.size();
-}
-
-void fdevent_reset() {
- g_poll_node_map.clear();
- g_pending_list.clear();
-
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- run_queue_notify_fd.reset();
- run_queue.clear();
-
- main_thread_valid = false;
- terminate_loop = false;
-}
diff --git a/adb/fdevent.h b/adb/fdevent.h
deleted file mode 100644
index 42dbb9e..0000000
--- a/adb/fdevent.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FDEVENT_H
-#define __FDEVENT_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include <chrono>
-#include <functional>
-#include <optional>
-#include <variant>
-
-#include "adb_unique_fd.h"
-
-// Events that may be observed
-#define FDE_READ 0x0001
-#define FDE_WRITE 0x0002
-#define FDE_ERROR 0x0004
-#define FDE_TIMEOUT 0x0008
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
-
-struct fdevent {
- uint64_t id;
-
- unique_fd fd;
- int force_eof = 0;
-
- uint16_t state = 0;
- uint16_t events = 0;
- std::optional<std::chrono::milliseconds> timeout;
- std::chrono::steady_clock::time_point last_active;
-
- std::variant<fd_func, fd_func2> func;
- void* arg = nullptr;
-};
-
-// Allocate and initialize a new fdevent object
-// TODO: Switch these to unique_fd.
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
-
-// Deallocate an fdevent object that was created by fdevent_create.
-void fdevent_destroy(fdevent *fde);
-
-// fdevent_destroy, except releasing the file descriptor previously owned by the fdevent.
-unique_fd fdevent_release(fdevent* fde);
-
-// Change which events should cause notifications
-void fdevent_set(fdevent *fde, unsigned events);
-void fdevent_add(fdevent *fde, unsigned events);
-void fdevent_del(fdevent *fde, unsigned events);
-
-// Set a timeout on an fdevent.
-// If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
-// Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
-// trigger repeatedly every |timeout| ms.
-void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
-
-// Loop forever, handling events.
-void fdevent_loop();
-
-void check_main_thread();
-
-// Queue an operation to run on the main thread.
-void fdevent_run_on_main_thread(std::function<void()> fn);
-
-// The following functions are used only for tests.
-void fdevent_terminate_loop();
-size_t fdevent_installed_count();
-void fdevent_reset();
-void set_main_thread();
-
-#endif
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
new file mode 100644
index 0000000..28b8f37
--- /dev/null
+++ b/adb/fdevent/fdevent.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2006, Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "fdevent_poll.h"
+
+std::string dump_fde(const fdevent* fde) {
+ std::string state;
+ if (fde->state & FDE_ACTIVE) {
+ state += "A";
+ }
+ if (fde->state & FDE_PENDING) {
+ state += "P";
+ }
+ if (fde->state & FDE_READ) {
+ state += "R";
+ }
+ if (fde->state & FDE_WRITE) {
+ state += "W";
+ }
+ if (fde->state & FDE_ERROR) {
+ state += "E";
+ }
+ return android::base::StringPrintf("(fdevent %" PRIu64 ": fd %d %s)", fde->id, fde->fd.get(),
+ state.c_str());
+}
+
+fdevent* fdevent_context::Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg) {
+ CheckMainThread();
+ CHECK_GE(fd.get(), 0);
+
+ fdevent* fde = new fdevent();
+ fde->id = fdevent_id_++;
+ fde->state = FDE_ACTIVE;
+ fde->fd = std::move(fd);
+ fde->func = func;
+ fde->arg = arg;
+ if (!set_file_block_mode(fde->fd, false)) {
+ // Here is not proper to handle the error. If it fails here, some error is
+ // likely to be detected by poll(), then we can let the callback function
+ // to handle it.
+ LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
+ }
+
+ this->Register(fde);
+ return fde;
+}
+
+unique_fd fdevent_context::Destroy(fdevent* fde) {
+ CheckMainThread();
+ if (!fde) {
+ return {};
+ }
+
+ this->Unregister(fde);
+
+ unique_fd result = std::move(fde->fd);
+ delete fde;
+ return result;
+}
+
+void fdevent_context::Add(fdevent* fde, unsigned events) {
+ Set(fde, (fde->state & FDE_EVENTMASK) | events);
+}
+
+void fdevent_context::Del(fdevent* fde, unsigned events) {
+ CHECK(!(events & FDE_TIMEOUT));
+ Set(fde, (fde->state & FDE_EVENTMASK) & ~events);
+}
+
+void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+ CheckMainThread();
+ fde->timeout = timeout;
+ fde->last_active = std::chrono::steady_clock::now();
+}
+
+void fdevent_context::CheckMainThread() {
+ if (main_thread_id_) {
+ CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
+ }
+}
+
+void fdevent_context::Run(std::function<void()> fn) {
+ {
+ std::lock_guard<std::mutex> lock(run_queue_mutex_);
+ run_queue_.push_back(std::move(fn));
+ }
+
+ Interrupt();
+}
+
+void fdevent_context::TerminateLoop() {
+ terminate_loop_ = true;
+ Interrupt();
+}
+
+void fdevent_context::FlushRunQueue() {
+ // We need to be careful around reentrancy here, since a function we call can queue up another
+ // function.
+ while (true) {
+ std::function<void()> fn;
+ {
+ std::lock_guard<std::mutex> lock(this->run_queue_mutex_);
+ if (this->run_queue_.empty()) {
+ break;
+ }
+ fn = this->run_queue_.front();
+ this->run_queue_.pop_front();
+ }
+ fn();
+ }
+}
+
+static auto& g_ambient_fdevent_context =
+ *new std::unique_ptr<fdevent_context>(new fdevent_context_poll());
+
+static fdevent_context* fdevent_get_ambient() {
+ return g_ambient_fdevent_context.get();
+}
+
+fdevent* fdevent_create(int fd, fd_func func, void* arg) {
+ unique_fd ufd(fd);
+ return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg) {
+ unique_fd ufd(fd);
+ return fdevent_get_ambient()->Create(std::move(ufd), func, arg);
+}
+
+unique_fd fdevent_release(fdevent* fde) {
+ return fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_destroy(fdevent* fde) {
+ fdevent_get_ambient()->Destroy(fde);
+}
+
+void fdevent_set(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Set(fde, events);
+}
+
+void fdevent_add(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Add(fde, events);
+}
+
+void fdevent_del(fdevent* fde, unsigned events) {
+ fdevent_get_ambient()->Del(fde, events);
+}
+
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
+ fdevent_get_ambient()->SetTimeout(fde, timeout);
+}
+
+void fdevent_run_on_main_thread(std::function<void()> fn) {
+ fdevent_get_ambient()->Run(std::move(fn));
+}
+
+void fdevent_loop() {
+ fdevent_get_ambient()->Loop();
+}
+
+void check_main_thread() {
+ fdevent_get_ambient()->CheckMainThread();
+}
+
+void fdevent_terminate_loop() {
+ fdevent_get_ambient()->TerminateLoop();
+}
+
+size_t fdevent_installed_count() {
+ return fdevent_get_ambient()->InstalledCount();
+}
+
+void fdevent_reset() {
+ g_ambient_fdevent_context.reset(new fdevent_context_poll());
+}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
new file mode 100644
index 0000000..ccb0c92
--- /dev/null
+++ b/adb/fdevent/fdevent.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __FDEVENT_H
+#define __FDEVENT_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <variant>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+
+// Events that may be observed
+#define FDE_READ 0x0001
+#define FDE_WRITE 0x0002
+#define FDE_ERROR 0x0004
+#define FDE_TIMEOUT 0x0008
+
+// Internal states.
+#define FDE_EVENTMASK 0x00ff
+#define FDE_STATEMASK 0xff00
+
+#define FDE_ACTIVE 0x0100
+#define FDE_PENDING 0x0200
+
+typedef void (*fd_func)(int fd, unsigned events, void *userdata);
+typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
+
+struct fdevent;
+std::string dump_fde(const fdevent* fde);
+
+struct fdevent_context {
+ public:
+ virtual ~fdevent_context() = default;
+
+ // Allocate and initialize a new fdevent object.
+ fdevent* Create(unique_fd fd, std::variant<fd_func, fd_func2> func, void* arg);
+
+ // Deallocate an fdevent object, returning the file descriptor that was owned by it.
+ unique_fd Destroy(fdevent* fde);
+
+ protected:
+ // Register an fdevent that is being created by Create with the fdevent_context.
+ virtual void Register(fdevent* fde) = 0;
+
+ // Unregister an fdevent that is being destroyed by Destroy with the fdevent_context.
+ virtual void Unregister(fdevent* fde) = 0;
+
+ public:
+ // Change which events should cause notifications.
+ virtual void Set(fdevent* fde, unsigned events) = 0;
+ void Add(fdevent* fde, unsigned events);
+ void Del(fdevent* fde, unsigned events);
+
+ // Set a timeout on an fdevent.
+ // If no events are triggered by the timeout, an FDE_TIMEOUT will be generated.
+ // Note timeouts are not defused automatically; if a timeout is set on an fdevent, it will
+ // trigger repeatedly every |timeout| ms.
+ void SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+
+ // Loop until TerminateLoop is called, handling events.
+ // Implementations should call FlushRunQueue on every iteration, and check the value of
+ // terminate_loop_ to determine whether to stop.
+ virtual void Loop() = 0;
+
+ // Assert that the caller is either running on the context's main thread, or that there is no
+ // active main thread.
+ void CheckMainThread();
+
+ // Queue an operation to be run on the main thread.
+ void Run(std::function<void()> fn);
+
+ // Test-only functionality:
+ void TerminateLoop();
+ virtual size_t InstalledCount() = 0;
+
+ protected:
+ // Interrupt the run loop.
+ virtual void Interrupt() = 0;
+
+ // Run all pending functions enqueued via Run().
+ void FlushRunQueue() EXCLUDES(run_queue_mutex_);
+
+ std::optional<uint64_t> main_thread_id_ = std::nullopt;
+ std::atomic<bool> terminate_loop_ = false;
+
+ private:
+ uint64_t fdevent_id_ = 0;
+ std::mutex run_queue_mutex_;
+ std::deque<std::function<void()>> run_queue_ GUARDED_BY(run_queue_mutex_);
+};
+
+struct fdevent {
+ uint64_t id;
+
+ unique_fd fd;
+ int force_eof = 0;
+
+ uint16_t state = 0;
+ uint16_t events = 0;
+ std::optional<std::chrono::milliseconds> timeout;
+ std::chrono::steady_clock::time_point last_active;
+
+ std::variant<fd_func, fd_func2> func;
+ void* arg = nullptr;
+};
+
+// Backwards compatibility shims that forward to the global fdevent_context.
+fdevent* fdevent_create(int fd, fd_func func, void* arg);
+fdevent* fdevent_create(int fd, fd_func2 func, void* arg);
+
+unique_fd fdevent_release(fdevent* fde);
+void fdevent_destroy(fdevent* fde);
+
+void fdevent_set(fdevent *fde, unsigned events);
+void fdevent_add(fdevent *fde, unsigned events);
+void fdevent_del(fdevent *fde, unsigned events);
+void fdevent_set_timeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+void fdevent_loop();
+void check_main_thread();
+
+// Queue an operation to run on the main thread.
+void fdevent_run_on_main_thread(std::function<void()> fn);
+
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
+size_t fdevent_installed_count();
+void fdevent_reset();
+
+#endif
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
new file mode 100644
index 0000000..75ea081
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG FDEVENT
+
+#include "sysdeps.h"
+#include "fdevent_poll.h"
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <deque>
+#include <functional>
+#include <list>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+#include <variant>
+#include <vector>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/threads.h>
+
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_unique_fd.h"
+#include "adb_utils.h"
+#include "fdevent.h"
+#include "sysdeps/chrono.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+ char buf[BUFSIZ];
+ ssize_t rc = TEMP_FAILURE_RETRY(adb_read(fd, buf, sizeof(buf)));
+ if (rc == -1) {
+ PLOG(FATAL) << "failed to read from fdevent interrupt fd";
+ }
+}
+
+fdevent_context_poll::fdevent_context_poll() {
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create fdevent interrupt socketpair";
+ }
+
+ if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+ PLOG(FATAL) << "failed to make fdevent interrupt socket nonblocking";
+ }
+
+ this->interrupt_fd_.reset(s[0]);
+ fdevent* fde = this->Create(unique_fd(s[1]), fdevent_interrupt, nullptr);
+ CHECK(fde != nullptr);
+ this->Add(fde, FDE_READ);
+}
+
+fdevent_context_poll::~fdevent_context_poll() {
+ this->Destroy(this->interrupt_fde_);
+}
+
+void fdevent_context_poll::Register(fdevent* fde) {
+ auto pair = poll_node_map_.emplace(fde->fd.get(), PollNode(fde));
+ CHECK(pair.second) << "install existing fd " << fde->fd.get();
+}
+
+void fdevent_context_poll::Unregister(fdevent* fde) {
+ if (fde->state & FDE_ACTIVE) {
+ poll_node_map_.erase(fde->fd.get());
+
+ if (fde->state & FDE_PENDING) {
+ pending_list_.remove(fde);
+ }
+ fde->state = 0;
+ fde->events = 0;
+ }
+}
+
+void fdevent_context_poll::Set(fdevent* fde, unsigned events) {
+ CheckMainThread();
+ events &= FDE_EVENTMASK;
+ if ((fde->state & FDE_EVENTMASK) == events) {
+ return;
+ }
+ CHECK(fde->state & FDE_ACTIVE);
+
+ auto it = poll_node_map_.find(fde->fd.get());
+ CHECK(it != poll_node_map_.end());
+ PollNode& node = it->second;
+ if (events & FDE_READ) {
+ node.pollfd.events |= POLLIN;
+ } else {
+ node.pollfd.events &= ~POLLIN;
+ }
+
+ if (events & FDE_WRITE) {
+ node.pollfd.events |= POLLOUT;
+ } else {
+ node.pollfd.events &= ~POLLOUT;
+ }
+ fde->state = (fde->state & FDE_STATEMASK) | events;
+
+ D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+ if (fde->state & FDE_PENDING) {
+ // If we are pending, make sure we don't signal an event that is no longer wanted.
+ fde->events &= events;
+ if (fde->events == 0) {
+ pending_list_.remove(fde);
+ fde->state &= ~FDE_PENDING;
+ }
+ }
+}
+
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
+ std::string result;
+ for (const auto& pollfd : pollfds) {
+ std::string op;
+ if (pollfd.events & POLLIN) {
+ op += "R";
+ }
+ if (pollfd.events & POLLOUT) {
+ op += "W";
+ }
+ android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
+ }
+ return result;
+}
+
+static std::optional<std::chrono::milliseconds> calculate_timeout(fdevent_context_poll* ctx) {
+ std::optional<std::chrono::milliseconds> result = std::nullopt;
+ auto now = std::chrono::steady_clock::now();
+ ctx->CheckMainThread();
+
+ for (const auto& [fd, pollnode] : ctx->poll_node_map_) {
+ UNUSED(fd);
+ auto timeout_opt = pollnode.fde->timeout;
+ if (timeout_opt) {
+ auto deadline = pollnode.fde->last_active + *timeout_opt;
+ auto time_left = std::chrono::duration_cast<std::chrono::milliseconds>(deadline - now);
+ if (time_left < std::chrono::milliseconds::zero()) {
+ time_left = std::chrono::milliseconds::zero();
+ }
+
+ if (!result) {
+ result = time_left;
+ } else {
+ result = std::min(*result, time_left);
+ }
+ }
+ }
+
+ return result;
+}
+
+static void fdevent_process(fdevent_context_poll* ctx) {
+ std::vector<adb_pollfd> pollfds;
+ for (const auto& pair : ctx->poll_node_map_) {
+ pollfds.push_back(pair.second.pollfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+
+ auto timeout = calculate_timeout(ctx);
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
+ }
+
+ int ret = adb_poll(&pollfds[0], pollfds.size(), timeout_ms);
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+
+ for (const auto& pollfd : pollfds) {
+ if (pollfd.revents != 0) {
+ D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
+ }
+ unsigned events = 0;
+ if (pollfd.revents & POLLIN) {
+ events |= FDE_READ;
+ }
+ if (pollfd.revents & POLLOUT) {
+ events |= FDE_WRITE;
+ }
+ if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+#if defined(__linux__)
+ if (pollfd.revents & POLLRDHUP) {
+ events |= FDE_READ | FDE_ERROR;
+ }
+#endif
+ auto it = ctx->poll_node_map_.find(pollfd.fd);
+ CHECK(it != ctx->poll_node_map_.end());
+ fdevent* fde = it->second.fde;
+
+ if (events == 0) {
+ // Check for timeout.
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ CHECK_EQ(fde->fd.get(), pollfd.fd);
+ fde->events |= events;
+ fde->last_active = post_poll;
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ fde->state |= FDE_PENDING;
+ ctx->pending_list_.push_back(fde);
+ }
+ }
+}
+
+template <class T>
+struct always_false : std::false_type {};
+
+static void fdevent_call_fdfunc(fdevent* fde) {
+ unsigned events = fde->events;
+ fde->events = 0;
+ CHECK(fde->state & FDE_PENDING);
+ fde->state &= (~FDE_PENDING);
+ D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
+ std::visit(
+ [&](auto&& f) {
+ using F = std::decay_t<decltype(f)>;
+ if constexpr (std::is_same_v<fd_func, F>) {
+ f(fde->fd.get(), events, fde->arg);
+ } else if constexpr (std::is_same_v<fd_func2, F>) {
+ f(fde, events, fde->arg);
+ } else {
+ static_assert(always_false<F>::value, "non-exhaustive visitor");
+ }
+ },
+ fde->func);
+}
+
+static void fdevent_check_spin(fdevent_context_poll* ctx, uint64_t cycle) {
+ // Check to see if we're spinning because we forgot about an fdevent
+ // by keeping track of how long fdevents have been continuously pending.
+ struct SpinCheck {
+ fdevent* fde;
+ android::base::boot_clock::time_point timestamp;
+ uint64_t cycle;
+ };
+
+ // TODO: Move this into the base fdevent_context.
+ static auto& g_continuously_pending = *new std::unordered_map<uint64_t, SpinCheck>();
+ static auto last_cycle = android::base::boot_clock::now();
+
+ auto now = android::base::boot_clock::now();
+ if (now - last_cycle > 10ms) {
+ // We're not spinning.
+ g_continuously_pending.clear();
+ last_cycle = now;
+ return;
+ }
+ last_cycle = now;
+
+ for (auto* fde : ctx->pending_list_) {
+ auto it = g_continuously_pending.find(fde->id);
+ if (it == g_continuously_pending.end()) {
+ g_continuously_pending[fde->id] =
+ SpinCheck{.fde = fde, .timestamp = now, .cycle = cycle};
+ } else {
+ it->second.cycle = cycle;
+ }
+ }
+
+ for (auto it = g_continuously_pending.begin(); it != g_continuously_pending.end();) {
+ if (it->second.cycle != cycle) {
+ it = g_continuously_pending.erase(it);
+ } else {
+ // Use an absurdly long window, since all we really care about is
+ // getting a bugreport eventually.
+ if (now - it->second.timestamp > 300s) {
+ LOG(FATAL_WITHOUT_ABORT)
+ << "detected spin in fdevent: " << dump_fde(it->second.fde);
+#if defined(__linux__)
+ int fd = it->second.fde->fd.get();
+ std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+ std::string path;
+ if (!android::base::Readlink(fd_path, &path)) {
+ PLOG(FATAL_WITHOUT_ABORT) << "readlink of fd " << fd << " failed";
+ }
+ LOG(FATAL_WITHOUT_ABORT) << "fd " << fd << " = " << path;
+#endif
+ abort();
+ }
+ ++it;
+ }
+ }
+}
+
+void fdevent_context_poll::Loop() {
+ main_thread_id_ = android::base::GetThreadId();
+
+ uint64_t cycle = 0;
+ while (true) {
+ if (terminate_loop_) {
+ break;
+ }
+
+ D("--- --- waiting for events");
+
+ fdevent_process(this);
+
+ fdevent_check_spin(this, cycle++);
+
+ while (!pending_list_.empty()) {
+ fdevent* fde = pending_list_.front();
+ pending_list_.pop_front();
+ fdevent_call_fdfunc(fde);
+ }
+
+ this->FlushRunQueue();
+ }
+
+ main_thread_id_.reset();
+}
+
+size_t fdevent_context_poll::InstalledCount() {
+ // We always have an installed fde for interrupt.
+ return poll_node_map_.size() - 1;
+}
+
+void fdevent_context_poll::Interrupt() {
+ int rc = adb_write(this->interrupt_fd_, "", 1);
+
+ // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+ if (rc == 0) {
+ PLOG(FATAL) << "fdevent interrupt fd was closed?";
+ } else if (rc == -1 && errno != EAGAIN) {
+ PLOG(FATAL) << "failed to write to fdevent interrupt fd";
+ }
+}
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
new file mode 100644
index 0000000..db08301
--- /dev/null
+++ b/adb/fdevent/fdevent_poll.h
@@ -0,0 +1,71 @@
+#pragma once
+
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+
+#include <deque>
+#include <list>
+#include <mutex>
+#include <unordered_map>
+
+#include <android-base/thread_annotations.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+struct PollNode {
+ fdevent* fde;
+ adb_pollfd pollfd;
+
+ explicit PollNode(fdevent* fde) : fde(fde) {
+ memset(&pollfd, 0, sizeof(pollfd));
+ pollfd.fd = fde->fd.get();
+
+#if defined(__linux__)
+ // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+ // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+ pollfd.events = POLLRDHUP;
+#endif
+ }
+};
+
+struct fdevent_context_poll : public fdevent_context {
+ fdevent_context_poll();
+ virtual ~fdevent_context_poll();
+
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
+ virtual void Set(fdevent* fde, unsigned events) final;
+
+ virtual void Loop() final;
+
+ virtual size_t InstalledCount() final;
+
+ protected:
+ virtual void Interrupt() final;
+
+ public:
+ // All operations to fdevent should happen only in the main thread.
+ // That's why we don't need a lock for fdevent.
+ std::unordered_map<int, PollNode> poll_node_map_;
+ std::list<fdevent*> pending_list_;
+
+ unique_fd interrupt_fd_;
+ fdevent* interrupt_fde_ = nullptr;
+};
diff --git a/adb/fdevent_test.cpp b/adb/fdevent/fdevent_test.cpp
similarity index 100%
rename from adb/fdevent_test.cpp
rename to adb/fdevent/fdevent_test.cpp
diff --git a/adb/fdevent_test.h b/adb/fdevent/fdevent_test.h
similarity index 95%
rename from adb/fdevent_test.h
rename to adb/fdevent/fdevent_test.h
index 24bce59..2139d0f 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -78,8 +78,8 @@
}
size_t GetAdditionalLocalSocketCount() {
- // dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
- return 2;
+ // dummy socket installed in PrepareThread()
+ return 1;
}
void TerminateThread() {
diff --git a/adb/socket.h b/adb/socket.h
index b8c559a..4276851 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -24,7 +24,7 @@
#include <string>
#include "adb_unique_fd.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "types.h"
class atransport;
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 5e28f76..1601ff0 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include <gtest/gtest.h>
@@ -29,7 +29,7 @@
#include "adb.h"
#include "adb_io.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
#include "socket.h"
#include "sysdeps.h"
#include "sysdeps/chrono.h"
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 78abba5..b0e7fa0 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -64,8 +64,6 @@
#include <memory> // unique_ptr
#include <string>
-#include "fdevent.h"
-
#define OS_PATH_SEPARATORS "\\/"
#define OS_PATH_SEPARATOR '\\'
#define OS_PATH_SEPARATOR_STR "\\"
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 841865a..8bc925f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -49,7 +49,7 @@
#include "adb_io.h"
#include "adb_trace.h"
#include "adb_utils.h"
-#include "fdevent.h"
+#include "fdevent/fdevent.h"
#include "sysdeps/chrono.h"
using android::base::ScopedLockAssertion;
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index b66f8fa..00beb3a 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -19,7 +19,7 @@
#include <gtest/gtest.h>
#include "adb.h"
-#include "fdevent_test.h"
+#include "fdevent/fdevent_test.h"
struct TransportTest : public FdeventTest {};
diff --git a/base/include/android-base/expected.h b/base/include/android-base/expected.h
index 957a8a0..030ef35e 100644
--- a/base/include/android-base/expected.h
+++ b/base/include/android-base/expected.h
@@ -81,15 +81,6 @@
#define _NODISCARD_
#endif
-namespace {
-template< class T >
-struct remove_cvref {
- typedef std::remove_cv_t<std::remove_reference_t<T>> type;
-};
-template< class T >
-using remove_cvref_t = typename remove_cvref<T>::type;
-} // namespace
-
// Class expected
template<class T, class E>
class _NODISCARD_ expected {
@@ -182,25 +173,23 @@
else var_ = unexpected(std::move(rhs.error()));
}
- template<class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
- !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
- std::is_convertible_v<U&&,T> /* non-explicit */
- )>
- constexpr expected(U&& v)
- : var_(std::in_place_index<0>, std::forward<U>(v)) {}
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ std::is_convertible_v<U&&, T> /* non-explicit */
+ )>
+ constexpr expected(U&& v) : var_(std::in_place_index<0>, std::forward<U>(v)) {}
- template<class U = T _ENABLE_IF(
- std::is_constructible_v<T, U&&> &&
- !std::is_same_v<remove_cvref_t<U>, std::in_place_t> &&
- !std::is_same_v<expected<T, E>, remove_cvref_t<U>> &&
- !std::is_same_v<unexpected<E>, remove_cvref_t<U>> &&
- !std::is_convertible_v<U&&,T> /* explicit */
- )>
- constexpr explicit expected(U&& v)
- : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
+ template <class U = T _ENABLE_IF(
+ std::is_constructible_v<T, U&&> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<U>>, std::in_place_t> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_same_v<unexpected<E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::is_convertible_v<U&&, T> /* explicit */
+ )>
+ constexpr explicit expected(U&& v) : var_(std::in_place_index<0>, T(std::forward<U>(v))) {}
template<class G = E _ENABLE_IF(
std::is_constructible_v<E, const G&> &&
@@ -267,16 +256,15 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(
+ std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_assignable_v<E>) = default;
- template<class U = T _ENABLE_IF(
- !std::is_void_v<T> &&
- !std::is_same_v<expected<T,E>, remove_cvref_t<U>> &&
- !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
- std::is_constructible_v<T,U> &&
- std::is_assignable_v<T&,U> &&
- std::is_nothrow_move_constructible_v<E>
- )>
+ template <class U = T _ENABLE_IF(
+ !std::is_void_v<T> &&
+ !std::is_same_v<expected<T, E>, std::remove_cv_t<std::remove_reference_t<U>>> &&
+ !std::conjunction_v<std::is_scalar<T>, std::is_same<T, std::decay_t<U>>> &&
+ std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
+ std::is_nothrow_move_constructible_v<E>)>
expected& operator=(U&& rhs) {
var_ = T(std::forward<U>(rhs));
return *this;
@@ -555,7 +543,7 @@
expected& operator=(const expected& rhs) = default;
// Note for SFNAIE above applies to here as well
- expected& operator=(expected&& rhs) = default;
+ expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_assignable_v<E>) = default;
template<class G = E>
expected& operator=(const unexpected<G>& rhs) {
@@ -646,15 +634,13 @@
public:
// constructors
constexpr unexpected(const unexpected&) = default;
- constexpr unexpected(unexpected&&) = default;
+ constexpr unexpected(unexpected&&) noexcept(std::is_nothrow_move_constructible_v<E>) = default;
- template<class Err = E _ENABLE_IF(
- std::is_constructible_v<E, Err> &&
- !std::is_same_v<remove_cvref_t<E>, std::in_place_t> &&
- !std::is_same_v<remove_cvref_t<E>, unexpected>
- )>
- constexpr unexpected(Err&& e)
- : val_(std::forward<Err>(e)) {}
+ template <class Err = E _ENABLE_IF(
+ std::is_constructible_v<E, Err> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, std::in_place_t> &&
+ !std::is_same_v<std::remove_cv_t<std::remove_reference_t<E>>, unexpected>)>
+ constexpr unexpected(Err&& e) : val_(std::forward<Err>(e)) {}
template<class U, class... Args _ENABLE_IF(
std::is_constructible_v<E, std::initializer_list<U>&, Args...>
@@ -724,7 +710,8 @@
// assignment
constexpr unexpected& operator=(const unexpected&) = default;
- constexpr unexpected& operator=(unexpected&&) = default;
+ constexpr unexpected& operator=(unexpected&&) noexcept(std::is_nothrow_move_assignable_v<E>) =
+ default;
template<class Err = E>
constexpr unexpected& operator=(const unexpected<Err>& rhs) {
val_ = rhs.value();
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 2ebd57d..e01e39b 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -61,7 +61,7 @@
LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
- auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
+ auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d3503c..65f0eff 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -73,6 +73,7 @@
whole_static_libs: [
"liblogwrap",
"libdm",
+ "libext2_uuid",
"libfstab",
],
cppflags: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 259f800..7a0d019 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1331,7 +1331,7 @@
continue;
}
} else if ((current_entry.fs_mgr_flags.verify)) {
- if (!fs_mgr_teardown_verity(¤t_entry, true /* wait */)) {
+ if (!fs_mgr_teardown_verity(¤t_entry)) {
LERROR << "Failed to tear down verified partition on mount point: "
<< current_entry.mount_point;
ret |= FsMgrUmountStatus::ERROR_VERITY;
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 1f21a71..04ba0bf 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -122,19 +122,9 @@
table.set_readonly(false);
}
std::string name = GetPartitionName(partition);
- if (!dm.CreateDevice(name, table)) {
+ if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
- if (!dm.GetDmDevicePathByName(name, path)) {
- return false;
- }
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- if (!WaitForFile(*path, timeout_ms)) {
- DestroyLogicalPartition(name, {});
- LERROR << "Timed out waiting for device path: " << *path;
- return false;
- }
- }
LINFO << "Created logical partition " << name << " on device " << *path;
return true;
}
@@ -194,24 +184,16 @@
timeout_ms, path);
}
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
+bool UnmapDevice(const std::string& name) {
DeviceMapper& dm = DeviceMapper::Instance();
- std::string path;
- if (timeout_ms > std::chrono::milliseconds::zero()) {
- dm.GetDmDevicePathByName(name, &path);
- }
if (!dm.DeleteDevice(name)) {
return false;
}
- if (!path.empty() && !WaitForFileDeleted(path, timeout_ms)) {
- LERROR << "Timed out waiting for device path to unlink: " << path;
- return false;
- }
return true;
}
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) {
- if (!UnmapDevice(name, timeout_ms)) {
+bool DestroyLogicalPartition(const std::string& name) {
+ if (!UnmapDevice(name)) {
return false;
}
LINFO << "Unmapped logical partition " << name;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 31790b1..9a0f4fe 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -704,10 +704,9 @@
return true;
}
-// For GSI to skip mounting /product and /product_services, until there are
-// well-defined interfaces between them and /system. Otherwise, the GSI flashed
-// on /system might not be able to work with /product and /product_services.
-// When they're skipped here, /system/product and /system/product_services in
+// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
+// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
+// /product and /system_ext. When they're skipped here, /system/product and /system/system_ext in
// GSI will be used.
bool SkipMountingPartitions(Fstab* fstab) {
constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg";
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 05ca5fc..ac15ce4 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -32,7 +32,6 @@
#include <unistd.h>
#include <algorithm>
-#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -445,7 +444,7 @@
auto metadata = builder->Export();
if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
if (change) *change = true;
- if (!DestroyLogicalPartition(partition_name, 0s)) return false;
+ if (!DestroyLogicalPartition(partition_name)) return false;
} else {
LERROR << "delete partition " << overlay;
return false;
@@ -518,10 +517,166 @@
return ret;
}
+bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+ auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+ nullptr);
+ if (ret) {
+ PERROR << "__mount(target=" << mount_point
+ << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+ return false;
+ }
+ return true;
+}
+
+bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+ auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+ if (ret) {
+ PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+ return false;
+ }
+ return true;
+}
+
+struct mount_info {
+ std::string mount_point;
+ bool shared_flag;
+};
+
+std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+ std::vector<mount_info> info;
+
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+ return info;
+ }
+
+ ssize_t len;
+ size_t alloc_len = 0;
+ char* line = nullptr;
+ while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+ /* if the last character is a newline, shorten the string by 1 byte */
+ if (line[len - 1] == '\n') {
+ line[len - 1] = '\0';
+ }
+
+ static constexpr char delim[] = " \t";
+ char* save_ptr;
+ if (!strtok_r(line, delim, &save_ptr)) {
+ LERROR << "Error parsing mount ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing parent ID";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount source";
+ break;
+ }
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing root";
+ break;
+ }
+
+ char* p;
+ if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+ LERROR << "Error parsing mount_point";
+ break;
+ }
+ mount_info entry = {p, false};
+
+ if (!strtok_r(nullptr, delim, &save_ptr)) {
+ LERROR << "Error parsing mount_flags";
+ break;
+ }
+
+ while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+ if ((p[0] == '-') && (p[1] == '\0')) break;
+ if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+ }
+ if (!p) {
+ LERROR << "Error parsing fields";
+ break;
+ }
+ info.emplace_back(std::move(entry));
+ }
+
+ free(line);
+ if (info.empty()) {
+ LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+ }
+ return info;
+}
+
bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
+ auto retval = true;
+ auto save_errno = errno;
+
+ struct move_entry {
+ std::string mount_point;
+ std::string dir;
+ bool shared_flag;
+ };
+ std::vector<move_entry> move;
+ auto parent_private = false;
+ auto parent_made_private = false;
+ auto dev_private = false;
+ auto dev_made_private = false;
+ for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+ if ((entry.mount_point == mount_point) && !entry.shared_flag) {
+ parent_private = true;
+ }
+ if ((entry.mount_point == "/dev") && !entry.shared_flag) {
+ dev_private = true;
+ }
+
+ if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+ continue;
+ }
+ if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
+ return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+ }) != move.end()) {
+ continue;
+ }
+
+ // use as the bound directory in /dev.
+ auto new_context = fs_mgr_get_context(entry.mount_point);
+ if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
+ PERROR << "setfscreatecon " << new_context;
+ }
+ move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
+ entry.shared_flag};
+ const auto target = mkdtemp(new_entry.dir.data());
+ if (!target) {
+ retval = false;
+ save_errno = errno;
+ PERROR << "temporary directory for MS_BIND";
+ setfscreatecon(nullptr);
+ continue;
+ }
+ setfscreatecon(nullptr);
+
+ if (!parent_private && !parent_made_private) {
+ parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+ }
+ if (new_entry.shared_flag) {
+ new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+ }
+ if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+ retval = false;
+ save_errno = errno;
+ if (new_entry.shared_flag) {
+ fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+ }
+ continue;
+ }
+ move.emplace_back(std::move(new_entry));
+ }
+
// hijack __mount() report format to help triage
auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
const auto opt_list = android::base::Split(options, ",");
@@ -536,12 +691,38 @@
auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
options.c_str());
if (ret) {
+ retval = false;
+ save_errno = errno;
PERROR << report << ret;
- return false;
} else {
LINFO << report << ret;
- return true;
}
+
+ // Move submounts back.
+ for (const auto& entry : move) {
+ if (!dev_private && !dev_made_private) {
+ dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
+ }
+
+ if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+ retval = false;
+ save_errno = errno;
+ } else if (entry.shared_flag &&
+ !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+ retval = false;
+ save_errno = errno;
+ }
+ rmdir(entry.dir.c_str());
+ }
+ if (dev_made_private) {
+ fs_mgr_overlayfs_set_shared_mount("/dev", true);
+ }
+ if (parent_made_private) {
+ fs_mgr_overlayfs_set_shared_mount(mount_point, true);
+ }
+
+ errno = save_errno;
+ return retval;
}
// Mount kScratchMountPoint
@@ -663,7 +844,7 @@
if (!android::base::EndsWith(name, suffix)) {
continue;
}
- if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name, 2s)) {
+ if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
continue;
}
builder->ResizePartition(builder->FindPartition(name), 0);
@@ -737,7 +918,7 @@
return false;
}
}
- if (!partition_create) DestroyLogicalPartition(partition_name, 10s);
+ if (!partition_create) DestroyLogicalPartition(partition_name);
changed = true;
*partition_exists = false;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 3a33cf3..c5e477c 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -97,10 +97,10 @@
bool fs_mgr_is_ext4(const std::string& blk_device);
bool fs_mgr_is_f2fs(const std::string& blk_device);
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab, bool wait);
+bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
namespace android {
namespace fs_mgr {
-bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool UnmapDevice(const std::string& name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index be8077b..efa2180 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -547,9 +547,9 @@
return retval;
}
-bool fs_mgr_teardown_verity(FstabEntry* entry, bool wait) {
+bool fs_mgr_teardown_verity(FstabEntry* entry) {
const std::string mount_point(basename(entry->mount_point.c_str()));
- if (!android::fs_mgr::UnmapDevice(mount_point, wait ? 1000ms : 0ms)) {
+ if (!android::fs_mgr::UnmapDevice(mount_point)) {
return false;
}
LINFO << "Unmapped verity device " << mount_point;
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index f33fc02..a1dc2dc 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -68,7 +68,7 @@
// Destroy the block device for a logical partition, by name. If |timeout_ms|
// is non-zero, then this will block until the device path has been unlinked.
-bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms);
+bool DestroyLogicalPartition(const std::string& name);
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 21255df..e429d9f 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -29,6 +29,9 @@
"loop_control.cpp",
],
+ static_libs: [
+ "libext2_uuid",
+ ],
header_libs: [
"libbase_headers",
"liblog_headers",
@@ -46,6 +49,7 @@
static_libs: [
"libdm",
"libbase",
+ "libext2_uuid",
"libfs_mgr",
"liblog",
],
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d54b6ef..d56a4b1 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,12 +20,20 @@
#include <sys/sysmacros.h>
#include <sys/types.h>
+#include <functional>
+#include <thread>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/strings.h>
+#include <uuid/uuid.h>
namespace android {
namespace dm {
+using namespace std::literals;
+
DeviceMapper::DeviceMapper() : fd_(-1) {
fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd_ < 0) {
@@ -37,13 +45,13 @@
static DeviceMapper instance;
return instance;
}
+
// Creates a new device mapper device
-bool DeviceMapper::CreateDevice(const std::string& name) {
+bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
if (name.empty()) {
LOG(ERROR) << "Unnamed device mapper device creation is not supported";
return false;
}
-
if (name.size() >= DM_NAME_LEN) {
LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
return false;
@@ -51,6 +59,9 @@
struct dm_ioctl io;
InitIo(&io, name);
+ if (!uuid.empty()) {
+ snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
+ }
if (ioctl(fd_, DM_DEV_CREATE, &io)) {
PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
@@ -67,16 +78,6 @@
}
bool DeviceMapper::DeleteDevice(const std::string& name) {
- if (name.empty()) {
- LOG(ERROR) << "Unnamed device mapper device creation is not supported";
- return false;
- }
-
- if (name.size() >= DM_NAME_LEN) {
- LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
- return false;
- }
-
struct dm_ioctl io;
InitIo(&io, name);
@@ -93,9 +94,81 @@
return true;
}
-const std::unique_ptr<DmTable> DeviceMapper::table(const std::string& /* name */) const {
- // TODO(b/110035986): Return the table, as read from the kernel instead
- return nullptr;
+bool WaitForCondition(const std::function<bool()>& condition,
+ const std::chrono::milliseconds& timeout_ms) {
+ auto start_time = std::chrono::steady_clock::now();
+ while (true) {
+ if (condition()) return true;
+
+ std::this_thread::sleep_for(20ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > timeout_ms) return false;
+ }
+}
+
+static std::string GenerateUuid() {
+ uuid_t uuid_bytes;
+ uuid_generate(uuid_bytes);
+
+ char uuid_chars[37] = {};
+ uuid_unparse_lower(uuid_bytes, uuid_chars);
+
+ return std::string{uuid_chars};
+}
+
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms) {
+ std::string uuid = GenerateUuid();
+ if (!CreateDevice(name, uuid)) {
+ return false;
+ }
+
+ // We use the unique path for testing whether the device is ready. After
+ // that, it's safe to use the dm-N path which is compatible with callers
+ // that expect it to be formatted as such.
+ std::string unique_path;
+ if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
+ !GetDmDevicePathByName(name, path)) {
+ DeleteDevice(name);
+ return false;
+ }
+
+ if (timeout_ms <= std::chrono::milliseconds::zero()) {
+ return true;
+ }
+
+ auto condition = [&]() -> bool {
+ // If the file exists but returns EPERM or something, we consider the
+ // condition met.
+ if (access(unique_path.c_str(), F_OK) != 0) {
+ if (errno == ENOENT) return false;
+ }
+ return true;
+ };
+ if (!WaitForCondition(condition, timeout_ms)) {
+ LOG(ERROR) << "Timed out waiting for device path: " << unique_path;
+ DeleteDevice(name);
+ return false;
+ }
+ return true;
+}
+
+bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(ERROR) << "Failed to get device path: " << name;
+ return false;
+ }
+
+ if (io.uuid[0] == '\0') {
+ LOG(ERROR) << "Device does not have a unique path: " << name;
+ return false;
+ }
+ *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
+ return true;
}
DmDeviceState DeviceMapper::GetState(const std::string& name) const {
@@ -111,11 +184,8 @@
}
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
- if (!CreateDevice(name)) {
- return false;
- }
- if (!LoadTableAndActivate(name, table)) {
- DeleteDevice(name);
+ std::string ignore_path;
+ if (!CreateDevice(name, table, &ignore_path, 0ms)) {
return false;
}
return true;
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index c5881dd..7a834e2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -52,10 +52,10 @@
public:
TempDevice(const std::string& name, const DmTable& table)
: dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
- valid_ = dm_.CreateDevice(name, table);
+ valid_ = dm_.CreateDevice(name, table, &path_, 5s);
}
TempDevice(TempDevice&& other) noexcept
- : dm_(other.dm_), name_(other.name_), valid_(other.valid_) {
+ : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
other.valid_ = false;
}
~TempDevice() {
@@ -70,29 +70,7 @@
valid_ = false;
return dm_.DeleteDevice(name_);
}
- bool WaitForUdev() const {
- auto start_time = std::chrono::steady_clock::now();
- while (true) {
- if (!access(path().c_str(), F_OK)) {
- return true;
- }
- if (errno != ENOENT) {
- return false;
- }
- std::this_thread::sleep_for(50ms);
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time;
- if (elapsed >= 5s) {
- return false;
- }
- }
- }
- std::string path() const {
- std::string device_path;
- if (!dm_.GetDmDevicePathByName(name_, &device_path)) {
- return "";
- }
- return device_path;
- }
+ std::string path() const { return path_; }
const std::string& name() const { return name_; }
bool valid() const { return valid_; }
@@ -109,6 +87,7 @@
private:
DeviceMapper& dm_;
std::string name_;
+ std::string path_;
bool valid_;
};
@@ -139,7 +118,6 @@
TempDevice dev("libdm-test-dm-linear", table);
ASSERT_TRUE(dev.valid());
ASSERT_FALSE(dev.path().empty());
- ASSERT_TRUE(dev.WaitForUdev());
auto& dm = DeviceMapper::Instance();
@@ -290,7 +268,6 @@
origin_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot-origin", origin_table);
ASSERT_TRUE(origin_dev_->valid());
ASSERT_FALSE(origin_dev_->path().empty());
- ASSERT_TRUE(origin_dev_->WaitForUdev());
// chunk size = 4K blocks.
DmTable snap_table;
@@ -302,7 +279,6 @@
snapshot_dev_ = std::make_unique<TempDevice>("libdm-test-dm-snapshot", snap_table);
ASSERT_TRUE(snapshot_dev_->valid());
ASSERT_FALSE(snapshot_dev_->path().empty());
- ASSERT_TRUE(snapshot_dev_->WaitForUdev());
setup_ok_ = true;
}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 08376c0..9c0c2f3 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -25,6 +25,7 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
#include <memory>
#include <string>
#include <utility>
@@ -73,10 +74,6 @@
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
- // Reads the device mapper table from the device with given anme and
- // returns it in a DmTable object.
- const std::unique_ptr<DmTable> table(const std::string& name) const;
-
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
@@ -84,6 +81,33 @@
// Creates a device, loads the given table, and activates it. If the device
// is not able to be activated, it is destroyed, and false is returned.
+ // After creation, |path| contains the result of calling
+ // GetDmDevicePathByName, and the path is guaranteed to exist. If after
+ // |timeout_ms| the path is not available, the device will be deleted and
+ // this function will return false.
+ //
+ // This variant must be used when depending on the device path. The
+ // following manual sequence should not be used:
+ //
+ // 1. CreateDevice(name, table)
+ // 2. GetDmDevicePathByName(name, &path)
+ // 3. fs_mgr::WaitForFile(path, <timeout>)
+ //
+ // This sequence has a race condition where, if another process deletes a
+ // device, CreateDevice may acquire the same path. When this happens, the
+ // WaitForFile() may early-return since ueventd has not yet processed all
+ // of the outstanding udev events. The caller may unexpectedly get an
+ // ENOENT on a system call using the affected path.
+ //
+ // If |timeout_ms| is 0ms, then this function will return true whether or
+ // not |path| is available. It is the caller's responsibility to ensure
+ // there are no races.
+ bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+ const std::chrono::milliseconds& timeout_ms);
+
+ // Create a device and activate the given table, without waiting to acquire
+ // a valid path. If the caller will use GetDmDevicePathByName(), it should
+ // use the timeout variant above.
bool CreateDevice(const std::string& name, const DmTable& table);
// Loads the device mapper table from parameter into the underlying device
@@ -110,8 +134,21 @@
// Returns the path to the device mapper device node in '/dev' corresponding to
// 'name'. If the device does not exist, false is returned, and the path
// parameter is not set.
+ //
+ // This returns a path in the format "/dev/block/dm-N" that can be easily
+ // re-used with sysfs.
+ //
+ // WaitForFile() should not be used in conjunction with this call, since it
+ // could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns a device's unique path as generated by ueventd. This will return
+ // true as long as the device has been created, even if ueventd has not
+ // processed it yet.
+ //
+ // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.
+ bool GetDeviceUniquePath(const std::string& name, std::string* path);
+
// Returns the dev_t for the named device-mapper node.
bool GetDeviceNumber(const std::string& name, dev_t* dev);
@@ -158,18 +195,12 @@
// limit we are imposing here of 256.
static constexpr uint32_t kMaxPossibleDmDevices = 256;
+ bool CreateDevice(const std::string& name, const std::string& uuid = {});
bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);
-
void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;
DeviceMapper();
- // Creates a device mapper device with given name.
- // Return 'true' on success and 'false' on failure to
- // create OR if a device mapper device with the same name already
- // exists.
- bool CreateDevice(const std::string& name);
-
int fd_;
// Non-copyable & Non-movable
DeviceMapper(const DeviceMapper&) = delete;
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index a3c76ab..414a186 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -61,6 +61,7 @@
"libavb",
"libavb_host_sysdeps",
"libdm",
+ "libext2_uuid",
"libfs_avb",
"libfstab",
"libgtest_host",
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index d9650f3..4505382 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -104,31 +104,23 @@
}
table.set_readonly(true);
+ std::chrono::milliseconds timeout = {};
+ if (wait_for_verity_dev) timeout = 1s;
+
+ std::string dev_path;
const std::string mount_point(Basename(fstab_entry->mount_point));
const std::string device_name(GetVerityDeviceName(*fstab_entry));
android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (!dm.CreateDevice(device_name, table)) {
+ if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
LERROR << "Couldn't create verity device!";
return false;
}
- std::string dev_path;
- if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
- LERROR << "Couldn't get verity device path!";
- return false;
- }
-
// Marks the underlying block device as read-only.
SetBlockDeviceReadOnly(fstab_entry->blk_device);
// Updates fstab_rec->blk_device to verity device name.
fstab_entry->blk_device = dev_path;
-
- // Makes sure we've set everything up properly.
- if (wait_for_verity_dev && !WaitForFile(dev_path, 1s)) {
- return false;
- }
-
return true;
}
diff --git a/init/Android.bp b/init/Android.bp
index 86dcb4c..ee339dd 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -74,7 +74,6 @@
shared_libs: [
"libbacktrace",
"libbase",
- "libbinder",
"libbootloader_message",
"libcutils",
"libcrypto",
@@ -243,11 +242,12 @@
],
whole_static_libs: ["libcap"],
shared_libs: [
- "libprotobuf-cpp-lite",
- "libhidl-gen-utils",
- "libprocessgroup",
- "liblog",
"libcutils",
+ "libhidl-gen-utils",
+ "libjsoncpp",
+ "liblog",
+ "libprocessgroup",
+ "libprotobuf-cpp-lite",
],
srcs: [
"action.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index 9017772..006e1bf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -113,6 +113,7 @@
libunwindstack \
libbacktrace \
libmodprobe \
+ libext2_uuid \
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
diff --git a/init/action.cpp b/init/action.cpp
index a169591..8cf7645 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -95,7 +95,7 @@
}
void Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {
- commands_.emplace_back(f, false, std::move(args), line);
+ commands_.emplace_back(std::move(f), false, std::move(args), line);
}
std::size_t Action::NumCommands() const {
diff --git a/init/action_manager.cpp b/init/action_manager.cpp
index 985b8ad..541c8f2 100644
--- a/init/action_manager.cpp
+++ b/init/action_manager.cpp
@@ -47,7 +47,7 @@
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
- action->AddCommand(func, {name}, 0);
+ action->AddCommand(std::move(func), {name}, 0);
event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
diff --git a/init/devices.cpp b/init/devices.cpp
index e8e6cd7..f6e453a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -22,7 +22,6 @@
#include <unistd.h>
#include <chrono>
-#include <map>
#include <memory>
#include <string>
#include <thread>
@@ -111,24 +110,16 @@
// the supplied buffer with the dm module's instantiated name.
// If it doesn't start with a virtual block device, or there is some
// error, return false.
-static bool FindDmDevicePartition(const std::string& path, std::string* result) {
- result->clear();
+static bool FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
- if (getpid() == 1) return false; // first_stage_init has no sepolicy needs
- static std::map<std::string, std::string> cache;
- // wait_for_file will not work, the content is also delayed ...
- for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
- if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
- // Got it, set cache with result, when node arrives
- cache[path] = *result = Trim(*result);
- return true;
- }
+ if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
+ return false;
}
- auto it = cache.find(path);
- if ((it == cache.end()) || (it->second.empty())) return false;
- // Return cached results, when node goes away
- *result = it->second;
+ ReadFileToString("/sys" + path + "/dm/uuid", uuid);
+
+ *name = android::base::Trim(*name);
+ *uuid = android::base::Trim(*uuid);
return true;
}
@@ -325,6 +316,7 @@
std::string device;
std::string type;
std::string partition;
+ std::string uuid;
if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
@@ -342,8 +334,12 @@
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
- } else if (FindDmDevicePartition(uevent.path, &partition)) {
- return {"/dev/block/mapper/" + partition};
+ } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
+ std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
+ if (!uuid.empty()) {
+ symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
+ }
+ return symlinks;
} else {
return {};
}
@@ -379,10 +375,41 @@
return links;
}
+static void RemoveDeviceMapperLinks(const std::string& devpath) {
+ std::vector<std::string> dirs = {
+ "/dev/block/mapper",
+ "/dev/block/mapper/by-uuid",
+ };
+ for (const auto& dir : dirs) {
+ if (access(dir.c_str(), F_OK) != 0) continue;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
+ if (!dh) {
+ PLOG(ERROR) << "Failed to open directory " << dir;
+ continue;
+ }
+
+ struct dirent* dp;
+ std::string link_path;
+ while ((dp = readdir(dh.get())) != nullptr) {
+ if (dp->d_type != DT_LNK) continue;
+
+ auto path = dir + "/" + dp->d_name;
+ if (Readlink(path, &link_path) && link_path == devpath) {
+ unlink(path.c_str());
+ }
+ }
+ }
+}
+
void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
+ }
+
+ // We don't have full device-mapper information until a change event is fired.
+ if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
for (const auto& link : links) {
if (!mkdir_recursive(Dirname(link), 0755)) {
PLOG(ERROR) << "Failed to create directory " << Dirname(link);
@@ -401,6 +428,9 @@
}
if (action == "remove") {
+ if (StartsWith(devpath, "/dev/block/dm-")) {
+ RemoveDeviceMapperLinks(devpath);
+ }
for (const auto& link : links) {
std::string link_path;
if (Readlink(link, &link_path) && link_path == devpath) {
diff --git a/init/devices.h b/init/devices.h
index 9d39eaa..442c53f 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -84,9 +84,9 @@
};
Subsystem() {}
- Subsystem(const std::string& name) : name_(name) {}
- Subsystem(const std::string& name, DevnameSource source, const std::string& dir_name)
- : name_(name), devname_source_(source), dir_name_(dir_name) {}
+ Subsystem(std::string name) : name_(std::move(name)) {}
+ Subsystem(std::string name, DevnameSource source, std::string dir_name)
+ : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_name)) {}
// Returns the full path for a uevent of a device that is a member of this subsystem,
// according to the rules parsed from ueventd.rc
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 3e7c1a8..c408bc1 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -30,7 +30,7 @@
class DeviceHandlerTester {
public:
void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
- const std::vector<std::string> expected_links) {
+ const std::vector<std::string>& expected_links) {
TemporaryDir fake_sys_root;
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 8aa3509..92c2aa5 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
@@ -29,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <json/json.h>
#include "action.h"
#include "action_manager.h"
@@ -129,21 +131,33 @@
return nullptr;
}
-static std::optional<std::set<std::string>> ReadKnownInterfaces(
- const std::string& known_interfaces_file) {
- if (known_interfaces_file.empty()) {
- LOG(WARNING) << "Missing a known interfaces file.";
+static std::optional<android::init::InterfaceInheritanceHierarchyMap>
+ReadInterfaceInheritanceHierarchy(const std::string& interface_inheritance_hierarchy_file) {
+ if (interface_inheritance_hierarchy_file.empty()) {
+ LOG(WARNING) << "Missing an interface inheritance hierarchy file.";
return {};
}
- std::string known_interfaces;
- if (!ReadFileToString(known_interfaces_file, &known_interfaces)) {
- LOG(ERROR) << "Failed to read known interfaces file '" << known_interfaces_file << "'";
+ Json::Value root;
+ Json::Reader reader;
+ std::ifstream stream(interface_inheritance_hierarchy_file);
+ if (!reader.parse(stream, root)) {
+ LOG(ERROR) << "Failed to read interface inheritance hierarchy file: "
+ << interface_inheritance_hierarchy_file << "\n"
+ << reader.getFormattedErrorMessages();
return {};
}
- auto interfaces = Split(known_interfaces, " ");
- return std::set<std::string>(interfaces.begin(), interfaces.end());
+ android::init::InterfaceInheritanceHierarchyMap result;
+ for (const Json::Value& entry : root) {
+ std::set<std::string> inherited_interfaces;
+ for (const Json::Value& intf : entry["inheritedInterfaces"]) {
+ inherited_interfaces.insert(intf.asString());
+ }
+ result[entry["interface"].asString()] = inherited_interfaces;
+ }
+
+ return result;
}
namespace android {
@@ -169,7 +183,7 @@
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
- std::string known_interfaces_file;
+ std::string interface_inheritance_hierarchy_file;
while (true) {
static const struct option long_options[] = {
@@ -177,7 +191,7 @@
{nullptr, 0, nullptr, 0},
};
- int arg = getopt_long(argc, argv, "p:k:", long_options, nullptr);
+ int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
if (arg == -1) {
break;
@@ -190,8 +204,8 @@
case 'p':
passwd_files.emplace_back(optarg);
break;
- case 'k':
- known_interfaces_file = optarg;
+ case 'i':
+ interface_inheritance_hierarchy_file = optarg;
break;
default:
std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
@@ -213,8 +227,10 @@
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser(
- "service", std::make_unique<ServiceParser>(&sl, nullptr,
- ReadKnownInterfaces(known_interfaces_file)));
+ "service",
+ std::make_unique<ServiceParser>(
+ &sl, nullptr,
+ ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file)));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
diff --git a/init/init.cpp b/init/init.cpp
index 675f3e5..5dba54d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -50,10 +50,6 @@
#include <processgroup/setup.h>
#include <selinux/android.h>
-#ifndef RECOVERY
-#include <binder/ProcessState.h>
-#endif
-
#include "action_parser.h"
#include "boringssl_self_test.h"
#include "builtins.h"
@@ -139,12 +135,12 @@
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
+ // late_import is available only in Q and earlier release. As we don't
+ // have system_ext in those versions, skip late_import for system_ext.
+ parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
- if (!parser.ParseConfig("/product_services/etc/init")) {
- late_import_paths.emplace_back("/product_services/etc/init");
- }
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
@@ -453,24 +449,6 @@
return {};
}
-static Result<void> InitBinder(const BuiltinArguments& args) {
- // init's use of binder is very limited. init cannot:
- // - have any binder threads
- // - receive incoming binder calls
- // - pass local binder services to remote processes
- // - use death recipients
- // The main supported usecases are:
- // - notifying other daemons (oneway calls only)
- // - retrieving data that is necessary to boot
- // Also, binder can't be used by recovery.
-#ifndef RECOVERY
- android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
- android::ProcessState::self()->setCallRestriction(
- ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY);
-#endif
- return {};
-}
-
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -766,9 +744,6 @@
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
- // Initialize binder before bringing up other system services
- am.QueueBuiltinAction(InitBinder, "InitBinder");
-
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
diff --git a/init/keychords.cpp b/init/keychords.cpp
index d0ca3e7..5f2682b 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -285,7 +285,7 @@
void Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {
epoll_ = epoll;
- handler_ = handler;
+ handler_ = std::move(handler);
if (entries_.size()) GeteventOpenDevice();
}
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index b0b63c5..791a019 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -140,11 +140,11 @@
}
}
free(buf);
- for (auto entry : untouched) {
+ for (auto& entry : untouched) {
SetMountProperty(entry, false);
mounts_.erase(entry);
}
- for (auto entry : touched) {
+ for (auto& entry : touched) {
SetMountProperty(entry, true);
mounts_.emplace(std::move(entry));
}
diff --git a/init/parser.cpp b/init/parser.cpp
index bbfbdc6..6ab61cb 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -37,7 +37,7 @@
}
void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
- line_callbacks_.emplace_back(prefix, callback);
+ line_callbacks_.emplace_back(prefix, std::move(callback));
}
void Parser::ParseData(const std::string& filename, std::string* data) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index b89914f..8623c30 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -782,10 +782,9 @@
"brand", "device", "manufacturer", "model", "name",
};
const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
- "odm", "product", "product_services", "system", "vendor",
+ "odm", "product", "system_ext", "system", "vendor",
};
- const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
- "product,product_services,odm,vendor,system";
+ const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
const std::string EMPTY = "";
std::string ro_product_props_source_order =
@@ -892,6 +891,7 @@
}
}
load_properties_from_file("/system/build.prop", nullptr, &properties);
+ load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
@@ -901,7 +901,6 @@
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
load_properties_from_file("/product/build.prop", nullptr, &properties);
- load_properties_from_file("/product_services/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (load_debug_prop) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index d9d885c..cb54d34 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -636,11 +636,9 @@
bool run_fsck = false;
bool command_invalid = false;
- if (cmd_params.size() > 3) {
- command_invalid = true;
- } else if (cmd_params[0] == "shutdown") {
+ if (cmd_params[0] == "shutdown") {
cmd = ANDROID_RB_POWEROFF;
- if (cmd_params.size() == 2) {
+ if (cmd_params.size() >= 2) {
if (cmd_params[1] == "userrequested") {
// The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
// Run fsck once the file system is remounted in read-only mode.
@@ -671,6 +669,13 @@
"bootloader_message: "
<< err;
}
+ } else if (reboot_target == "recovery") {
+ const std::vector<std::string> options = {};
+ std::string err;
+ if (!write_bootloader_message(options, &err)) {
+ LOG(ERROR) << "Failed to set bootloader message: " << err;
+ return false;
+ }
} else if (reboot_target == "sideload" || reboot_target == "sideload-auto-reboot" ||
reboot_target == "fastboot") {
std::string arg = reboot_target == "sideload-auto-reboot" ? "sideload_auto_reboot"
@@ -686,9 +691,9 @@
reboot_target = "recovery";
}
- // If there is an additional parameter, pass it along
- if ((cmd_params.size() == 3) && cmd_params[2].size()) {
- reboot_target += "," + cmd_params[2];
+ // If there are additional parameter, pass them along
+ for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {
+ reboot_target += "," + cmd_params[i];
}
}
} else {
diff --git a/init/security.cpp b/init/security.cpp
index 5d87f3c..586d0c7 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -83,7 +83,7 @@
return {};
}
-static bool SetHighestAvailableOptionValue(std::string path, int min, int max) {
+static bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {
std::ifstream inf(path, std::fstream::in);
if (!inf) {
LOG(ERROR) << "Cannot open for reading: " << path;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index ba35104..88ce364 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -18,6 +18,9 @@
#include <linux/input.h>
+#include <algorithm>
+#include <sstream>
+
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -152,12 +155,6 @@
return Error() << "Interface name must not be a value name '" << interface_name << "'";
}
- if (known_interfaces_ && known_interfaces_->count(interface_name) == 0) {
- return Error() << "Interface is not in the known set of hidl_interfaces: '"
- << interface_name << "'. Please ensure the interface is built "
- << "by a hidl_interface target.";
- }
-
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
@@ -540,6 +537,37 @@
return {};
}
+ if (interface_inheritance_hierarchy_) {
+ std::set<std::string> interface_names;
+ for (const std::string& intf : service_->interfaces()) {
+ interface_names.insert(Split(intf, "/")[0]);
+ }
+ std::ostringstream error_stream;
+ for (const std::string& intf : interface_names) {
+ if (interface_inheritance_hierarchy_->count(intf) == 0) {
+ error_stream << "\nInterface is not in the known set of hidl_interfaces: '" << intf
+ << "'. Please ensure the interface is spelled correctly and built "
+ << "by a hidl_interface target.";
+ continue;
+ }
+ const std::set<std::string>& required_interfaces =
+ (*interface_inheritance_hierarchy_)[intf];
+ std::set<std::string> diff;
+ std::set_difference(required_interfaces.begin(), required_interfaces.end(),
+ interface_names.begin(), interface_names.end(),
+ std::inserter(diff, diff.begin()));
+ if (!diff.empty()) {
+ error_stream << "\nInterface '" << intf << "' requires its full inheritance "
+ << "hierarchy to be listed in this init_rc file. Missing "
+ << "interfaces: [" << base::Join(diff, " ") << "]";
+ }
+ }
+ const std::string& errors = error_stream.str();
+ if (!errors.empty()) {
+ return Error() << errors;
+ }
+ }
+
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 5a16768..5ad26ef 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -26,13 +26,16 @@
namespace android {
namespace init {
+using InterfaceInheritanceHierarchyMap = std::map<std::string, std::set<std::string>>;
+
class ServiceParser : public SectionParser {
public:
- ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts,
- const std::optional<std::set<std::string>>& known_interfaces)
+ ServiceParser(
+ ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+ const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
: service_list_(service_list),
subcontexts_(subcontexts),
- known_interfaces_(known_interfaces),
+ interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
@@ -85,7 +88,7 @@
ServiceList* service_list_;
std::vector<Subcontext>* subcontexts_;
- std::optional<std::set<std::string>> known_interfaces_;
+ std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
};
diff --git a/init/test_function_map.h b/init/test_function_map.h
index 293f1f9..466836c 100644
--- a/init/test_function_map.h
+++ b/init/test_function_map.h
@@ -30,17 +30,17 @@
public:
// Helper for argument-less functions
using BuiltinFunctionNoArgs = std::function<void(void)>;
- void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
- Add(name, 0, 0, false, [function](const BuiltinArguments&) {
- function();
+ void Add(const std::string& name, BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, false, [f = std::move(function)](const BuiltinArguments&) {
+ f();
return Result<void>{};
});
}
void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
- bool run_in_subcontext, const BuiltinFunction function) {
- builtin_functions_[name] =
- make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+ bool run_in_subcontext, BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters,
+ make_pair(run_in_subcontext, std::move(function)));
}
private:
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 3b9de0f..8b2cf62 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -146,7 +146,7 @@
void ColdBoot::RegenerateUevents() {
uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {
- uevent_queue_.emplace_back(std::move(uevent));
+ uevent_queue_.emplace_back(uevent);
return ListenerAction::kContinue;
});
}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 897a169..b29638c 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -106,7 +106,7 @@
// oem/ file-system since the intent is to provide support for customized
// portions of a separate vendor.img or oem.img. Has to remain open so that
// customization can also land on /system/vendor, /system/oem, /system/odm,
-// /system/product or /system/product_services.
+// /system/product or /system/system_ext.
//
// We expect build-time checking or filtering when constructing the associated
// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
@@ -118,15 +118,12 @@
static const char odm_conf_file[] = "/odm/etc/fs_config_files";
static const char product_conf_dir[] = "/product/etc/fs_config_dirs";
static const char product_conf_file[] = "/product/etc/fs_config_files";
-static const char product_services_conf_dir[] = "/product_services/etc/fs_config_dirs";
-static const char product_services_conf_file[] = "/product_services/etc/fs_config_files";
+static const char system_ext_conf_dir[] = "/system_ext/etc/fs_config_dirs";
+static const char system_ext_conf_file[] = "/system_ext/etc/fs_config_files";
static const char* conf[][2] = {
- {sys_conf_file, sys_conf_dir},
- {ven_conf_file, ven_conf_dir},
- {oem_conf_file, oem_conf_dir},
- {odm_conf_file, odm_conf_dir},
- {product_conf_file, product_conf_dir},
- {product_services_conf_file, product_services_conf_dir},
+ {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir},
+ {oem_conf_file, oem_conf_dir}, {odm_conf_file, odm_conf_dir},
+ {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir},
};
// Do not use android_files to grant Linux capabilities. Use ambient capabilities in their
@@ -158,9 +155,9 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "product/build.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, product_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, product_conf_file + 1 },
- { 00600, AID_ROOT, AID_ROOT, 0, "product_services/build.prop" },
- { 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_dir + 1 },
- { 00444, AID_ROOT, AID_ROOT, 0, product_services_conf_file + 1 },
+ { 00600, AID_ROOT, AID_ROOT, 0, "system_ext/build.prop" },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, system_ext_conf_file + 1 },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump32" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/crash_dump64" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/bin/debuggerd" },
@@ -248,9 +245,9 @@
}
// if path is "odm/<stuff>", "oem/<stuff>", "product/<stuff>",
-// "product_services/<stuff>" or "vendor/<stuff>"
+// "system_ext/<stuff>" or "vendor/<stuff>"
static bool is_partition(const std::string& path) {
- static const char* partitions[] = {"odm/", "oem/", "product/", "product_services/", "vendor/"};
+ static const char* partitions[] = {"odm/", "oem/", "product/", "system_ext/", "vendor/"};
for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {
if (StartsWith(path, partitions[i])) return true;
}
@@ -285,10 +282,8 @@
if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;
// Check match between logical partition's files and patterns.
- static constexpr const char* kLogicalPartitions[] = {"system/product/",
- "system/product_services/",
- "system/vendor/",
- "vendor/odm/"};
+ static constexpr const char* kLogicalPartitions[] = {"system/product/", "system/system_ext/",
+ "system/vendor/", "vendor/odm/"};
for (auto& logical_partition : kLogicalPartitions) {
if (StartsWith(input, logical_partition)) {
std::string input_in_partition = input.substr(input.find('/') + 1);
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 7fa3f43..a4b3cd7 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -407,63 +407,15 @@
}
int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) {
- struct iovec vec[3];
- char tmp_tag[32];
-
if (!tag) tag = "";
- /* XXX: This needs to go! */
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wstring-plus-int"
- if (bufID != LOG_ID_RADIO) {
- switch (tag[0]) {
- case 'H':
- if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
- goto inform;
- case 'R':
- /* Any log tag with "RIL" as the prefix */
- if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
- goto inform;
- case 'Q':
- /* Any log tag with "QC_RIL" as the prefix */
- if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
- goto inform;
- case 'I':
- /* Any log tag with "IMS" as the prefix */
- if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
- goto inform;
- case 'A':
- if (strcmp(tag + 1, "AT" + 1)) break;
- goto inform;
- case 'G':
- if (strcmp(tag + 1, "GSM" + 1)) break;
- goto inform;
- case 'S':
- if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
- goto inform;
- case 'C':
- if (strcmp(tag + 1, "CDMA" + 1)) break;
- goto inform;
- case 'P':
- if (strcmp(tag + 1, "PHONE" + 1)) break;
- /* FALLTHRU */
- inform:
- bufID = LOG_ID_RADIO;
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
- [[fallthrough]];
- default:
- break;
- }
- }
-#pragma clang diagnostic pop
-
#if __BIONIC__
if (prio == ANDROID_LOG_FATAL) {
android_set_abort_message(msg);
}
#endif
+ struct iovec vec[3];
vec[0].iov_base = (unsigned char*)&prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void*)tag;
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 5423de5..a0a6b4f 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -246,6 +246,7 @@
"tests/files/offline/jit_debug_x86/*",
"tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
+ "tests/files/offline/load_bias_ro_rx_x86_64/*",
"tests/files/offline/offset_arm/*",
"tests/files/offline/shared_lib_in_apk_arm64/*",
"tests/files/offline/shared_lib_in_apk_memory_only_arm64/*",
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index f0e4138..bdfee01 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -197,6 +197,7 @@
template <typename EhdrType, typename PhdrType>
void ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias) {
uint64_t offset = ehdr.e_phoff;
+ bool first_exec_load_header = true;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
if (!memory_->ReadFully(offset, &phdr, sizeof(phdr))) {
@@ -212,9 +213,11 @@
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
static_cast<size_t>(phdr.p_memsz)};
- if (phdr.p_offset == 0) {
- *load_bias = phdr.p_vaddr;
+ // Only set the load bias from the first executable load header.
+ if (first_exec_load_header && phdr.p_vaddr > phdr.p_offset) {
+ *load_bias = phdr.p_vaddr - phdr.p_offset;
}
+ first_exec_load_header = false;
break;
}
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index cdc927a..f9ee9eb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -360,7 +360,7 @@
uint64_t load_bias = 0;
ASSERT_TRUE(elf->Init(&load_bias));
- EXPECT_EQ(0U, load_bias);
+ EXPECT_EQ(0x1001U, load_bias);
const std::unordered_map<uint64_t, LoadInfo>& pt_loads = elf->pt_loads();
ASSERT_EQ(1U, pt_loads.size());
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index baada82..e6158a2 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -1457,4 +1457,77 @@
EXPECT_EQ(0xc2044218, unwinder.frames()[0].sp);
}
+TEST_F(UnwindOfflineTest, load_bias_ro_rx_x86_64) {
+ ASSERT_NO_FATAL_FAILURE(Init("load_bias_ro_rx_x86_64/", ARCH_X86_64));
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(17U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000000e9dd4 libc.so (__write+20)\n"
+ " #01 pc 000000000007ab9c libc.so (_IO_file_write+44)\n"
+ " #02 pc 0000000000079f3e libc.so\n"
+ " #03 pc 000000000007bce8 libc.so (_IO_do_write+24)\n"
+ " #04 pc 000000000007b26e libc.so (_IO_file_xsputn+270)\n"
+ " #05 pc 000000000004f7f9 libc.so (_IO_vfprintf+1945)\n"
+ " #06 pc 0000000000057cb5 libc.so (_IO_printf+165)\n"
+ " #07 pc 0000000000ed1796 perfetto_unittests "
+ "(testing::internal::PrettyUnitTestResultPrinter::OnTestIterationStart(testing::UnitTest "
+ "const&, int)+374)\n"
+ " #08 pc 0000000000ed30fd perfetto_unittests "
+ "(testing::internal::TestEventRepeater::OnTestIterationStart(testing::UnitTest const&, "
+ "int)+125)\n"
+ " #09 pc 0000000000ed5e25 perfetto_unittests "
+ "(testing::internal::UnitTestImpl::RunAllTests()+581)\n"
+ " #10 pc 0000000000ef63f3 perfetto_unittests "
+ "(_ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_"
+ "MS4_FS3_vEPKc+131)\n"
+ " #11 pc 0000000000ee2a21 perfetto_unittests "
+ "(_ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_"
+ "FS3_vEPKc+113)\n"
+ " #12 pc 0000000000ed5bb9 perfetto_unittests (testing::UnitTest::Run()+185)\n"
+ " #13 pc 0000000000e900f0 perfetto_unittests (RUN_ALL_TESTS()+16)\n"
+ " #14 pc 0000000000e900d8 perfetto_unittests (main+56)\n"
+ " #15 pc 000000000002352a libc.so (__libc_start_main+234)\n"
+ " #16 pc 0000000000919029 perfetto_unittests (_start+41)\n",
+ frame_info);
+
+ EXPECT_EQ(0x7f9326a57dd4ULL, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffd224153c8ULL, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x7f93269e8b9cULL, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffd224153d0ULL, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x7f93269e7f3eULL, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffd22415400ULL, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x7f93269e9ce8ULL, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffd22415440ULL, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7f93269e926eULL, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffd22415450ULL, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x7f93269bd7f9ULL, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7ffd22415490ULL, unwinder.frames()[5].sp);
+ EXPECT_EQ(0x7f93269c5cb5ULL, unwinder.frames()[6].pc);
+ EXPECT_EQ(0x7ffd22415a10ULL, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xed1796ULL, unwinder.frames()[7].pc);
+ EXPECT_EQ(0x7ffd22415af0ULL, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xed30fdULL, unwinder.frames()[8].pc);
+ EXPECT_EQ(0x7ffd22415b70ULL, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xed5e25ULL, unwinder.frames()[9].pc);
+ EXPECT_EQ(0x7ffd22415bb0ULL, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xef63f3ULL, unwinder.frames()[10].pc);
+ EXPECT_EQ(0x7ffd22415c60ULL, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xee2a21ULL, unwinder.frames()[11].pc);
+ EXPECT_EQ(0x7ffd22415cc0ULL, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xed5bb9ULL, unwinder.frames()[12].pc);
+ EXPECT_EQ(0x7ffd22415d40ULL, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xe900f0ULL, unwinder.frames()[13].pc);
+ EXPECT_EQ(0x7ffd22415d90ULL, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xe900d8ULL, unwinder.frames()[14].pc);
+ EXPECT_EQ(0x7ffd22415da0ULL, unwinder.frames()[14].sp);
+ EXPECT_EQ(0x7f932699152aULL, unwinder.frames()[15].pc);
+ EXPECT_EQ(0x7ffd22415dd0ULL, unwinder.frames()[15].sp);
+ EXPECT_EQ(0x919029ULL, unwinder.frames()[16].pc);
+ EXPECT_EQ(0x7ffd22415e90ULL, unwinder.frames()[16].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
new file mode 100644
index 0000000..63383d0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
new file mode 100644
index 0000000..ba5a31b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/maps.txt
@@ -0,0 +1,3 @@
+200000-919000 r--p 0 00:00 0 perfetto_unittests
+919000-1a0c000 r-xp 719000 00:00 0 perfetto_unittests
+7f932696e000-7f9326b23000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
new file mode 100644
index 0000000..a30e599
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/perfetto_unittests
Binary files differ
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
new file mode 100644
index 0000000..6cb4055
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/regs.txt
@@ -0,0 +1,17 @@
+rax: 3b
+rbx: 3b
+rcx: 7f9326a57dd4
+rdx: 3b
+r8: 7ffd22415b09
+r9: 7ffd224155e0
+r10: 0
+r11: 246
+r12: 7f9326d28760
+r13: 3b
+r14: 7f9326d23760
+r15: 3b
+rdi: 1
+rsi: 2678850
+rbp: 2678850
+rsp: 7ffd224153c8
+rip: 7f9326a57dd4
diff --git a/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
new file mode 100644
index 0000000..4edfe07
--- /dev/null
+++ b/libunwindstack/tests/files/offline/load_bias_ro_rx_x86_64/stack.data
Binary files differ
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index a105474..3a02a8a 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -188,9 +188,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class TextOutput;
-TextOutput& printWeakPointer(TextOutput& to, const void* val);
-
// ---------------------------------------------------------------------------
#define COMPARE_WEAK(_op_) \
@@ -459,9 +456,6 @@
weakref_type* m_refs;
};
-template <typename T>
-TextOutput& operator<<(TextOutput& to, const wp<T>& val);
-
#undef COMPARE_WEAK
// ---------------------------------------------------------------------------
@@ -635,12 +629,6 @@
}
}
-template <typename T>
-inline TextOutput& operator<<(TextOutput& to, const wp<T>& val)
-{
- return printWeakPointer(to, val.unsafe_get());
-}
-
// ---------------------------------------------------------------------------
// this class just serves as a namespace so TYPE::moveReferences can stay
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 48140b8..42af751 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -79,6 +79,7 @@
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define MEMINFO_PATH "/proc/meminfo"
+#define PROC_STATUS_TGID_FIELD "Tgid:"
#define LINE_MAX 128
/* Android Logger event logtags (see event.logtags) */
@@ -551,6 +552,49 @@
(to->tv_nsec - from->tv_nsec) / (long)NS_PER_MS;
}
+static int proc_get_tgid(int pid) {
+ char path[PATH_MAX];
+ char buf[PAGE_SIZE];
+ int fd;
+ ssize_t size;
+ char *pos;
+ int64_t tgid = -1;
+
+ snprintf(path, PATH_MAX, "/proc/%d/status", pid);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ return -1;
+ }
+
+ size = read_all(fd, buf, sizeof(buf) - 1);
+ if (size < 0) {
+ goto out;
+ }
+ buf[size] = 0;
+
+ pos = buf;
+ while (true) {
+ pos = strstr(pos, PROC_STATUS_TGID_FIELD);
+ /* Stop if TGID tag not found or found at the line beginning */
+ if (pos == NULL || pos == buf || pos[-1] == '\n') {
+ break;
+ }
+ pos++;
+ }
+
+ if (pos == NULL) {
+ goto out;
+ }
+
+ pos += strlen(PROC_STATUS_TGID_FIELD);
+ while (*pos == ' ') pos++;
+ parse_int64(pos, &tgid);
+
+out:
+ close(fd);
+ return (int)tgid;
+}
+
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
@@ -559,6 +603,7 @@
struct lmk_procprio params;
bool is_system_server;
struct passwd *pwdrec;
+ int tgid;
lmkd_pack_get_procprio(packet, ¶ms);
@@ -568,6 +613,14 @@
return;
}
+ /* Check if registered process is a thread group leader */
+ tgid = proc_get_tgid(params.pid);
+ if (tgid >= 0 && tgid != params.pid) {
+ ALOGE("Attempt to register a task that is not a thread group leader (tid %d, tgid %d)",
+ params.pid, tgid);
+ return;
+ }
+
/* gid containing AID_READPROC required */
/* CAP_SYS_RESOURCE required */
/* CAP_DAC_OVERRIDE required */
@@ -1332,6 +1385,7 @@
static int kill_one_process(struct proc* procp, int min_oom_score) {
int pid = procp->pid;
uid_t uid = procp->uid;
+ int tgid;
char *taskname;
int tasksize;
int r;
@@ -1345,6 +1399,12 @@
(void)(min_oom_score);
#endif
+ tgid = proc_get_tgid(pid);
+ if (tgid >= 0 && tgid != pid) {
+ ALOGE("Possible pid reuse detected (pid %d, tgid %d)!", pid, tgid);
+ goto out;
+ }
+
taskname = proc_get_name(pid);
if (!taskname) {
goto out;
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index b1616d3..a6ea2ab 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -48,8 +49,8 @@
namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
# We can't have entire /system/${LIB} as permitted paths because doing so
# makes it possible to load libs in /system/${LIB}/vndk* directories by
@@ -61,12 +62,15 @@
namespace.default.permitted.paths = /system/${LIB}/drm
namespace.default.permitted.paths += /system/${LIB}/extractors
namespace.default.permitted.paths += /system/${LIB}/hw
+namespace.default.permitted.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.permitted.paths += /%PRODUCT%/${LIB}
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
# These are where odex files are located. libart has to be able to dlopen the files
namespace.default.permitted.paths += /system/framework
namespace.default.permitted.paths += /system/app
namespace.default.permitted.paths += /system/priv-app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
@@ -80,9 +84,6 @@
namespace.default.permitted.paths += /%PRODUCT%/framework
namespace.default.permitted.paths += /%PRODUCT%/app
namespace.default.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.permitted.paths += /data
namespace.default.permitted.paths += /mnt/expand
namespace.default.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
@@ -90,10 +91,10 @@
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.permitted.paths = /data
namespace.default.asan.permitted.paths += /system/${LIB}/drm
@@ -102,6 +103,10 @@
namespace.default.asan.permitted.paths += /system/framework
namespace.default.asan.permitted.paths += /system/app
namespace.default.asan.permitted.paths += /system/priv-app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/framework
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/app
+namespace.default.asan.permitted.paths += /%SYSTEM_EXT%/priv-app
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
@@ -116,10 +121,6 @@
namespace.default.asan.permitted.paths += /%PRODUCT%/framework
namespace.default.asan.permitted.paths += /%PRODUCT%/app
namespace.default.asan.permitted.paths += /%PRODUCT%/priv-app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
-namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
namespace.default.asan.permitted.paths += /mnt/expand
namespace.default.asan.permitted.paths += /apex/com.android.runtime/${LIB}/bionic
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
@@ -510,15 +511,15 @@
namespace.system.isolated = false
namespace.system.search.paths = /system/${LIB}
+namespace.system.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.system.search.paths += /%PRODUCT%/${LIB}
-namespace.system.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
-namespace.system.asan.search.paths += /data/asan/product/${LIB}
+namespace.system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.system.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.system.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.system.links = runtime
namespace.system.link.runtime.shared_libs = libdexfile_external.so
@@ -553,15 +554,15 @@
# The search paths here should be kept the same as that of the 'system'
# namespace.
namespace.vndk_in_system.search.paths = /system/${LIB}
+namespace.vndk_in_system.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.vndk_in_system.search.paths += /%PRODUCT%/${LIB}
-namespace.vndk_in_system.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.vndk_in_system.asan.search.paths = /data/asan/system/${LIB}
namespace.vndk_in_system.asan.search.paths += /system/${LIB}
-namespace.vndk_in_system.asan.search.paths += /data/asan/product/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.vndk_in_system.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.vndk_in_system.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.vndk_in_system.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.vndk_in_system.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.vndk_in_system.whitelisted = %VNDK_USING_CORE_VARIANT_LIBRARIES%
@@ -705,7 +706,7 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.link.runtime.shared_libs = %SANITIZER_RUNTIME_LIBRARIES%
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 9212408..69535a9 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -7,6 +7,7 @@
# absolute path of an executable is selected.
dir.system = /system/bin/
dir.system = /system/xbin/
+dir.system = /%SYSTEM_EXT%/bin/
dir.system = /%PRODUCT%/bin/
dir.vendor = /odm/bin/
@@ -48,21 +49,21 @@
namespace.default.visible = true
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.search.paths += /%PRODUCT%/${LIB}
namespace.default.search.paths += /odm/${LIB}
namespace.default.search.paths += /vendor/${LIB}
-namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths = /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
+namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /data/asan/odm/${LIB}
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/%PRODUCT_SERVICES%/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
# Keep in sync with the "platform" namespace in art/build/apex/ld.config.txt.
# If a shared library or an executable requests a shared library that
@@ -336,8 +337,9 @@
# Access to system libraries is allowed
namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
+# Put /system/lib/vndk at the last search order in vndk_lite for GSI
namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
@@ -356,10 +358,10 @@
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
-namespace.default.asan.search.paths += /data/asan/product/${LIB}
+namespace.default.asan.search.paths += /data/asan/%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /%SYSTEM_EXT%/${LIB}
+namespace.default.asan.search.paths += /data/asan/%PRODUCT%/${LIB}
namespace.default.asan.search.paths += /%PRODUCT%/${LIB}
-namespace.default.asan.search.paths += /data/asan/product_services/${LIB}
-namespace.default.asan.search.paths += /%PRODUCT_SERVICES%/${LIB}
namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
@@ -514,5 +516,5 @@
[postinstall]
namespace.default.isolated = false
namespace.default.search.paths = /system/${LIB}
+namespace.default.search.paths += /%SYSTEM_EXT%/${LIB}
namespace.default.search.paths += /%PRODUCT%/${LIB}
-namespace.default.search.paths += /%PRODUCT_SERVICES%/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3acf301..96ffa69 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -413,6 +413,10 @@
# HALs required before storage encryption can get unlocked (FBE/FDE)
class_start early_hal
+ # Check and mark a successful boot, before mounting userdata with mount_all.
+ # No-op for non-A/B device.
+ exec_start update_verifier_nonencrypted
+
on post-fs-data
mark_post_data
@@ -628,22 +632,16 @@
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
start netd
start zygote
start zygote_secondary
@@ -770,8 +768,6 @@
trigger zygote-start
on property:vold.decrypt=trigger_restart_min_framework
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index f62c3df..c949a4f 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -147,12 +147,7 @@
$(hide) sed -i.bak -e "s?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g" $@
$(hide) sed -i.bak -e "s?%VNDK_VER%?$(PRIVATE_VNDK_VERSION_SUFFIX)?g" $@
$(hide) sed -i.bak -e "s?%PRODUCT%?$(TARGET_COPY_OUT_PRODUCT)?g" $@
-ifeq ($(TARGET_COPY_OUT_PRODUCT),$(TARGET_COPY_OUT_PRODUCT_SERVICES))
- # Remove lines containing %PRODUCT_SERVICES% (identical to the %PRODUCT% ones)
- $(hide) sed -i.bak -e "\?%PRODUCT_SERVICES%?d" $@
-else
- $(hide) sed -i.bak -e "s?%PRODUCT_SERVICES%?$(TARGET_COPY_OUT_PRODUCT_SERVICES)?g" $@
-endif
+ $(hide) sed -i.bak -e "s?%SYSTEM_EXT%?$(TARGET_COPY_OUT_SYSTEM_EXT)?g" $@
$(hide) sed -i.bak -e "s?^$(PRIVATE_VNDK_VERSION_TAG)??g" $@
$(hide) sed -i.bak "/^\#VNDK[0-9]\{2\}\#.*$$/d" $@
$(hide) rm -f $@.bak
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 1926a4f..d391cc1 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -214,7 +214,7 @@
## Android R
-BSD: grep fsck\_msdos newfs\_msdos
+BSD: fsck\_msdos newfs\_msdos
bzip2: bzcat bzip2 bunzip2