Merge "Implement sysprop_library API stability check"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a3bd44f..cc1978d 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -4,6 +4,9 @@
"name": "adbd_test"
},
{
+ "name": "CtsInitTestCases"
+ },
+ {
"name": "debuggerd_test"
},
{
@@ -13,9 +16,6 @@
"name": "fs_mgr_vendor_overlay_test"
},
{
- "name": "init_tests"
- },
- {
"name": "libbase_test"
},
{
diff --git a/adb/Android.bp b/adb/Android.bp
index 06cfcbf..57872b0 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -142,6 +142,10 @@
"sysdeps/posix/network.cpp",
]
+libadb_linux_srcs = [
+ "fdevent/fdevent_epoll.cpp",
+]
+
libadb_test_srcs = [
"adb_io_test.cpp",
"adb_listeners_test.cpp",
@@ -170,12 +174,11 @@
target: {
linux: {
- srcs: ["client/usb_linux.cpp"],
+ srcs: ["client/usb_linux.cpp"] + libadb_linux_srcs,
},
darwin: {
srcs: ["client/usb_osx.cpp"],
},
-
not_windows: {
srcs: libadb_posix_srcs,
},
@@ -342,7 +345,7 @@
// libminadbd wants both, as it's used to build native tests.
compile_multilib: "both",
- srcs: libadb_srcs + libadb_posix_srcs + [
+ srcs: libadb_srcs + libadb_linux_srcs + libadb_posix_srcs + [
"daemon/auth.cpp",
"daemon/jdwp_service.cpp",
],
diff --git a/adb/fdevent/fdevent.cpp b/adb/fdevent/fdevent.cpp
index 28b8f37..562f587 100644
--- a/adb/fdevent/fdevent.cpp
+++ b/adb/fdevent/fdevent.cpp
@@ -26,16 +26,24 @@
#include "adb_utils.h"
#include "fdevent.h"
+#include "fdevent_epoll.h"
#include "fdevent_poll.h"
+using namespace std::chrono_literals;
+using std::chrono::duration_cast;
+
+void invoke_fde(struct fdevent* fde, unsigned events) {
+ if (auto f = std::get_if<fd_func>(&fde->func)) {
+ (*f)(fde->fd.get(), events, fde->arg);
+ } else if (auto f = std::get_if<fd_func2>(&fde->func)) {
+ (*f)(fde, events, fde->arg);
+ } else {
+ __builtin_unreachable();
+ }
+}
+
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";
}
@@ -53,9 +61,11 @@
CheckMainThread();
CHECK_GE(fd.get(), 0);
+ int fd_num = fd.get();
+
fdevent* fde = new fdevent();
fde->id = fdevent_id_++;
- fde->state = FDE_ACTIVE;
+ fde->state = 0;
fde->fd = std::move(fd);
fde->func = func;
fde->arg = arg;
@@ -66,6 +76,10 @@
LOG(ERROR) << "failed to set non-blocking mode for fd " << fde->fd.get();
}
+ auto [it, inserted] = this->installed_fdevents_.emplace(fd_num, fde);
+ CHECK(inserted);
+ UNUSED(it);
+
this->Register(fde);
return fde;
}
@@ -78,18 +92,22 @@
this->Unregister(fde);
+ auto erased = this->installed_fdevents_.erase(fde->fd.get());
+ CHECK_EQ(1UL, erased);
+
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);
+ CHECK(!(events & FDE_TIMEOUT));
+ Set(fde, fde->state | events);
}
void fdevent_context::Del(fdevent* fde, unsigned events) {
CHECK(!(events & FDE_TIMEOUT));
- Set(fde, (fde->state & FDE_EVENTMASK) & ~events);
+ Set(fde, fde->state & ~events);
}
void fdevent_context::SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout) {
@@ -98,6 +116,56 @@
fde->last_active = std::chrono::steady_clock::now();
}
+std::optional<std::chrono::milliseconds> fdevent_context::CalculatePollDuration() {
+ std::optional<std::chrono::milliseconds> result = std::nullopt;
+ auto now = std::chrono::steady_clock::now();
+ CheckMainThread();
+
+ for (const auto& [fd, fde] : this->installed_fdevents_) {
+ UNUSED(fd);
+ auto timeout_opt = fde->timeout;
+ if (timeout_opt) {
+ auto deadline = fde->last_active + *timeout_opt;
+ auto time_left = duration_cast<std::chrono::milliseconds>(deadline - now);
+ if (time_left < 0ms) {
+ time_left = 0ms;
+ }
+
+ if (!result) {
+ result = time_left;
+ } else {
+ result = std::min(*result, time_left);
+ }
+ }
+ }
+
+ return result;
+}
+
+void fdevent_context::HandleEvents(const std::vector<fdevent_event>& events) {
+ for (const auto& event : events) {
+ invoke_fde(event.fde, event.events);
+ }
+ FlushRunQueue();
+}
+
+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 = std::move(this->run_queue_.front());
+ this->run_queue_.pop_front();
+ }
+ fn();
+ }
+}
+
void fdevent_context::CheckMainThread() {
if (main_thread_id_) {
CHECK_EQ(*main_thread_id_, android::base::GetThreadId());
@@ -118,25 +186,16 @@
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 std::unique_ptr<fdevent_context> fdevent_create_context() {
+#if defined(__linux__)
+ return std::make_unique<fdevent_context_epoll>();
+#else
+ return std::make_unique<fdevent_context_poll>();
+#endif
}
static auto& g_ambient_fdevent_context =
- *new std::unique_ptr<fdevent_context>(new fdevent_context_poll());
+ *new std::unique_ptr<fdevent_context>(fdevent_create_context());
static fdevent_context* fdevent_get_ambient() {
return g_ambient_fdevent_context.get();
@@ -197,5 +256,5 @@
}
void fdevent_reset() {
- g_ambient_fdevent_context.reset(new fdevent_context_poll());
+ g_ambient_fdevent_context = fdevent_create_context();
}
diff --git a/adb/fdevent/fdevent.h b/adb/fdevent/fdevent.h
index 2424252..86814d7 100644
--- a/adb/fdevent/fdevent.h
+++ b/adb/fdevent/fdevent.h
@@ -26,6 +26,7 @@
#include <functional>
#include <mutex>
#include <optional>
+#include <unordered_map>
#include <variant>
#include <android-base/thread_annotations.h>
@@ -38,19 +39,19 @@
#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
+struct fdevent;
typedef void (*fd_func)(int fd, unsigned events, void *userdata);
typedef void (*fd_func2)(struct fdevent* fde, unsigned events, void* userdata);
-struct fdevent;
+void invoke_fde(struct fdevent* fde, unsigned events);
std::string dump_fde(const fdevent* fde);
+struct fdevent_event {
+ fdevent* fde;
+ unsigned events;
+};
+
struct fdevent_context {
public:
virtual ~fdevent_context() = default;
@@ -59,14 +60,13 @@
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.
+ // Note that this calls Set, which is a virtual method, so destructors that call this must be
+ // final.
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;
+ virtual void Register(fdevent*) {}
+ virtual void Unregister(fdevent*) {}
public:
// Change which events should cause notifications.
@@ -80,6 +80,15 @@
// trigger repeatedly every |timeout| ms.
void SetTimeout(fdevent* fde, std::optional<std::chrono::milliseconds> timeout);
+ protected:
+ std::optional<std::chrono::milliseconds> CalculatePollDuration();
+ void HandleEvents(const std::vector<fdevent_event>& events);
+
+ private:
+ // Run all pending functions enqueued via Run().
+ void FlushRunQueue() EXCLUDES(run_queue_mutex_);
+
+ public:
// 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.
@@ -100,12 +109,12 @@
// 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;
+ protected:
+ std::unordered_map<int, fdevent*> installed_fdevents_;
+
private:
uint64_t fdevent_id_ = 0;
std::mutex run_queue_mutex_;
@@ -119,7 +128,6 @@
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;
diff --git a/adb/fdevent/fdevent_epoll.cpp b/adb/fdevent/fdevent_epoll.cpp
new file mode 100644
index 0000000..e3d1674
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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 "fdevent_epoll.h"
+
+#if defined(__linux__)
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+#include <android-base/threads.h>
+
+#include "adb_unique_fd.h"
+#include "fdevent.h"
+
+static void fdevent_interrupt(int fd, unsigned, void*) {
+ uint64_t buf;
+ 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_epoll::fdevent_context_epoll() {
+ epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
+
+ unique_fd interrupt_fd(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
+ if (interrupt_fd == -1) {
+ PLOG(FATAL) << "failed to create fdevent interrupt eventfd";
+ }
+
+ unique_fd interrupt_fd_dup(fcntl(interrupt_fd.get(), F_DUPFD_CLOEXEC, 3));
+ if (interrupt_fd_dup == -1) {
+ PLOG(FATAL) << "failed to dup fdevent interrupt eventfd";
+ }
+
+ this->interrupt_fd_ = std::move(interrupt_fd_dup);
+ fdevent* fde = this->Create(std::move(interrupt_fd), fdevent_interrupt, nullptr);
+ CHECK(fde != nullptr);
+ this->Add(fde, FDE_READ);
+}
+
+fdevent_context_epoll::~fdevent_context_epoll() {
+ // Destroy calls virtual methods, but this class is final, so that's okay.
+ this->Destroy(this->interrupt_fde_);
+}
+
+static epoll_event calculate_epoll_event(fdevent* fde) {
+ epoll_event result;
+ result.events = 0;
+ if (fde->state & FDE_READ) {
+ result.events |= EPOLLIN;
+ }
+ if (fde->state & FDE_WRITE) {
+ result.events |= EPOLLOUT;
+ }
+ if (fde->state & FDE_ERROR) {
+ result.events |= EPOLLERR;
+ }
+ result.events |= EPOLLRDHUP;
+ result.data.ptr = fde;
+ return result;
+}
+
+void fdevent_context_epoll::Register(fdevent* fde) {
+ epoll_event ev = calculate_epoll_event(fde);
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fde->fd.get(), &ev) != 0) {
+ PLOG(FATAL) << "failed to register fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Unregister(fdevent* fde) {
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fde->fd.get(), nullptr) != 0) {
+ PLOG(FATAL) << "failed to unregister fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Set(fdevent* fde, unsigned events) {
+ unsigned previous_state = fde->state;
+ fde->state = events;
+
+ // If the state is the same, or only differed by FDE_TIMEOUT, we don't need to modify epoll.
+ if ((previous_state & ~FDE_TIMEOUT) == (events & ~FDE_TIMEOUT)) {
+ return;
+ }
+
+ epoll_event ev = calculate_epoll_event(fde);
+ if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, fde->fd.get(), &ev) != 0) {
+ PLOG(FATAL) << "failed to modify fd " << fde->fd.get() << " with epoll";
+ }
+}
+
+void fdevent_context_epoll::Loop() {
+ main_thread_id_ = android::base::GetThreadId();
+
+ std::vector<fdevent_event> fde_events;
+ std::vector<epoll_event> epoll_events;
+ epoll_events.resize(this->installed_fdevents_.size());
+
+ while (true) {
+ if (terminate_loop_) {
+ break;
+ }
+
+ int rc = -1;
+ while (rc == -1) {
+ std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
+ }
+
+ rc = epoll_wait(epoll_fd_.get(), epoll_events.data(), epoll_events.size(), timeout_ms);
+ if (rc == -1 && errno != EINTR) {
+ PLOG(FATAL) << "epoll_wait failed";
+ }
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+ std::unordered_map<fdevent*, unsigned> event_map;
+ for (int i = 0; i < rc; ++i) {
+ fdevent* fde = static_cast<fdevent*>(epoll_events[i].data.ptr);
+
+ unsigned events = 0;
+ if (epoll_events[i].events & EPOLLIN) {
+ CHECK(fde->state & FDE_READ);
+ events |= FDE_READ;
+ }
+ if (epoll_events[i].events & EPOLLOUT) {
+ CHECK(fde->state & FDE_WRITE);
+ events |= FDE_WRITE;
+ }
+ if (epoll_events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) {
+ // We fake a read, as the rest of the code assumes that errors will
+ // be detected at that point.
+ events |= FDE_READ | FDE_ERROR;
+ }
+
+ event_map[fde] = events;
+ }
+
+ for (const auto& [fd, fde] : installed_fdevents_) {
+ unsigned events = 0;
+ if (auto it = event_map.find(fde); it != event_map.end()) {
+ events = it->second;
+ }
+
+ if (events == 0) {
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ LOG(DEBUG) << dump_fde(fde) << " got events " << std::hex << std::showbase
+ << events;
+ fde_events.push_back({fde, events});
+ fde->last_active = post_poll;
+ }
+ }
+ this->HandleEvents(std::move(fde_events));
+ fde_events.clear();
+ }
+
+ main_thread_id_.reset();
+}
+
+size_t fdevent_context_epoll::InstalledCount() {
+ // We always have an installed fde for interrupt.
+ return this->installed_fdevents_.size() - 1;
+}
+
+void fdevent_context_epoll::Interrupt() {
+ uint64_t i = 1;
+ ssize_t rc = TEMP_FAILURE_RETRY(adb_write(this->interrupt_fd_, &i, sizeof(i)));
+ if (rc != sizeof(i)) {
+ PLOG(FATAL) << "failed to write to fdevent interrupt eventfd";
+ }
+}
+
+#endif // defined(__linux__)
diff --git a/adb/fdevent/fdevent_epoll.h b/adb/fdevent/fdevent_epoll.h
new file mode 100644
index 0000000..684fa32
--- /dev/null
+++ b/adb/fdevent/fdevent_epoll.h
@@ -0,0 +1,61 @@
+#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.
+ */
+
+#if defined(__linux__)
+
+#include "sysdeps.h"
+
+#include <sys/epoll.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 fdevent_context_epoll final : public fdevent_context {
+ fdevent_context_epoll();
+ virtual ~fdevent_context_epoll();
+
+ virtual void Register(fdevent* fde) final;
+ virtual void Unregister(fdevent* fde) final;
+
+ virtual void Set(fdevent* fde, unsigned events) final;
+
+ virtual void Loop() final;
+ 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, fdevent*> epoll_node_map_;
+ std::list<fdevent*> pending_list_;
+
+ unique_fd epoll_fd_;
+ unique_fd interrupt_fd_;
+ fdevent* interrupt_fde_ = nullptr;
+};
+
+#endif // defined(__linux__)
diff --git a/adb/fdevent/fdevent_poll.cpp b/adb/fdevent/fdevent_poll.cpp
index 75ea081..cc4a7a1 100644
--- a/adb/fdevent/fdevent_poll.cpp
+++ b/adb/fdevent/fdevent_poll.cpp
@@ -75,60 +75,14 @@
}
fdevent_context_poll::~fdevent_context_poll() {
+ // Destroy calls virtual methods, but this class is final, so that's okay.
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;
-
+ fde->state = 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) {
@@ -146,204 +100,94 @@
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");
+ std::vector<adb_pollfd> pollfds;
+ for (const auto& [fd, fde] : this->installed_fdevents_) {
+ adb_pollfd pfd;
+ pfd.fd = fd;
+ pfd.events = 0;
+ if (fde->state & FDE_READ) {
+ pfd.events |= POLLIN;
+ }
+ if (fde->state & FDE_WRITE) {
+ pfd.events |= POLLOUT;
+ }
+ if (fde->state & FDE_ERROR) {
+ pfd.events |= POLLERR;
+ }
+#if defined(__linux__)
+ pfd.events |= POLLRDHUP;
+#endif
+ pfd.revents = 0;
+ pollfds.push_back(pfd);
+ }
+ CHECK_GT(pollfds.size(), 0u);
+ D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
- 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);
+ std::optional<std::chrono::milliseconds> timeout = CalculatePollDuration();
+ int timeout_ms;
+ if (!timeout) {
+ timeout_ms = -1;
+ } else {
+ timeout_ms = timeout->count();
}
- this->FlushRunQueue();
+ int ret = adb_poll(pollfds.data(), pollfds.size(), timeout_ms);
+ if (ret == -1) {
+ PLOG(ERROR) << "poll(), ret = " << ret;
+ return;
+ }
+
+ auto post_poll = std::chrono::steady_clock::now();
+ std::vector<fdevent_event> poll_events;
+
+ for (const auto& pollfd : pollfds) {
+ 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 = this->installed_fdevents_.find(pollfd.fd);
+ CHECK(it != this->installed_fdevents_.end());
+ fdevent* fde = it->second;
+
+ if (events == 0) {
+ if (fde->timeout) {
+ auto deadline = fde->last_active + *fde->timeout;
+ if (deadline < post_poll) {
+ events |= FDE_TIMEOUT;
+ }
+ }
+ }
+
+ if (events != 0) {
+ D("%s got events %x", dump_fde(fde).c_str(), events);
+ poll_events.push_back({fde, events});
+ fde->last_active = post_poll;
+ }
+ }
+ this->HandleEvents(std::move(poll_events));
}
main_thread_id_.reset();
@@ -351,7 +195,7 @@
size_t fdevent_context_poll::InstalledCount() {
// We always have an installed fde for interrupt.
- return poll_node_map_.size() - 1;
+ return this->installed_fdevents_.size() - 1;
}
void fdevent_context_poll::Interrupt() {
diff --git a/adb/fdevent/fdevent_poll.h b/adb/fdevent/fdevent_poll.h
index db08301..98abab2 100644
--- a/adb/fdevent/fdevent_poll.h
+++ b/adb/fdevent/fdevent_poll.h
@@ -44,13 +44,10 @@
}
};
-struct fdevent_context_poll : public fdevent_context {
+struct fdevent_context_poll final : 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;
@@ -61,11 +58,6 @@
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/fdevent_test.cpp b/adb/fdevent/fdevent_test.cpp
index 682f061..e06b3b3 100644
--- a/adb/fdevent/fdevent_test.cpp
+++ b/adb/fdevent/fdevent_test.cpp
@@ -118,8 +118,8 @@
TEST_F(FdeventTest, smoke) {
for (bool use_new_callback : {true, false}) {
fdevent_reset();
- const size_t PIPE_COUNT = 10;
- const size_t MESSAGE_LOOP_COUNT = 100;
+ const size_t PIPE_COUNT = 512;
+ const size_t MESSAGE_LOOP_COUNT = 10;
const std::string MESSAGE = "fdevent_test";
int fd_pair1[2];
int fd_pair2[2];
@@ -172,44 +172,6 @@
}
}
-struct InvalidFdArg {
- fdevent* fde;
- unsigned expected_events;
- size_t* happened_event_count;
-};
-
-static void InvalidFdEventCallback(int, unsigned events, void* userdata) {
- InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
- ASSERT_EQ(arg->expected_events, events);
- fdevent_destroy(arg->fde);
- if (++*(arg->happened_event_count) == 2) {
- fdevent_terminate_loop();
- }
-}
-
-static void InvalidFdThreadFunc() {
- const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
- size_t happened_event_count = 0;
- InvalidFdArg read_arg;
- read_arg.expected_events = FDE_READ | FDE_ERROR;
- read_arg.happened_event_count = &happened_event_count;
- read_arg.fde = fdevent_create(INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
- fdevent_add(read_arg.fde, FDE_READ);
-
- const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
- InvalidFdArg write_arg;
- write_arg.expected_events = FDE_READ | FDE_ERROR;
- write_arg.happened_event_count = &happened_event_count;
- write_arg.fde = fdevent_create(INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
- fdevent_add(write_arg.fde, FDE_WRITE);
- fdevent_loop();
-}
-
-TEST_F(FdeventTest, invalid_fd) {
- std::thread thread(InvalidFdThreadFunc);
- thread.join();
-}
-
TEST_F(FdeventTest, run_on_main_thread) {
std::vector<int> vec;
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index 53ef01c..b4a1e71 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -2,10 +2,10 @@
user tombstoned
group system
- # Don't start tombstoned until after the real /data is mounted.
- class late_start
-
socket tombstoned_crash seqpacket 0666 system system
socket tombstoned_intercept seqpacket 0666 system system
socket tombstoned_java_trace seqpacket 0666 system system
writepid /dev/cpuset/system-background/tasks
+
+on post-fs-data
+ start tombstoned
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index eaa515a..1b85b47 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -52,29 +52,36 @@
using DmTargetZero = android::dm::DmTargetZero;
using DmTargetLinear = android::dm::DmTargetLinear;
-static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata,
+static bool GetPhysicalPartitionDevicePath(const IPartitionOpener& opener,
+ const LpMetadata& metadata,
const LpMetadataBlockDevice& block_device,
- const std::string& super_device,
- std::string* result) {
- // Note: device-mapper will not accept symlinks, so we must use realpath
- // here.
- std::string name = GetBlockDevicePartitionName(block_device);
- std::string path = "/dev/block/by-name/" + name;
+ const std::string& super_device, std::string* result) {
// If the super device is the source of this block device's metadata,
// make sure we use the correct super device (and not just "super",
// which might not exist.)
+ std::string name = GetBlockDevicePartitionName(block_device);
+ std::string dev_string = opener.GetDeviceString(name);
if (GetMetadataSuperBlockDevice(metadata) == &block_device) {
- path = super_device;
+ dev_string = opener.GetDeviceString(super_device);
}
- if (!android::base::Realpath(path, result)) {
- PERROR << "realpath: " << path;
- return false;
+
+ // Note: device-mapper will not accept symlinks, so we must use realpath
+ // here. If the device string is a major:minor sequence, we don't need to
+ // to call Realpath (it would not work anyway).
+ if (android::base::StartsWith(dev_string, "/")) {
+ if (!android::base::Realpath(dev_string, result)) {
+ PERROR << "realpath: " << dev_string;
+ return false;
+ }
+ } else {
+ *result = dev_string;
}
return true;
}
-static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition,
- const std::string& super_device, DmTable* table) {
+static bool CreateDmTable(const IPartitionOpener& opener, const LpMetadata& metadata,
+ const LpMetadataPartition& partition, const std::string& super_device,
+ DmTable* table) {
uint64_t sector = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
const auto& extent = metadata.extents[partition.first_extent_index + i];
@@ -85,12 +92,13 @@
break;
case LP_TARGET_TYPE_LINEAR: {
const auto& block_device = metadata.block_devices[extent.target_source];
- std::string path;
- if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) {
+ std::string dev_string;
+ if (!GetPhysicalPartitionDevicePath(opener, metadata, block_device, super_device,
+ &dev_string)) {
LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
return false;
}
- target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, path,
+ target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
extent.target_data);
break;
}
@@ -179,8 +187,12 @@
}
}
+ PartitionOpener default_opener;
+ const IPartitionOpener* opener =
+ params.partition_opener ? params.partition_opener : &default_opener;
+
DmTable table;
- if (!CreateDmTable(*metadata, *partition, params.block_device, &table)) {
+ if (!CreateDmTable(*opener, *metadata, *partition, params.block_device, &table)) {
return false;
}
if (params.force_writable) {
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 6eb541c..8e2fdbb 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -73,6 +73,10 @@
// If this is non-empty, it will override the device mapper name (by
// default the partition name will be used).
std::string device_name;
+
+ // If non-null, this will use the specified IPartitionOpener rather than
+ // the default one.
+ const IPartitionOpener* partition_opener = nullptr;
};
bool CreateLogicalPartition(const CreateLogicalPartitionParams& params, std::string* path);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index a4e0d76..e7a3ff2 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -79,6 +79,13 @@
return true;
}
+bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
+ if (GetState(name) == DmDeviceState::INVALID) {
+ return true;
+ }
+ return DeleteDevice(name);
+}
+
bool DeviceMapper::DeleteDevice(const std::string& name) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index cf306f3..e25ce7f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -89,6 +89,7 @@
// Removes a device mapper device with the given name.
// Returns 'true' on success, false otherwise.
bool DeleteDevice(const std::string& name);
+ bool DeleteDeviceIfExists(const std::string& name);
// Fetches and returns the complete state of the underlying device mapper
// device with given name.
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 5b377ae..f0142bb 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -55,13 +55,14 @@
export_include_dirs: ["include"],
}
-cc_test {
- name: "liblp_test_static",
+cc_defaults {
+ name: "liblp_test_defaults",
defaults: ["fs_mgr_defaults"],
cppflags: [
"-Wno-unused-parameter",
],
static_libs: [
+ "libcutils",
"libgmock",
"libfs_mgr",
"liblp",
@@ -69,15 +70,25 @@
stl: "libc++_static",
srcs: [
"builder_test.cpp",
+ "device_test.cpp",
"io_test.cpp",
"test_partition_opener.cpp",
"utility_test.cpp",
],
- target: {
- android: {
- static_libs: [
- "libcutils",
- ],
- },
- },
}
+
+cc_test {
+ name: "liblp_test",
+ defaults: ["liblp_test_defaults"],
+ test_config: "liblp_test.xml",
+ test_suites: [
+ "device-tests",
+ "vts-core",
+ ],
+}
+
+cc_test {
+ name: "vts_kernel_liblp_test",
+ defaults: ["liblp_test_defaults"],
+}
+
diff --git a/fs_mgr/liblp/AndroidTest.xml b/fs_mgr/liblp/AndroidTest.xml
index fe1002c..2eb0ad1 100644
--- a/fs_mgr/liblp/AndroidTest.xml
+++ b/fs_mgr/liblp/AndroidTest.xml
@@ -21,8 +21,8 @@
</target_preparer>
<test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
<option name="test-module-name" value="VtsKernelLiblpTest"/>
- <option name="binary-test-source" value="_32bit::DATA/nativetest/liblp_test_static/liblp_test_static" />
- <option name="binary-test-source" value="_64bit::DATA/nativetest64/liblp_test_static/liblp_test_static" />
+ <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_kernel_liblp_test/vts_kernel_liblp_test" />
+ <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_kernel_liblp_test/vts_kernel_liblp_test" />
<option name="binary-test-type" value="gtest"/>
<option name="test-timeout" value="1m"/>
<option name="precondition-first-api-level" value="29" />
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
new file mode 100644
index 0000000..04bcbda
--- /dev/null
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "liblp_test"
+ }
+ ]
+}
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index b3c13e6..dde6d07 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,34 +14,25 @@
* limitations under the License.
*/
-#include <fs_mgr.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
-#include "mock_property_fetcher.h"
+#include "liblp_test.h"
#include "utility.h"
using namespace std;
using namespace android::fs_mgr;
-using ::android::fs_mgr::MockPropertyFetcher;
+using namespace android::fs_mgr::testing;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::ElementsAre;
using ::testing::NiceMock;
using ::testing::Return;
-static void ResetPropertyFetcher() {
- IPropertyFetcher::OverrideForTesting(std::make_unique<NiceMock<MockPropertyFetcher>>());
-}
-
-MockPropertyFetcher* GetMockedPropertyFetcher() {
- return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
-}
-
class Environment : public ::testing::Environment {
public:
- void SetUp() override { ResetPropertyFetcher(); }
+ void SetUp() override { ResetMockPropertyFetcher(); }
};
int main(int argc, char** argv) {
@@ -49,11 +40,7 @@
return RUN_ALL_TESTS();
}
-class BuilderTest : public ::testing::Test {
- public:
- void SetUp() override { ResetPropertyFetcher(); }
- void TearDown() override { ResetPropertyFetcher(); }
-};
+class BuilderTest : public LiblpTest {};
TEST_F(BuilderTest, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
@@ -460,23 +447,6 @@
EXPECT_EQ(builder, nullptr);
}
-TEST_F(BuilderTest, block_device_info) {
- PartitionOpener opener;
-
- BlockDeviceInfo device_info;
- ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
-
- // Sanity check that the device doesn't give us some weird inefficient
- // alignment.
- ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
- ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
- ASSERT_LE(device_info.alignment_offset, INT_MAX);
- ASSERT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
-
- // Having an alignment offset > alignment doesn't really make sense.
- ASSERT_LT(device_info.alignment_offset, device_info.alignment);
-}
-
TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
diff --git a/fs_mgr/liblp/device_test.cpp b/fs_mgr/liblp/device_test.cpp
new file mode 100644
index 0000000..382d53d
--- /dev/null
+++ b/fs_mgr/liblp/device_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <android-base/properties.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
+#include <gtest/gtest.h>
+#include <liblp/liblp.h>
+#include <liblp/metadata_format.h>
+#include <liblp/partition_opener.h>
+#include <liblp/property_fetcher.h>
+
+#include "liblp_test.h"
+
+using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
+using ::testing::Return;
+
+// Compliance test on the actual device with dynamic partitions.
+class DeviceTest : public LiblpTest {
+ public:
+ void SetUp() override {
+ // Read real properties.
+ IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
+ if (!IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions",
+ false)) {
+ GTEST_SKIP() << "Device doesn't have dynamic partitions enabled, skipping";
+ }
+ }
+};
+
+TEST_F(DeviceTest, BlockDeviceInfo) {
+ PartitionOpener opener;
+ BlockDeviceInfo device_info;
+ ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));
+
+ // Sanity check that the device doesn't give us some weird inefficient
+ // alignment.
+ EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
+ EXPECT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
+ EXPECT_LE(device_info.alignment_offset, INT_MAX);
+ EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);
+
+ // Having an alignment offset > alignment doesn't really make sense.
+ EXPECT_LT(device_info.alignment_offset, device_info.alignment);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {
+ auto slot_suffix = fs_mgr_get_slot_suffix();
+ auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
+ auto super_name = fs_mgr_get_super_partition_name(slot_number);
+ auto metadata = ReadMetadata(super_name, slot_number);
+ EXPECT_NE(metadata, nullptr);
+}
+
+TEST_F(DeviceTest, ReadSuperPartitionOtherSlot) {
+ auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
+ if (other_slot_suffix.empty()) {
+ GTEST_SKIP() << "No other slot, skipping";
+ }
+ if (IPropertyFetcher::GetInstance()->GetBoolProperty("ro.boot.dynamic_partitions_retrofit",
+ false)) {
+ GTEST_SKIP() << "Device with retrofit dynamic partition may not have metadata at other "
+ << "slot, skipping";
+ }
+
+ auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
+ auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
+ auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
+ EXPECT_NE(other_metadata, nullptr);
+}
diff --git a/fs_mgr/liblp/mock_property_fetcher.h b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
similarity index 79%
rename from fs_mgr/liblp/mock_property_fetcher.h
rename to fs_mgr/liblp/include/liblp/mock_property_fetcher.h
index 0c28710..f15611b 100644
--- a/fs_mgr/liblp/mock_property_fetcher.h
+++ b/fs_mgr/liblp/include/liblp/mock_property_fetcher.h
@@ -22,6 +22,7 @@
namespace android {
namespace fs_mgr {
+namespace testing {
class MockPropertyFetcher : public IPropertyFetcher {
public:
@@ -41,7 +42,15 @@
}
};
+static inline void ResetMockPropertyFetcher() {
+ IPropertyFetcher::OverrideForTesting(
+ std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());
+}
+
+static inline MockPropertyFetcher* GetMockedPropertyFetcher() {
+ return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());
+}
+
+} // namespace testing
} // namespace fs_mgr
} // namespace android
-
-android::fs_mgr::MockPropertyFetcher* GetMockedPropertyFetcher();
diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h
index e506bd5..7c9100b 100644
--- a/fs_mgr/liblp/include/liblp/partition_opener.h
+++ b/fs_mgr/liblp/include/liblp/partition_opener.h
@@ -62,6 +62,11 @@
// Return block device information about the given named physical partition.
// The name can be an absolute path if the full path is already known.
virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;
+
+ // Return a path that can be used to pass the block device to device-mapper.
+ // This must either result in an absolute path, or a major:minor device
+ // sequence.
+ virtual std::string GetDeviceString(const std::string& partition_name) const = 0;
};
// Helper class to implement IPartitionOpener. If |partition_name| is not an
@@ -71,6 +76,7 @@
virtual android::base::unique_fd Open(const std::string& partition_name,
int flags) const override;
virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;
+ virtual std::string GetDeviceString(const std::string& partition_name) const override;
};
} // namespace fs_mgr
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 3dace25..df89e9c 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -21,14 +21,12 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fstab/fstab.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <liblp/builder.h>
#include "images.h"
-#include "mock_property_fetcher.h"
+#include "liblp_test.h"
#include "reader.h"
#include "test_partition_opener.h"
#include "utility.h"
@@ -36,6 +34,7 @@
using namespace std;
using namespace android::fs_mgr;
+using namespace android::fs_mgr::testing;
using ::testing::_;
using ::testing::Return;
using unique_fd = android::base::unique_fd;
@@ -124,7 +123,7 @@
}
// Test that our CreateFakeDisk() function works.
-TEST(liblp, CreateFakeDisk) {
+TEST_F(LiblpTest, CreateFakeDisk) {
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
@@ -140,7 +139,7 @@
// Flashing metadata should not work if the metadata was created for a larger
// disk than the destination disk.
-TEST(liblp, ExportDiskTooSmall) {
+TEST_F(LiblpTest, ExportDiskTooSmall) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);
ASSERT_NE(builder, nullptr);
unique_ptr<LpMetadata> exported = builder->Export();
@@ -157,7 +156,7 @@
}
// Test the basics of flashing a partition and reading it back.
-TEST(liblp, FlashAndReadback) {
+TEST_F(LiblpTest, FlashAndReadback) {
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -207,7 +206,7 @@
}
// Test that we can update metadata slots without disturbing others.
-TEST(liblp, UpdateAnyMetadataSlot) {
+TEST_F(LiblpTest, UpdateAnyMetadataSlot) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -252,7 +251,7 @@
}
}
-TEST(liblp, InvalidMetadataSlot) {
+TEST_F(LiblpTest, InvalidMetadataSlot) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -271,7 +270,7 @@
// Test that updating a metadata slot does not allow it to be computed based
// on mismatching geometry.
-TEST(liblp, NoChangingGeometry) {
+TEST_F(LiblpTest, NoChangingGeometry) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -300,7 +299,7 @@
}
// Test that changing one bit of metadata is enough to break the checksum.
-TEST(liblp, BitFlipGeometry) {
+TEST_F(LiblpTest, BitFlipGeometry) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -319,7 +318,7 @@
EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
}
-TEST(liblp, ReadBackupGeometry) {
+TEST_F(LiblpTest, ReadBackupGeometry) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -339,7 +338,7 @@
EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr);
}
-TEST(liblp, ReadBackupMetadata) {
+TEST_F(LiblpTest, ReadBackupMetadata) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -366,7 +365,7 @@
// Test that we don't attempt to write metadata if it would overflow its
// reserved space.
-TEST(liblp, TooManyPartitions) {
+TEST_F(LiblpTest, TooManyPartitions) {
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
@@ -420,7 +419,7 @@
}
// Test that we can read and write image files.
-TEST(liblp, ImageFiles) {
+TEST_F(LiblpTest, ImageFiles) {
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -436,7 +435,7 @@
}
// Test that we can read images from buffers.
-TEST(liblp, ImageFilesInMemory) {
+TEST_F(LiblpTest, ImageFilesInMemory) {
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -496,7 +495,7 @@
// Test that an interrupted flash operation on the "primary" copy of metadata
// is not fatal.
-TEST(liblp, UpdatePrimaryMetadataFailure) {
+TEST_F(LiblpTest, UpdatePrimaryMetadataFailure) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -524,7 +523,7 @@
// Test that an interrupted flash operation on the "backup" copy of metadata
// is not fatal.
-TEST(liblp, UpdateBackupMetadataFailure) {
+TEST_F(LiblpTest, UpdateBackupMetadataFailure) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -553,7 +552,7 @@
// Test that an interrupted write *in between* writing metadata will read
// the correct metadata copy. The primary is always considered newer than
// the backup.
-TEST(liblp, UpdateMetadataCleanFailure) {
+TEST_F(LiblpTest, UpdateMetadataCleanFailure) {
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
@@ -590,7 +589,7 @@
}
// Test that writing a sparse image can be read back.
-TEST(liblp, FlashSparseImage) {
+TEST_F(LiblpTest, FlashSparseImage) {
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
@@ -624,7 +623,7 @@
ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
}
-TEST(liblp, AutoSlotSuffixing) {
+TEST_F(LiblpTest, AutoSlotSuffixing) {
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
ASSERT_NE(builder, nullptr);
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
@@ -667,7 +666,7 @@
EXPECT_EQ(metadata->groups[1].flags, 0);
}
-TEST(liblp, UpdateRetrofit) {
+TEST_F(LiblpTest, UpdateRetrofit) {
ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(true));
@@ -699,7 +698,7 @@
ASSERT_TRUE(updated->extents.empty());
}
-TEST(liblp, UpdateNonRetrofit) {
+TEST_F(LiblpTest, UpdateNonRetrofit) {
ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
.WillByDefault(Return(false));
@@ -714,19 +713,3 @@
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
}
-
-TEST(liblp, ReadSuperPartition) {
- auto slot_suffix = fs_mgr_get_slot_suffix();
- auto slot_number = SlotNumberForSlotSuffix(slot_suffix);
- auto super_name = fs_mgr_get_super_partition_name(slot_number);
- auto metadata = ReadMetadata(super_name, slot_number);
- ASSERT_NE(metadata, nullptr);
-
- if (!slot_suffix.empty()) {
- auto other_slot_suffix = fs_mgr_get_other_slot_suffix();
- auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);
- auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);
- auto other_metadata = ReadMetadata(other_super_name, other_slot_number);
- ASSERT_NE(other_metadata, nullptr);
- }
-}
diff --git a/fs_mgr/liblp/liblp_test.h b/fs_mgr/liblp/liblp_test.h
new file mode 100644
index 0000000..c61aaa5
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+#include <liblp/mock_property_fetcher.h>
+
+namespace android {
+namespace fs_mgr {
+namespace testing {
+
+class LiblpTest : public ::testing::Test {
+ public:
+ void SetUp() override { ResetMockPropertyFetcher(); }
+ void TearDown() override { ResetMockPropertyFetcher(); }
+};
+
+} // namespace testing
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/liblp_test.xml b/fs_mgr/liblp/liblp_test.xml
new file mode 100644
index 0000000..d9ee12e
--- /dev/null
+++ b/fs_mgr/liblp/liblp_test.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for liblp_test">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="vts-core" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="liblp_test" />
+ </test>
+</configuration>
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index cc4a882..1d4db85 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -41,7 +41,21 @@
if (android::base::StartsWith(path, "/")) {
return path;
}
- return "/dev/block/by-name/" + path;
+
+ auto by_name = "/dev/block/by-name/" + path;
+ if (access(by_name.c_str(), F_OK) != 0) {
+ // If the by-name symlink doesn't exist, as a special case we allow
+ // certain devices to be used as partition names. This can happen if a
+ // Dynamic System Update is installed to an sdcard, which won't be in
+ // the boot device list.
+ //
+ // We whitelist because most devices in /dev/block are not valid for
+ // storing fiemaps.
+ if (android::base::StartsWith(path, "mmcblk")) {
+ return "/dev/block/" + path;
+ }
+ }
+ return by_name;
}
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
@@ -100,5 +114,9 @@
return GetBlockDeviceInfo(path, info);
}
+std::string PartitionOpener::GetDeviceString(const std::string& partition_name) const {
+ return GetPartitionAbsolutePath(partition_name);
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 128a4f9..51f5c50 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -74,6 +74,7 @@
defaults: ["libsnapshot_defaults"],
srcs: [
"snapshot_test.cpp",
+ "test_helpers.cpp",
],
shared_libs: [
"libbinder",
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 438ec2f..518c619 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,9 +26,13 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <fs_mgr_dm_linear.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include <liblp/builder.h>
+
+#include "test_helpers.h"
namespace android {
namespace snapshot {
@@ -37,25 +41,35 @@
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::fiemap::IImageManager;
+using android::fs_mgr::BlockDeviceInfo;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::MetadataBuilder;
using namespace std::chrono_literals;
using namespace std::string_literals;
-class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
- public:
- std::string GetGsidDir() const override { return "ota/test"s; }
- std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
- std::string GetSlotSuffix() const override { return slot_suffix_; }
-
- void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
-
- private:
- std::string slot_suffix_ = "_a";
-};
-
-// These are not reset between each test because it's expensive to reconnect
-// to gsid each time.
+// These are not reset between each test because it's expensive to create
+// these resources (starting+connecting to gsid, zero-filling images).
std::unique_ptr<SnapshotManager> sm;
TestDeviceInfo* test_device = nullptr;
+std::string fake_super;
+
+static constexpr uint64_t kSuperSize = 16 * 1024 * 1024;
+
+// Helper to remove stale partitions in fake super.
+void CleanupPartitions() {
+ // These are hardcoded since we might abort in the middle of a test, and
+ // can't recover the in-use list.
+ static std::vector<std::string> kPartitionNames = {
+ "base-device",
+ };
+
+ auto& dm = DeviceMapper::Instance();
+ for (const auto& partition : kPartitionNames) {
+ if (dm.GetState(partition) != DmDeviceState::INVALID) {
+ dm.DeleteDevice(partition);
+ }
+ }
+}
class SnapshotTest : public ::testing::Test {
public:
@@ -69,6 +83,7 @@
test_device->set_slot_suffix("_a");
CleanupTestArtifacts();
+ FormatFakeSuper();
ASSERT_TRUE(sm->BeginUpdate());
}
@@ -96,12 +111,7 @@
android::base::RemoveFileIfExists(status_file);
}
- // Remove all images. We hardcode the list of names since this can run
- // before the test (when cleaning up from a crash).
- std::vector<std::string> temp_partitions = {"base-device"};
- for (const auto& partition : temp_partitions) {
- DeleteBackingImage(image_manager_, partition);
- }
+ CleanupPartitions();
if (sm->GetUpdateState() != UpdateState::None) {
auto state_file = sm->GetStateFilePath();
@@ -114,11 +124,48 @@
return !!lock_;
}
- bool CreateTempDevice(const std::string& name, uint64_t size, std::string* path) {
- if (!image_manager_->CreateBackingImage(name, size, false)) {
+ void FormatFakeSuper() {
+ BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4096);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+
+ auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
+ ASSERT_NE(builder, nullptr);
+
+ auto metadata = builder->Export();
+ ASSERT_NE(metadata, nullptr);
+
+ TestPartitionOpener opener(fake_super);
+ ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));
+ }
+
+ // If |path| is non-null, the partition will be mapped after creation.
+ bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr) {
+ TestPartitionOpener opener(fake_super);
+ auto builder = MetadataBuilder::New(opener, "super", 0);
+ if (!builder) return false;
+
+ auto partition = builder->AddPartition(name, 0);
+ if (!partition) return false;
+ if (!builder->ResizePartition(partition, size)) {
return false;
}
- return image_manager_->MapImageDevice(name, 10s, path);
+
+ auto metadata = builder->Export();
+ if (!metadata) return false;
+ if (!UpdatePartitionTable(opener, "super", *metadata.get(), 0)) {
+ return false;
+ }
+
+ if (!path) return true;
+
+ CreateLogicalPartitionParams params = {
+ .block_device = fake_super,
+ .metadata = metadata.get(),
+ .partition_name = name,
+ .force_writable = true,
+ .timeout_ms = 10s,
+ };
+ return CreateLogicalPartition(params, path);
}
void DeleteSnapshotDevice(const std::string& snapshot) {
@@ -130,18 +177,10 @@
}
}
- void DeleteBackingImage(IImageManager* manager, const std::string& name) {
- if (manager->IsImageMapped(name)) {
- ASSERT_TRUE(manager->UnmapImageDevice(name));
- }
- if (manager->BackingImageExists(name)) {
- ASSERT_TRUE(manager->DeleteBackingImage(name));
- }
- }
-
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
+ std::string fake_super_;
};
TEST_F(SnapshotTest, CreateSnapshot) {
@@ -177,7 +216,7 @@
kDeviceSize));
std::string base_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
std::string snap_device;
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -193,7 +232,7 @@
kSnapshotSize));
std::string base_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
std::string snap_device;
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
@@ -215,7 +254,7 @@
kDeviceSize));
std::string base_device, snap_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
std::string test_string = "This is a test string.";
@@ -270,7 +309,7 @@
kDeviceSize));
std::string base_device, snap_device;
- ASSERT_TRUE(CreateTempDevice("base-device", kDeviceSize, &base_device));
+ ASSERT_TRUE(CreatePartition("base-device", kDeviceSize, &base_device));
ASSERT_TRUE(sm->MapSnapshot(lock_.get(), "test-snapshot", base_device, 10s, &snap_device));
// Keep an open handle to the cow device. This should cause the merge to
@@ -321,10 +360,14 @@
::testing::InitGoogleTest(&argc, argv);
std::vector<std::string> paths = {
+ // clang-format off
"/data/gsi/ota/test",
+ "/data/gsi/ota/test/super",
"/metadata/gsi/ota/test",
+ "/metadata/gsi/ota/test/super",
"/metadata/ota/test",
"/metadata/ota/test/snapshots",
+ // clang-format on
};
for (const auto& path : paths) {
if (!Mkdir(path)) {
@@ -336,9 +379,38 @@
test_device = new TestDeviceInfo();
sm = SnapshotManager::New(test_device);
if (!sm) {
- std::cerr << "Could not create snapshot manager";
+ std::cerr << "Could not create snapshot manager\n";
return 1;
}
- return RUN_ALL_TESTS();
+ // Use a separate image manager for our fake super partition.
+ auto super_images = IImageManager::Open("ota/test/super", 10s);
+ if (!super_images) {
+ std::cerr << "Could not create image manager\n";
+ return 1;
+ }
+
+ // Clean up previous run.
+ CleanupPartitions();
+ DeleteBackingImage(super_images.get(), "fake-super");
+
+ // Create and map the fake super partition.
+ static constexpr int kImageFlags =
+ IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
+ if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
+ std::cerr << "Could not create fake super partition\n";
+ return 1;
+ }
+ if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
+ std::cerr << "Could not map fake super partition\n";
+ return 1;
+ }
+
+ auto result = RUN_ALL_TESTS();
+
+ // Clean up again.
+ CleanupPartitions();
+ DeleteBackingImage(super_images.get(), "fake-super");
+
+ return result;
}
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
new file mode 100644
index 0000000..17ffa4e
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -0,0 +1,50 @@
+// 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 "test_helpers.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace snapshot {
+
+using android::fiemap::IImageManager;
+
+void DeleteBackingImage(IImageManager* manager, const std::string& name) {
+ if (manager->IsImageMapped(name)) {
+ ASSERT_TRUE(manager->UnmapImageDevice(name));
+ }
+ if (manager->BackingImageExists(name)) {
+ ASSERT_TRUE(manager->DeleteBackingImage(name));
+ }
+}
+
+android::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,
+ int flags) const {
+ if (partition_name == "super") {
+ return PartitionOpener::Open(fake_super_path_, flags);
+ }
+ return PartitionOpener::Open(partition_name, flags);
+}
+
+bool TestPartitionOpener::GetInfo(const std::string& partition_name,
+ android::fs_mgr::BlockDeviceInfo* info) const {
+ if (partition_name == "super") {
+ return PartitionOpener::GetInfo(fake_super_path_, info);
+ }
+ return PartitionOpener::GetInfo(partition_name, info);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
new file mode 100644
index 0000000..9491be3
--- /dev/null
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -0,0 +1,58 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+
+#include <libfiemap/image_manager.h>
+#include <liblp/partition_opener.h>
+#include <libsnapshot/snapshot.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::string_literals;
+
+class TestDeviceInfo : public SnapshotManager::IDeviceInfo {
+ public:
+ std::string GetGsidDir() const override { return "ota/test"s; }
+ std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
+ std::string GetSlotSuffix() const override { return slot_suffix_; }
+
+ void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }
+
+ private:
+ std::string slot_suffix_ = "_a";
+};
+
+// Redirect requests for "super" to our fake super partition.
+class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
+ public:
+ explicit TestPartitionOpener(const std::string& fake_super_path)
+ : fake_super_path_(fake_super_path) {}
+
+ android::base::unique_fd Open(const std::string& partition_name, int flags) const override;
+ bool GetInfo(const std::string& partition_name,
+ android::fs_mgr::BlockDeviceInfo* info) const override;
+
+ private:
+ std::string fake_super_path_;
+};
+
+// Helper for error-spam-free cleanup.
+void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
+
+} // namespace snapshot
+} // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index 38d495f..6f02555 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -117,6 +117,7 @@
"firmware_handler.cpp",
"first_stage_init.cpp",
"first_stage_mount.cpp",
+ "fscrypt_init_extensions.cpp",
"import_parser.cpp",
"init.cpp",
"interface_utils.cpp",
@@ -206,9 +207,18 @@
// ------------------------------------------------------------------------------
cc_test {
- name: "init_tests",
+ name: "CtsInitTestCases",
defaults: ["init_defaults"],
- compile_multilib: "first",
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
+
srcs: [
"devices_test.cpp",
"init_test.cpp",
@@ -225,7 +235,12 @@
"util_test.cpp",
],
static_libs: ["libinit"],
- test_suites: ["device-tests"],
+
+ test_suites: [
+ "cts",
+ "device-tests",
+ "vts",
+ ],
}
cc_benchmark {
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
new file mode 100644
index 0000000..94a02e6
--- /dev/null
+++ b/init/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for CTS init test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="systems" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsInitTestCases" />
+ <option name="runtime-hint" value="65s" />
+ </test>
+</configuration>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a2d782b..ed24026 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -55,7 +55,6 @@
#include <cutils/android_reboot.h>
#include <fs_mgr.h>
#include <fscrypt/fscrypt.h>
-#include <fscrypt/fscrypt_init_extensions.h>
#include <libgsi/libgsi.h>
#include <selinux/android.h>
#include <selinux/label.h>
@@ -64,6 +63,7 @@
#include "action_manager.h"
#include "bootchart.h"
+#include "fscrypt_init_extensions.h"
#include "init.h"
#include "mount_namespace.h"
#include "parser.h"
@@ -640,12 +640,7 @@
if (!ReadFstabFromFile(fstab_file, &fstab)) {
return Error() << "Could not read fstab";
}
-
- auto mount_fstab_return_code =
- CallFunctionAndHandleProperties(fs_mgr_mount_all, &fstab, mount_mode);
- if (!mount_fstab_return_code) {
- return Error() << "Could not call fs_mgr_mount_all(): " << mount_fstab_return_code.error();
- }
+ auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_mode);
property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc && SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {
@@ -656,7 +651,7 @@
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- auto queue_fs_result = queue_fs_event(*mount_fstab_return_code);
+ auto queue_fs_result = queue_fs_event(mount_fstab_return_code);
if (!queue_fs_result) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -672,13 +667,8 @@
return Error() << "Could not read fstab";
}
- auto result = CallFunctionAndHandleProperties(fs_mgr_umount_all, &fstab);
- if (!result) {
- return Error() << "Could not call fs_mgr_mount_all() " << result.error();
- }
-
- if (*result != 0) {
- return Error() << "fs_mgr_mount_all() failed: " << *result;
+ if (auto result = fs_mgr_umount_all(&fstab); result != 0) {
+ return Error() << "umount_fstab() failed " << result;
}
return {};
}
@@ -689,13 +679,8 @@
return Error() << "Could not read fstab '" << args[1] << "'";
}
- auto result = CallFunctionAndHandleProperties(fs_mgr_swapon_all, fstab);
- if (!result) {
- return Error() << "Could not call fs_mgr_swapon_all() " << result.error();
- }
-
- if (*result == 0) {
- return Error() << "fs_mgr_swapon_all() failed.";
+ if (!fs_mgr_swapon_all(fstab)) {
+ return Error() << "fs_mgr_swapon_all() failed";
}
return {};
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1a5ed28..dffd6af 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -602,19 +602,11 @@
return;
}
- // Find the name of the super partition for the GSI. It will either be
- // "userdata", or a block device such as an sdcard. There are no by-name
- // partitions other than userdata that we support installing GSIs to.
+ // Find the super name. PartitionOpener will ensure this translates to the
+ // correct block device path.
auto super = GetMetadataSuperBlockDevice(*metadata.get());
- std::string super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
- std::string super_path;
- if (super_name == "userdata") {
- super_path = "/dev/block/by-name/" + super_name;
- } else {
- super_path = "/dev/block/" + super_name;
- }
-
- if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path)) {
+ auto super_name = android::fs_mgr::GetBlockDevicePartitionName(*super);
+ if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_name)) {
LOG(ERROR) << "GSI partition layout could not be instantiated";
return;
}
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
new file mode 100644
index 0000000..956228a
--- /dev/null
+++ b/init/fscrypt_init_extensions.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2016 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 "fscrypt_init_extensions.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fts.h>
+#include <fscrypt/fscrypt.h>
+#include <keyutils.h>
+#include <logwrap/logwrap.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+
+#define TAG "fscrypt"
+
+static int set_system_de_policy_on(char const* dir);
+
+int fscrypt_install_keyring()
+{
+ key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0,
+ KEY_SPEC_SESSION_KEYRING);
+
+ if (device_keyring == -1) {
+ PLOG(ERROR) << "Failed to create keyring";
+ return -1;
+ }
+
+ LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
+
+ return 0;
+}
+
+// TODO(b/139378601): use a single central implementation of this.
+static void delete_dir_contents(const char* dir) {
+ char* const paths[2] = {const_cast<char*>(dir), nullptr};
+ FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+ FTSENT* cur;
+ while ((cur = fts_read(fts)) != nullptr) {
+ if (cur->fts_info == FTS_ERR) {
+ PLOG(ERROR) << "fts_read";
+ break;
+ }
+ if (strcmp(dir, cur->fts_path) == 0) {
+ continue;
+ }
+ switch (cur->fts_info) {
+ case FTS_D:
+ break; // Ignore these
+ case FTS_DP:
+ if (rmdir(cur->fts_path) == -1) {
+ PLOG(ERROR) << "rmdir " << cur->fts_path;
+ }
+ break;
+ default:
+ PLOG(ERROR) << "FTS unexpected type " << cur->fts_info << " at " << cur->fts_path;
+ if (rmdir(cur->fts_path) != -1) break;
+ // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)
+ FALLTHROUGH_INTENDED;
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (unlink(cur->fts_path) == -1) {
+ PLOG(ERROR) << "unlink " << cur->fts_path;
+ }
+ break;
+ }
+ }
+
+ if (fts_close(fts) != 0) {
+ PLOG(ERROR) << "fts_close";
+ }
+}
+
+int fscrypt_set_directory_policy(const char* dir)
+{
+ const std::string prefix = "/data/";
+
+ if (!dir || strncmp(dir, prefix.c_str(), prefix.size())) {
+ return 0;
+ }
+
+ // Special-case /data/media/obb per b/64566063
+ if (strcmp(dir, "/data/media/obb") == 0) {
+ // Try to set policy on this directory, but if it is non-empty this may fail.
+ set_system_de_policy_on(dir);
+ return 0;
+ }
+
+ // Only set policy on first level /data directories
+ // To make this less restrictive, consider using a policy file.
+ // However this is overkill for as long as the policy is simply
+ // to apply a global policy to all /data folders created via makedir
+ if (strchr(dir + prefix.size(), '/')) {
+ return 0;
+ }
+
+ // Special case various directories that must not be encrypted,
+ // often because their subdirectories must be encrypted.
+ // This isn't a nice way to do this, see b/26641735
+ std::vector<std::string> directories_to_exclude = {
+ "lost+found",
+ "system_ce", "system_de",
+ "misc_ce", "misc_de",
+ "vendor_ce", "vendor_de",
+ "media",
+ "data", "user", "user_de",
+ "apex", "preloads", "app-staging",
+ "gsi",
+ };
+ for (const auto& d: directories_to_exclude) {
+ if ((prefix + d) == dir) {
+ LOG(INFO) << "Not setting policy on " << dir;
+ return 0;
+ }
+ }
+ int err = set_system_de_policy_on(dir);
+ if (err == 0) {
+ return 0;
+ }
+ // Empty these directories if policy setting fails.
+ std::vector<std::string> wipe_on_failure = {
+ "rollback", "rollback-observer", // b/139193659
+ };
+ for (const auto& d : wipe_on_failure) {
+ if ((prefix + d) == dir) {
+ LOG(ERROR) << "Setting policy failed, deleting: " << dir;
+ delete_dir_contents(dir);
+ err = set_system_de_policy_on(dir);
+ break;
+ }
+ }
+ return err;
+}
+
+static int set_system_de_policy_on(char const* dir) {
+ std::string ref_filename = std::string("/data") + fscrypt_key_ref;
+ std::string policy;
+ if (!android::base::ReadFileToString(ref_filename, &policy)) {
+ LOG(ERROR) << "Unable to read system policy to set on " << dir;
+ return -1;
+ }
+
+ auto type_filename = std::string("/data") + fscrypt_key_mode;
+ std::string modestring;
+ if (!android::base::ReadFileToString(type_filename, &modestring)) {
+ LOG(ERROR) << "Cannot read mode";
+ }
+
+ std::vector<std::string> modes = android::base::Split(modestring, ":");
+
+ if (modes.size() < 1 || modes.size() > 2) {
+ LOG(ERROR) << "Invalid encryption mode string: " << modestring;
+ return -1;
+ }
+
+ LOG(INFO) << "Setting policy on " << dir;
+ int result = fscrypt_policy_ensure(dir, policy.c_str(), policy.length(),
+ modes[0].c_str(),
+ modes.size() >= 2 ?
+ modes[1].c_str() : "aes-256-cts");
+ if (result) {
+ LOG(ERROR) << android::base::StringPrintf(
+ "Setting %02x%02x%02x%02x policy on %s failed!",
+ policy[0], policy[1], policy[2], policy[3], dir);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/init/fscrypt_init_extensions.h b/init/fscrypt_init_extensions.h
new file mode 100644
index 0000000..2b6c46e
--- /dev/null
+++ b/init/fscrypt_init_extensions.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 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 _FSCRYPT_INIT_EXTENSIONS_H_
+#define _FSCRYPT_INIT_EXTENSIONS_H_
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+__BEGIN_DECLS
+
+// These functions assume they are being called from init
+// They will not operate properly outside of init
+int fscrypt_install_keyring();
+int fscrypt_set_directory_policy(const char* path);
+
+__END_DECLS
+
+#endif // _FSCRYPT_INIT_EXTENSIONS_H_
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 17622a3..3408ff3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -39,7 +39,6 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
-#include <atomic>
#include <map>
#include <memory>
#include <mutex>
@@ -53,7 +52,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
#include <selinux/android.h>
@@ -61,6 +59,7 @@
#include <selinux/selinux.h>
#include "debug_ramdisk.h"
+#include "epoll.h"
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
@@ -77,7 +76,6 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::Trim;
-using android::base::unique_fd;
using android::base::WriteStringToFile;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
@@ -1004,42 +1002,5 @@
}
}
-Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f) {
- unique_fd reader;
- unique_fd writer;
- if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &reader, &writer)) {
- return ErrnoError() << "Could not create socket pair";
- }
-
- int result = 0;
- std::atomic<bool> end = false;
- auto thread = std::thread{[&f, &result, &end, &writer] {
- result = f();
- end = true;
- send(writer, "1", 1, 0);
- }};
-
- Epoll epoll;
- if (auto result = epoll.Open(); !result) {
- return Error() << "Could not create epoll: " << result.error();
- }
- if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
- return Error() << "Could not register epoll handler for property fd: " << result.error();
- }
-
- // No-op function, just used to break from loop.
- if (auto result = epoll.RegisterHandler(reader, [] {}); !result) {
- return Error() << "Could not register epoll handler for ending thread:" << result.error();
- }
-
- while (!end) {
- epoll.Wait({});
- }
-
- thread.join();
-
- return result;
-}
-
} // namespace init
} // namespace android
diff --git a/init/property_service.h b/init/property_service.h
index dc47b4d..7f9f844 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,11 +18,9 @@
#include <sys/socket.h>
-#include <functional>
#include <string>
#include "epoll.h"
-#include "result.h"
namespace android {
namespace init {
@@ -39,13 +37,5 @@
void load_persist_props();
void StartPropertyService(Epoll* epoll);
-template <typename F, typename... Args>
-Result<int> CallFunctionAndHandleProperties(F&& f, Args&&... args) {
- Result<int> CallFunctionAndHandlePropertiesImpl(const std::function<int()>& f);
-
- auto func = [&] { return f(args...); };
- return CallFunctionAndHandlePropertiesImpl(func);
-}
-
} // namespace init
} // namespace android
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index c038aff..0f4cd0d 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -56,6 +56,11 @@
}
TEST(property_service, non_utf8_value) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ae89c38..dcbff82 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -43,7 +43,7 @@
template <typename F>
void RunTest(F&& test_function) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index bfdc28e..2d7d2f8 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -68,7 +68,7 @@
TEST(ueventd, setegid_IsPerThread) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
@@ -92,11 +92,11 @@
TEST(ueventd, setfscreatecon_IsPerThread) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
if (!is_selinux_enabled() || security_getenforce() == 1) {
- GTEST_LOG_(INFO) << "Skipping test, SELinux must be enabled and in permissive mode.";
+ GTEST_SKIP() << "Skipping test, SELinux must be enabled and in permissive mode.";
return;
}
@@ -127,7 +127,7 @@
TEST(ueventd, selabel_lookup_MultiThreaded) {
if (getuid() != 0) {
- GTEST_LOG_(INFO) << "Skipping test, must be run as root.";
+ GTEST_SKIP() << "Skipping test, must be run as root.";
return;
}
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 1f87b3e..d931ed1 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -3004,21 +3004,6 @@
EXPECT_LE(0, android_log_destroy(&ctx));
ASSERT_TRUE(NULL == ctx);
}
-
-TEST(liblog, android_log_write_list_buffer) {
- __android_log_event_list ctx(1005);
- ctx << 1005 << "tag_def"
- << "(tag|1),(name|3),(format|3)";
- std::string buffer(ctx);
- ctx.close();
-
- char msgBuf[1024];
- memset(msgBuf, 0, sizeof(msgBuf));
- EXPECT_EQ(android_log_buffer_to_string(buffer.data(), buffer.length(), msgBuf,
- sizeof(msgBuf)),
- 0);
- EXPECT_STREQ(msgBuf, "[1005,tag_def,(tag|1),(name|3),(format|3)]");
-}
#endif // USING_LOGGER_DEFAULT
#ifdef USING_LOGGER_DEFAULT // Do not retest pmsg functionality
diff --git a/liblog/tests/liblog_test_default.cpp b/liblog/tests/liblog_test_default.cpp
index 9fc443c..2edea27 100644
--- a/liblog/tests/liblog_test_default.cpp
+++ b/liblog/tests/liblog_test_default.cpp
@@ -2,4 +2,5 @@
#include <log/log_transport.h>
#define TEST_LOGGER LOGGER_DEFAULT
#endif
+#define USING_LOGGER_DEFAULT
#include "liblog_test.cpp"
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index 7f5768c..9a33b55 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -124,7 +124,7 @@
// we might as well end up loading them from /system/lib or /product/lib
// For now we rely on CTS test to catch things like this but
// it should probably be addressed in the future.
- for (const auto& soname : android::base::Split(default_public_libraries(), ":")) {
+ for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) {
LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr,
"Error preloading public library %s: %s", soname.c_str(), dlerror());
}
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index a641109..75255b6 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -29,6 +29,7 @@
#include "public_libraries.h"
using namespace ::testing;
+using namespace ::android::nativeloader::internal;
namespace android {
namespace nativeloader {
@@ -289,7 +290,7 @@
void SetExpectations() {
std::vector<std::string> default_public_libs =
- android::base::Split(default_public_libraries(), ":");
+ android::base::Split(preloadable_public_libraries(), ":");
for (auto l : default_public_libs) {
EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE))
.WillOnce(Return(any_nonnull));
@@ -576,5 +577,87 @@
INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool());
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
+
+TEST(NativeLoaderConfigParser, NamesAndComments) {
+ const char file_content[] = R"(
+######
+
+libA.so
+#libB.so
+
+
+ libC.so
+libD.so
+ #### libE.so
+)";
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"};
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithBitness) {
+ const char file_content[] = R"(
+libA.so 32
+libB.so 64
+libC.so
+)";
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libB.so", "libC.so"};
+#else
+ const std::vector<std::string> expected_result = {"libA.so", "libC.so"};
+#endif
+ Result<std::vector<std::string>> result = ParseConfig(file_content, always_true);
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreload) {
+ const char file_content[] = R"(
+libA.so nopreload
+libB.so nopreload
+libC.so
+)";
+
+ const std::vector<std::string> expected_result = {"libC.so"};
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) {
+ const char file_content[] = R"(
+libA.so nopreload 32
+libB.so 64 nopreload
+libC.so 32
+libD.so 64
+libE.so nopreload
+)";
+
+#if defined(__LP64__)
+ const std::vector<std::string> expected_result = {"libD.so"};
+#else
+ const std::vector<std::string> expected_result = {"libC.so"};
+#endif
+ Result<std::vector<std::string>> result =
+ ParseConfig(file_content,
+ [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; });
+ ASSERT_TRUE(result) << result.error().message();
+ ASSERT_EQ(expected_result, *result);
+}
+
+TEST(NativeLoaderConfigParser, RejectMalformed) {
+ ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true));
+ ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true));
+ ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true));
+ ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true));
+}
+
} // namespace nativeloader
} // namespace android
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 6cee668..3694360 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -34,7 +34,8 @@
namespace android::nativeloader {
-using namespace std::string_literals;
+using namespace internal;
+using namespace ::std::string_literals;
using android::base::ErrnoError;
using android::base::Errorf;
using android::base::Result;
@@ -95,53 +96,21 @@
file_name->insert(insert_pos, vndk_version_str());
}
-const std::function<Result<void>(const std::string&)> always_true =
- [](const std::string&) -> Result<void> { return {}; };
+const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
+ [](const struct ConfigEntry&) -> Result<bool> { return true; };
Result<std::vector<std::string>> ReadConfig(
const std::string& configFile,
- const std::function<Result<void>(const std::string& /* soname */)>& check_soname) {
- // Read list of public native libraries from the config file.
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
std::string file_content;
if (!base::ReadFileToString(configFile, &file_content)) {
return ErrnoError();
}
-
- std::vector<std::string> lines = base::Split(file_content, "\n");
-
- std::vector<std::string> sonames;
- for (auto& line : lines) {
- auto trimmed_line = base::Trim(line);
- if (trimmed_line[0] == '#' || trimmed_line.empty()) {
- continue;
- }
- size_t space_pos = trimmed_line.rfind(' ');
- if (space_pos != std::string::npos) {
- std::string type = trimmed_line.substr(space_pos + 1);
- if (type != "32" && type != "64") {
- return Errorf("Malformed line: {}", line);
- }
-#if defined(__LP64__)
- // Skip 32 bit public library.
- if (type == "32") {
- continue;
- }
-#else
- // Skip 64 bit public library.
- if (type == "64") {
- continue;
- }
-#endif
- trimmed_line.resize(space_pos);
- }
-
- auto ret = check_soname(trimmed_line);
- if (!ret) {
- return ret.error();
- }
- sonames.push_back(trimmed_line);
+ Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
+ if (!result) {
+ return Errorf("Cannot parse {}: {}", configFile, result.error().message());
}
- return sonames;
+ return result;
}
void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
@@ -165,13 +134,13 @@
config_file_path.c_str());
auto ret = ReadConfig(
- config_file_path, [&company_name](const std::string& soname) -> Result<void> {
- if (android::base::StartsWith(soname, "lib") &&
- android::base::EndsWith(soname, "." + company_name + ".so")) {
- return {};
+ config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
+ if (android::base::StartsWith(entry.soname, "lib") &&
+ android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
+ return true;
} else {
- return Errorf("Library name \"{}\" does not end with the company name {}.", soname,
- company_name);
+ return Errorf("Library name \"{}\" does not end with the company name {}.",
+ entry.soname, company_name);
}
});
if (ret) {
@@ -185,9 +154,16 @@
}
}
-static std::string InitDefaultPublicLibraries() {
+static std::string InitDefaultPublicLibraries(bool for_preload) {
std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
- auto sonames = ReadConfig(config_file, always_true);
+ auto sonames =
+ ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
+ if (for_preload) {
+ return !entry.nopreload;
+ } else {
+ return true;
+ }
+ });
if (!sonames) {
LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
config_file.c_str(), sonames.error().message().c_str());
@@ -290,8 +266,13 @@
} // namespace
+const std::string& preloadable_public_libraries() {
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
+ return list;
+}
+
const std::string& default_public_libraries() {
- static std::string list = InitDefaultPublicLibraries();
+ static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
return list;
}
@@ -325,4 +306,61 @@
return list;
}
+namespace internal {
+// Exported for testing
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+ for (auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+
+ std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
+ if (tokens.size() < 1 || tokens.size() > 3) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
+ size_t i = tokens.size();
+ while (i-- > 0) {
+ if (tokens[i] == "nopreload") {
+ entry.nopreload = true;
+ } else if (tokens[i] == "32" || tokens[i] == "64") {
+ if (entry.bitness != ALL) {
+ return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
+ }
+ entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
+ } else {
+ if (i != 0) {
+ return Errorf("Malformed line \"{}\"", line);
+ }
+ entry.soname = tokens[i];
+ }
+ }
+
+ // skip 32-bit lib on 64-bit process and vice versa
+#if defined(__LP64__)
+ if (entry.bitness == ONLY_32) continue;
+#else
+ if (entry.bitness == ONLY_64) continue;
+#endif
+
+ Result<bool> ret = filter_fn(entry);
+ if (!ret) {
+ return ret.error();
+ }
+ if (*ret) {
+ // filter_fn has returned true.
+ sonames.push_back(entry.soname);
+ }
+ }
+ return sonames;
+}
+
+} // namespace internal
+
} // namespace android::nativeloader
diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h
index 9bb3366..2de4172 100644
--- a/libnativeloader/public_libraries.h
+++ b/libnativeloader/public_libraries.h
@@ -15,13 +15,19 @@
*/
#pragma once
+#include <algorithm>
#include <string>
+#include <android-base/result.h>
+
namespace android::nativeloader {
+using android::base::Result;
+
// These provide the list of libraries that are available to the namespace for apps.
// Not all of the libraries are available to apps. Depending on the context,
// e.g., if it is a vendor app or not, different set of libraries are made available.
+const std::string& preloadable_public_libraries();
const std::string& default_public_libraries();
const std::string& runtime_public_libraries();
const std::string& vendor_public_libraries();
@@ -30,4 +36,21 @@
const std::string& llndk_libraries();
const std::string& vndksp_libraries();
-}; // namespace android::nativeloader
+// These are exported for testing
+namespace internal {
+
+enum Bitness { ALL = 0, ONLY_32, ONLY_64 };
+
+struct ConfigEntry {
+ std::string soname;
+ bool nopreload;
+ Bitness bitness;
+};
+
+Result<std::vector<std::string>> ParseConfig(
+ const std::string& file_content,
+ const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn);
+
+} // namespace internal
+
+} // namespace android::nativeloader
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index 15f03d0..0c9a2b8 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -34,6 +34,7 @@
host_supported: true,
srcs: [
"process.cpp",
+ "process_map.cpp",
],
local_include_dirs: ["include"],
@@ -58,6 +59,7 @@
name: "libprocinfo_test",
defaults: ["libprocinfo_defaults"],
host_supported: true,
+ isolated: true,
srcs: [
"process_test.cpp",
"process_map_test.cpp",
diff --git a/libprocinfo/include/procinfo/process_map.h b/libprocinfo/include/procinfo/process_map.h
index b6ec3cb..569a022 100644
--- a/libprocinfo/include/procinfo/process_map.h
+++ b/libprocinfo/include/procinfo/process_map.h
@@ -176,5 +176,9 @@
const char* name) { maps->emplace_back(start, end, flags, pgoff, inode, name); });
}
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback);
+
} /* namespace procinfo */
} /* namespace android */
diff --git a/libprocinfo/process_map.cpp b/libprocinfo/process_map.cpp
new file mode 100644
index 0000000..5e240b9
--- /dev/null
+++ b/libprocinfo/process_map.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <procinfo/process_map.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <procinfo/process.h>
+
+namespace android {
+namespace procinfo {
+
+bool ReadMapFileAsyncSafe(const char* map_file, void* buffer, size_t buffer_size,
+ const std::function<void(uint64_t, uint64_t, uint16_t, uint64_t, ino_t,
+ const char*)>& callback) {
+ if (buffer == nullptr || buffer_size == 0) {
+ return false;
+ }
+
+ int fd = open(map_file, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return false;
+ }
+
+ char* char_buffer = reinterpret_cast<char*>(buffer);
+ size_t start = 0;
+ size_t read_bytes = 0;
+ char* line = nullptr;
+ bool read_complete = false;
+ while (true) {
+ ssize_t bytes =
+ TEMP_FAILURE_RETRY(read(fd, char_buffer + read_bytes, buffer_size - read_bytes - 1));
+ if (bytes <= 0) {
+ if (read_bytes == 0) {
+ close(fd);
+ return bytes == 0;
+ }
+ // Treat the last piece of data as the last line.
+ char_buffer[start + read_bytes] = '\n';
+ bytes = 1;
+ read_complete = true;
+ }
+ read_bytes += bytes;
+
+ while (read_bytes > 0) {
+ char* newline = reinterpret_cast<char*>(memchr(&char_buffer[start], '\n', read_bytes));
+ if (newline == nullptr) {
+ break;
+ }
+ *newline = '\0';
+ line = &char_buffer[start];
+ start = newline - char_buffer + 1;
+ read_bytes -= newline - line + 1;
+
+ // Ignore the return code, errors are okay.
+ ReadMapFileContent(line, callback);
+ }
+
+ if (read_complete) {
+ close(fd);
+ return true;
+ }
+
+ if (start == 0 && read_bytes == buffer_size - 1) {
+ // The buffer provided is too small to contain this line, give up
+ // and indicate failure.
+ close(fd);
+ return false;
+ }
+
+ // Copy any leftover data to the front of the buffer.
+ if (start > 0) {
+ if (read_bytes > 0) {
+ memmove(char_buffer, &char_buffer[start], read_bytes);
+ }
+ start = 0;
+ }
+ }
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_map_test.cpp b/libprocinfo/process_map_test.cpp
index 562d864..b1bdc08 100644
--- a/libprocinfo/process_map_test.cpp
+++ b/libprocinfo/process_map_test.cpp
@@ -16,9 +16,14 @@
#include <procinfo/process_map.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+
#include <string>
+#include <vector>
#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <gtest/gtest.h>
@@ -63,3 +68,215 @@
ASSERT_TRUE(android::procinfo::ReadProcessMaps(getpid(), &maps));
ASSERT_GT(maps.size(), 0u);
}
+
+extern "C" void malloc_disable();
+extern "C" void malloc_enable();
+
+struct TestMapInfo {
+ TestMapInfo() = default;
+ TestMapInfo(uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* new_name)
+ : start(start), end(end), flags(flags), pgoff(pgoff), inode(inode) {
+ strcpy(name, new_name);
+ }
+ uint64_t start = 0;
+ uint64_t end = 0;
+ uint16_t flags = 0;
+ uint64_t pgoff = 0;
+ ino_t inode = 0;
+ char name[100] = {};
+};
+
+void VerifyReadMapFileAsyncSafe(const char* maps_data,
+ const std::vector<TestMapInfo>& expected_info) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(maps_data, tf.fd));
+
+ std::vector<TestMapInfo> saved_info(expected_info.size());
+ size_t num_maps = 0;
+
+ auto callback = [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff, ino_t inode,
+ const char* name) {
+ if (num_maps != saved_info.size()) {
+ TestMapInfo& saved = saved_info[num_maps];
+ saved.start = start;
+ saved.end = end;
+ saved.flags = flags;
+ saved.pgoff = pgoff;
+ saved.inode = inode;
+ strcpy(saved.name, name);
+ }
+ num_maps++;
+ };
+
+ std::vector<char> buffer(64 * 1024);
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer.data(), buffer.size(), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_TRUE(parsed) << "Parsing of data failed:\n" << maps_data;
+ ASSERT_EQ(expected_info.size(), num_maps);
+ for (size_t i = 0; i < expected_info.size(); i++) {
+ const TestMapInfo& expected = expected_info[i];
+ const TestMapInfo& saved = saved_info[i];
+ EXPECT_EQ(expected.start, saved.start);
+ EXPECT_EQ(expected.end, saved.end);
+ EXPECT_EQ(expected.flags, saved.flags);
+ EXPECT_EQ(expected.pgoff, saved.pgoff);
+ EXPECT_EQ(expected.inode, saved.inode);
+ EXPECT_STREQ(expected.name, saved.name);
+ }
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_invalid) {
+ std::vector<TestMapInfo> expected_info;
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_with_newline) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0x12c00000, 0x2ac00000, PROT_READ | PROT_WRITE, 0x100, 10267643,
+ "/lib/fake.so");
+
+ VerifyReadMapFileAsyncSafe("12c00000-2ac00000 rw-p 00000100 00:05 10267643 /lib/fake.so\n",
+ expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_single_no_library) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 0xb00, 101, "");
+
+ VerifyReadMapFileAsyncSafe("a0000-c0000 rwxp 00000b00 00:05 101", expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple) {
+ std::vector<TestMapInfo> expected_info;
+ expected_info.emplace_back(0xa0000, 0xc0000, PROT_READ | PROT_WRITE | PROT_EXEC, 1, 100, "");
+ expected_info.emplace_back(0xd0000, 0xe0000, PROT_READ, 2, 101, "/lib/libsomething1.so");
+ expected_info.emplace_back(0xf0000, 0x100000, PROT_WRITE, 3, 102, "/lib/libsomething2.so");
+ expected_info.emplace_back(0x110000, 0x120000, PROT_EXEC, 4, 103, "[anon:something or another]");
+
+ std::string map_data =
+ "0a0000-0c0000 rwxp 00000001 00:05 100\n"
+ "0d0000-0e0000 r--p 00000002 00:05 101 /lib/libsomething1.so\n"
+ "0f0000-100000 -w-p 00000003 00:05 102 /lib/libsomething2.so\n"
+ "110000-120000 --xp 00000004 00:05 103 [anon:something or another]\n";
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_multiple_reads) {
+ std::vector<TestMapInfo> expected_info;
+ std::string map_data;
+ uint64_t start = 0xa0000;
+ for (size_t i = 0; i < 10000; i++) {
+ map_data += android::base::StringPrintf("%" PRIx64 "-%" PRIx64 " r--p %zx 01:20 %zu fake.so\n",
+ start, start + 0x1000, i, 1000 + i);
+ expected_info.emplace_back(start, start + 0x1000, PROT_READ, i, 1000 + i, "fake.so");
+ }
+
+ VerifyReadMapFileAsyncSafe(map_data.c_str(), expected_info);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_nullptr) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", nullptr, 10, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_size_zero) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, 0, callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_no_calls) {
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[10];
+ bool parsed =
+ android::procinfo::ReadMapFileAsyncSafe("/proc/self/maps", buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
+
+TEST(process_map, ReadMapFileAsyncSafe_buffer_too_small_could_parse) {
+ TemporaryFile tf;
+ ASSERT_TRUE(android::base::WriteStringToFd(
+ "0a0000-0c0000 rwxp 00000001 00:05 100 /fake/lib.so\n", tf.fd));
+
+ size_t num_calls = 0;
+ auto callback = [&](uint64_t, uint64_t, uint16_t, uint64_t, ino_t, const char*) { num_calls++; };
+
+#if defined(__BIONIC__)
+ // Any allocations will block after this call.
+ malloc_disable();
+#endif
+
+ char buffer[39];
+ bool parsed = android::procinfo::ReadMapFileAsyncSafe(tf.path, buffer, sizeof(buffer), callback);
+
+#if defined(__BIONIC__)
+ malloc_enable();
+#endif
+
+ ASSERT_FALSE(parsed);
+ EXPECT_EQ(0UL, num_calls);
+}
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 2fd9f95..82c4fb9 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -56,14 +56,7 @@
mLogId(elem.mLogId),
mDropped(elem.mDropped) {
if (mDropped) {
- if (elem.isBinary() && elem.mMsg != nullptr) {
- // for the following "len" value, refer to : setDropped(uint16_t value), getTag()
- const int len = sizeof(android_event_header_t);
- mMsg = new char[len];
- memcpy(mMsg, elem.mMsg, len);
- } else {
- mMsg = nullptr;
- }
+ mTag = elem.getTag();
} else {
mMsg = new char[mMsgLen];
memcpy(mMsg, elem.mMsg, mMsgLen);
@@ -71,31 +64,43 @@
}
LogBufferElement::~LogBufferElement() {
- delete[] mMsg;
+ if (!mDropped) {
+ delete[] mMsg;
+ }
}
uint32_t LogBufferElement::getTag() const {
- return (isBinary() &&
- ((mDropped && mMsg != nullptr) ||
- (!mDropped && mMsgLen >= sizeof(android_event_header_t))))
- ? reinterpret_cast<const android_event_header_t*>(mMsg)->tag
- : 0;
+ // Binary buffers have no tag.
+ if (!isBinary()) {
+ return 0;
+ }
+
+ // Dropped messages store the tag in place of mMsg.
+ if (mDropped) {
+ return mTag;
+ }
+
+ // For non-dropped messages, we get the tag from the message header itself.
+ if (mMsgLen < sizeof(android_event_header_t)) {
+ return 0;
+ }
+
+ return reinterpret_cast<const android_event_header_t*>(mMsg)->tag;
}
uint16_t LogBufferElement::setDropped(uint16_t value) {
- // The tag information is saved in mMsg data, if the tag is non-zero
- // save only the information needed to get the tag.
- if (getTag() != 0) {
- if (mMsgLen > sizeof(android_event_header_t)) {
- char* truncated_msg = new char[sizeof(android_event_header_t)];
- memcpy(truncated_msg, mMsg, sizeof(android_event_header_t));
- delete[] mMsg;
- mMsg = truncated_msg;
- } // mMsgLen == sizeof(android_event_header_t), already at minimum.
- } else {
- delete[] mMsg;
- mMsg = nullptr;
+ if (mDropped) {
+ return mDroppedCount = value;
}
+
+ // The tag information is saved in mMsg data, which is in a union with mTag, used after mDropped
+ // is set to true. Therefore we save the tag value aside, delete mMsg, then set mTag to the tag
+ // value in its place.
+ auto old_tag = getTag();
+ delete[] mMsg;
+ mMsg = nullptr;
+
+ mTag = old_tag;
mDropped = true;
return mDroppedCount = value;
}
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 57b0a95..da4991b 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -41,7 +41,10 @@
const uint32_t mPid;
const uint32_t mTid;
log_time mRealTime;
- char* mMsg;
+ union {
+ char* mMsg; // mDropped == false
+ int32_t mTag; // mDropped == true
+ };
union {
const uint16_t mMsgLen; // mDropped == false
uint16_t mDroppedCount; // mDropped == true
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4d34b67..0f61a61 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -127,6 +127,10 @@
mkdir /mnt/expand 0771 system system
mkdir /mnt/appfuse 0711 root root
+ # tmpfs place for BORINGSSL_self_test() to remember whether it has run
+ mkdir /dev/boringssl 0755 root root
+ mkdir /dev/boringssl/selftest 0755 root root
+
# Storage views to support runtime permissions
mkdir /mnt/runtime 0700 root root
mkdir /mnt/runtime/default 0755 root root
@@ -614,6 +618,9 @@
mkdir /data/cache/backup_stage 0700 system system
mkdir /data/cache/backup 0700 system system
+ mkdir /data/rollback 0700 system system
+ mkdir /data/rollback-observer 0700 system system
+
# Wait for apexd to finish activating APEXes before starting more processes.
wait_for_prop apexd.status ready
parse_apex_configs
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index ca2421b..6668cf3 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -41,7 +41,6 @@
using android::hardware::health::V2_0::StorageInfo;
const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
-const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
const char* emmc_info_t::emmc_ver_str[9] = {
"4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0", "5.1"
};
@@ -62,10 +61,8 @@
if (healthService != nullptr) {
return new health_storage_info_t(healthService);
}
- if (FileExists(emmc_info_t::emmc_sysfs) ||
- FileExists(emmc_info_t::emmc_debugfs)) {
- return new emmc_info_t;
- }
+ if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;
+
if (FileExists(ufs_info_t::health_file)) {
return new ufs_info_t;
}
@@ -241,8 +238,7 @@
void emmc_info_t::report()
{
- if (!report_sysfs() && !report_debugfs())
- return;
+ if (!report_sysfs()) return;
publish();
}
@@ -284,54 +280,6 @@
return true;
}
-namespace {
-
-const size_t EXT_CSD_FILE_MIN_SIZE = 1024;
-/* 2 characters in string for each byte */
-const size_t EXT_CSD_REV_IDX = 192 * 2;
-const size_t EXT_PRE_EOL_INFO_IDX = 267 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * 2;
-const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * 2;
-
-} // namespace
-
-bool emmc_info_t::report_debugfs()
-{
- string buffer;
- uint16_t rev = 0;
-
- if (!ReadFileToString(emmc_debugfs, &buffer) ||
- buffer.length() < (size_t)EXT_CSD_FILE_MIN_SIZE) {
- return false;
- }
-
- string str = buffer.substr(EXT_CSD_REV_IDX, 2);
- if (!ParseUint(str, &rev) ||
- rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {
- return false;
- }
-
- version = "emmc ";
- version += emmc_ver_str[rev];
-
- str = buffer.substr(EXT_PRE_EOL_INFO_IDX, 2);
- if (!ParseUint(str, &eol)) {
- return false;
- }
-
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, 2);
- if (!ParseUint(str, &lifetime_a)) {
- return false;
- }
-
- str = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, 2);
- if (!ParseUint(str, &lifetime_b)) {
- return false;
- }
-
- return true;
-}
-
void ufs_info_t::report()
{
string buffer;