Merge "Remove bootstrapping the passthrough mount."
diff --git a/adb/Android.bp b/adb/Android.bp
index d605907..bd1f124 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -134,6 +134,7 @@
"transport_fd.cpp",
"transport_local.cpp",
"transport_usb.cpp",
+ "types.cpp",
]
libadb_posix_srcs = [
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 1abae87..a9ad805 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -117,14 +117,18 @@
}
};
+template <class Payload>
struct IoBlock {
bool pending = false;
struct iocb control = {};
- std::shared_ptr<Block> payload;
+ Payload payload;
TransferId id() const { return TransferId::from_value(control.aio_data); }
};
+using IoReadBlock = IoBlock<Block>;
+using IoWriteBlock = IoBlock<std::shared_ptr<Block>>;
+
struct ScopedAioContext {
ScopedAioContext() = default;
~ScopedAioContext() { reset(); }
@@ -208,16 +212,17 @@
virtual bool Write(std::unique_ptr<apacket> packet) override final {
LOG(DEBUG) << "USB write: " << dump_header(&packet->msg);
- Block header(sizeof(packet->msg));
- memcpy(header.data(), &packet->msg, sizeof(packet->msg));
+ auto header = std::make_shared<Block>(sizeof(packet->msg));
+ memcpy(header->data(), &packet->msg, sizeof(packet->msg));
std::lock_guard<std::mutex> lock(write_mutex_);
- write_requests_.push_back(CreateWriteBlock(std::move(header), next_write_id_++));
+ write_requests_.push_back(
+ CreateWriteBlock(std::move(header), 0, sizeof(packet->msg), next_write_id_++));
if (!packet->payload.empty()) {
// The kernel attempts to allocate a contiguous block of memory for each write,
// which can fail if the write is large and the kernel heap is fragmented.
// Split large writes into smaller chunks to avoid this.
- std::shared_ptr<Block> payload = std::make_shared<Block>(std::move(packet->payload));
+ auto payload = std::make_shared<Block>(std::move(packet->payload));
size_t offset = 0;
size_t len = payload->size();
@@ -464,16 +469,20 @@
worker_thread_.join();
}
- void PrepareReadBlock(IoBlock* block, uint64_t id) {
+ void PrepareReadBlock(IoReadBlock* block, uint64_t id) {
block->pending = false;
- block->payload = std::make_shared<Block>(kUsbReadSize);
+ if (block->payload.capacity() >= kUsbReadSize) {
+ block->payload.resize(kUsbReadSize);
+ } else {
+ block->payload = Block(kUsbReadSize);
+ }
block->control.aio_data = static_cast<uint64_t>(TransferId::read(id));
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data());
- block->control.aio_nbytes = block->payload->size();
+ block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload.data());
+ block->control.aio_nbytes = block->payload.size();
}
- IoBlock CreateReadBlock(uint64_t id) {
- IoBlock block;
+ IoReadBlock CreateReadBlock(uint64_t id) {
+ IoReadBlock block;
PrepareReadBlock(&block, id);
block.control.aio_rw_flags = 0;
block.control.aio_lio_opcode = IOCB_CMD_PREAD;
@@ -518,9 +527,9 @@
void HandleRead(TransferId id, int64_t size) {
uint64_t read_idx = id.id % kUsbReadQueueDepth;
- IoBlock* block = &read_requests_[read_idx];
+ IoReadBlock* block = &read_requests_[read_idx];
block->pending = false;
- block->payload->resize(size);
+ block->payload.resize(size);
// Notification for completed reads can be received out of order.
if (block->id().id != needed_read_id_) {
@@ -531,7 +540,7 @@
for (uint64_t id = needed_read_id_;; ++id) {
size_t read_idx = id % kUsbReadQueueDepth;
- IoBlock* current_block = &read_requests_[read_idx];
+ IoReadBlock* current_block = &read_requests_[read_idx];
if (current_block->pending) {
break;
}
@@ -540,19 +549,19 @@
}
}
- void ProcessRead(IoBlock* block) {
- if (!block->payload->empty()) {
+ void ProcessRead(IoReadBlock* block) {
+ if (!block->payload.empty()) {
if (!incoming_header_.has_value()) {
- CHECK_EQ(sizeof(amessage), block->payload->size());
- amessage msg;
- memcpy(&msg, block->payload->data(), sizeof(amessage));
+ CHECK_EQ(sizeof(amessage), block->payload.size());
+ amessage& msg = incoming_header_.emplace();
+ memcpy(&msg, block->payload.data(), sizeof(msg));
LOG(DEBUG) << "USB read:" << dump_header(&msg);
incoming_header_ = msg;
} else {
size_t bytes_left = incoming_header_->data_length - incoming_payload_.size();
- Block payload = std::move(*block->payload);
+ Block payload = std::move(block->payload);
CHECK_LE(payload.size(), bytes_left);
- incoming_payload_.append(std::make_unique<Block>(std::move(payload)));
+ incoming_payload_.append(std::move(payload));
}
if (incoming_header_->data_length == incoming_payload_.size()) {
@@ -560,11 +569,15 @@
packet->msg = *incoming_header_;
// TODO: Make apacket contain an IOVector so we don't have to coalesce.
- packet->payload = incoming_payload_.coalesce();
+ packet->payload = std::move(incoming_payload_).coalesce();
read_callback_(this, std::move(packet));
incoming_header_.reset();
- incoming_payload_.clear();
+ // reuse the capacity of the incoming payload while we can.
+ auto free_block = incoming_payload_.clear();
+ if (block->payload.capacity() == 0) {
+ block->payload = std::move(free_block);
+ }
}
}
@@ -572,7 +585,7 @@
SubmitRead(block);
}
- bool SubmitRead(IoBlock* block) {
+ bool SubmitRead(IoReadBlock* block) {
block->pending = true;
struct iocb* iocb = &block->control;
if (io_submit(aio_context_.get(), 1, &iocb) != 1) {
@@ -594,7 +607,7 @@
std::lock_guard<std::mutex> lock(write_mutex_);
auto it =
std::find_if(write_requests_.begin(), write_requests_.end(), [id](const auto& req) {
- return static_cast<uint64_t>(req->id()) == static_cast<uint64_t>(id);
+ return static_cast<uint64_t>(req.id()) == static_cast<uint64_t>(id);
});
CHECK(it != write_requests_.end());
@@ -605,27 +618,26 @@
SubmitWrites();
}
- std::unique_ptr<IoBlock> CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset,
- size_t len, uint64_t id) {
- auto block = std::make_unique<IoBlock>();
- block->payload = std::move(payload);
- block->control.aio_data = static_cast<uint64_t>(TransferId::write(id));
- block->control.aio_rw_flags = 0;
- block->control.aio_lio_opcode = IOCB_CMD_PWRITE;
- block->control.aio_reqprio = 0;
- block->control.aio_fildes = write_fd_.get();
- block->control.aio_buf = reinterpret_cast<uintptr_t>(block->payload->data() + offset);
- block->control.aio_nbytes = len;
- block->control.aio_offset = 0;
- block->control.aio_flags = IOCB_FLAG_RESFD;
- block->control.aio_resfd = worker_event_fd_.get();
+ IoWriteBlock CreateWriteBlock(std::shared_ptr<Block> payload, size_t offset, size_t len,
+ uint64_t id) {
+ auto block = IoWriteBlock();
+ block.payload = std::move(payload);
+ block.control.aio_data = static_cast<uint64_t>(TransferId::write(id));
+ block.control.aio_rw_flags = 0;
+ block.control.aio_lio_opcode = IOCB_CMD_PWRITE;
+ block.control.aio_reqprio = 0;
+ block.control.aio_fildes = write_fd_.get();
+ block.control.aio_buf = reinterpret_cast<uintptr_t>(block.payload->data() + offset);
+ block.control.aio_nbytes = len;
+ block.control.aio_offset = 0;
+ block.control.aio_flags = IOCB_FLAG_RESFD;
+ block.control.aio_resfd = worker_event_fd_.get();
return block;
}
- std::unique_ptr<IoBlock> CreateWriteBlock(Block payload, uint64_t id) {
- std::shared_ptr<Block> block = std::make_shared<Block>(std::move(payload));
- size_t len = block->size();
- return CreateWriteBlock(std::move(block), 0, len, id);
+ IoWriteBlock CreateWriteBlock(Block&& payload, uint64_t id) {
+ size_t len = payload.size();
+ return CreateWriteBlock(std::make_shared<Block>(std::move(payload)), 0, len, id);
}
void SubmitWrites() REQUIRES(write_mutex_) {
@@ -642,9 +654,9 @@
struct iocb* iocbs[kUsbWriteQueueDepth];
for (int i = 0; i < writes_to_submit; ++i) {
- CHECK(!write_requests_[writes_submitted_ + i]->pending);
- write_requests_[writes_submitted_ + i]->pending = true;
- iocbs[i] = &write_requests_[writes_submitted_ + i]->control;
+ CHECK(!write_requests_[writes_submitted_ + i].pending);
+ write_requests_[writes_submitted_ + i].pending = true;
+ iocbs[i] = &write_requests_[writes_submitted_ + i].control;
LOG(VERBOSE) << "submitting write_request " << static_cast<void*>(iocbs[i]);
}
@@ -689,7 +701,7 @@
std::optional<amessage> incoming_header_;
IOVector incoming_payload_;
- std::array<IoBlock, kUsbReadQueueDepth> read_requests_;
+ std::array<IoReadBlock, kUsbReadQueueDepth> read_requests_;
IOVector read_data_;
// ID of the next request that we're going to send out.
@@ -699,7 +711,7 @@
size_t needed_read_id_ = 0;
std::mutex write_mutex_;
- std::deque<std::unique_ptr<IoBlock>> write_requests_ GUARDED_BY(write_mutex_);
+ std::deque<IoWriteBlock> write_requests_ GUARDED_BY(write_mutex_);
size_t next_write_id_ GUARDED_BY(write_mutex_) = 0;
size_t writes_submitted_ GUARDED_BY(write_mutex_) = 0;
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index 98468b5..27e8c46 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -272,7 +272,9 @@
if (hostname.empty() && gListenAll) {
result = network_inaddr_any_server(port, SOCK_STREAM, error);
} else if (tcp_host_is_local(hostname)) {
- result = network_loopback_server(port, SOCK_STREAM, error);
+ result = network_loopback_server(port, SOCK_STREAM, error, true);
+ } else if (hostname == "::1") {
+ result = network_loopback_server(port, SOCK_STREAM, error, false);
} else {
// TODO: Implement me.
*error = "listening on specified hostname currently unsupported";
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index e78530c..7d5bf17 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -125,8 +125,7 @@
if (rc > 0 && static_cast<size_t>(rc) == s->packet_queue.size()) {
s->packet_queue.clear();
} else if (rc > 0) {
- // TODO: Implement a faster drop_front?
- s->packet_queue.take_front(rc);
+ s->packet_queue.drop_front(rc);
fdevent_add(s->fde, FDE_WRITE);
return SocketFlushResult::TryAgain;
} else if (rc == -1 && errno == EAGAIN) {
diff --git a/adb/sysdeps/network.h b/adb/sysdeps/network.h
index 83ce371..fadd155 100644
--- a/adb/sysdeps/network.h
+++ b/adb/sysdeps/network.h
@@ -19,4 +19,4 @@
#include <string>
int network_loopback_client(int port, int type, std::string* error);
-int network_loopback_server(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4);
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index c5c2275..a4d9013 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -119,12 +119,15 @@
return s.release();
}
-int network_loopback_server(int port, int type, std::string* error) {
- int rc = _network_loopback_server(false, port, type, error);
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4) {
+ int rc = -1;
+ if (prefer_ipv4) {
+ rc = _network_loopback_server(false, port, type, error);
+ }
- // Only attempt to listen on IPv6 if IPv4 is unavailable.
+ // Only attempt to listen on IPv6 if IPv4 is unavailable or prefer_ipv4 is false
// We don't want to start an IPv6 server if there's already an IPv4 one running.
- if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)) {
+ if (rc == -1 && (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT || !prefer_ipv4)) {
return _network_loopback_server(true, port, type, error);
}
return rc;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 4d6cf3d..d9cc36f 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -920,7 +920,8 @@
return fd;
}
-int network_loopback_server(int port, int type, std::string* error) {
+int network_loopback_server(int port, int type, std::string* error, bool prefer_ipv4) {
+ // TODO implement IPv6 support on windows
return _network_server(port, type, INADDR_LOOPBACK, error);
}
@@ -1132,7 +1133,7 @@
int local_port = -1;
std::string error;
- server = network_loopback_server(0, SOCK_STREAM, &error);
+ server = network_loopback_server(0, SOCK_STREAM, &error, true);
if (server < 0) {
D("adb_socketpair: failed to create server: %s", error.c_str());
goto fail;
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 8272722..3d6de26 100755
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -281,6 +281,37 @@
subprocess.check_output(["adb", "-P", str(port), "kill-server"],
stderr=subprocess.STDOUT)
+ @unittest.skipUnless(
+ os.name == "posix",
+ "adb doesn't yet support IPv6 on Windows",
+ )
+ def test_starts_on_ipv6_localhost(self):
+ """
+ Tests that the server can start up on ::1 and that it's accessible
+ """
+ server_port = 5037
+ # Kill any existing server on this non-default port.
+ subprocess.check_output(
+ ["adb", "-P", str(server_port), "kill-server"],
+ stderr=subprocess.STDOUT,
+ )
+ try:
+ subprocess.check_output(
+ ["adb", "-L", "tcp:[::1]:{}".format(server_port), "server"],
+ stderr=subprocess.STDOUT,
+ )
+ with fake_adbd() as (port, _):
+ with adb_connect(self, serial="localhost:{}".format(port)):
+ pass
+ finally:
+ # If we started a server, kill it.
+ subprocess.check_output(
+ ["adb", "-P", str(server_port), "kill-server"],
+ stderr=subprocess.STDOUT,
+ )
+
+
+
class EmulatorTest(unittest.TestCase):
"""Tests for the emulator connection."""
diff --git a/adb/transport_fd.cpp b/adb/transport_fd.cpp
index a93e68a..8d2ad66 100644
--- a/adb/transport_fd.cpp
+++ b/adb/transport_fd.cpp
@@ -93,8 +93,8 @@
if (pfds[0].revents & POLLIN) {
// TODO: Should we be getting blocks from a free list?
- auto block = std::make_unique<IOVector::block_type>(MAX_PAYLOAD);
- rc = adb_read(fd_.get(), &(*block)[0], block->size());
+ auto block = IOVector::block_type(MAX_PAYLOAD);
+ rc = adb_read(fd_.get(), &block[0], block.size());
if (rc == -1) {
*error = std::string("read failed: ") + strerror(errno);
return;
@@ -102,7 +102,7 @@
*error = "read failed: EOF";
return;
}
- block->resize(rc);
+ block.resize(rc);
read_buffer_.append(std::move(block));
if (!read_header_ && read_buffer_.size() >= sizeof(amessage)) {
@@ -116,7 +116,7 @@
auto data_chain = read_buffer_.take_front(read_header_->data_length);
// TODO: Make apacket carry around a IOVector instead of coalescing.
- auto payload = data_chain.coalesce<apacket::payload_type>();
+ auto payload = std::move(data_chain).coalesce();
auto packet = std::make_unique<apacket>();
packet->msg = *read_header_;
packet->payload = std::move(payload);
@@ -184,8 +184,7 @@
return WriteResult::Error;
}
- // TODO: Implement a more efficient drop_front?
- write_buffer_.take_front(rc);
+ write_buffer_.drop_front(rc);
writable_ = write_buffer_.empty();
if (write_buffer_.empty()) {
return WriteResult::Completed;
@@ -199,10 +198,10 @@
std::lock_guard<std::mutex> lock(write_mutex_);
const char* header_begin = reinterpret_cast<const char*>(&packet->msg);
const char* header_end = header_begin + sizeof(packet->msg);
- auto header_block = std::make_unique<IOVector::block_type>(header_begin, header_end);
+ auto header_block = IOVector::block_type(header_begin, header_end);
write_buffer_.append(std::move(header_block));
if (!packet->payload.empty()) {
- write_buffer_.append(std::make_unique<IOVector::block_type>(std::move(packet->payload)));
+ write_buffer_.append(std::move(packet->payload));
}
WriteResult result = DispatchWrites();
diff --git a/adb/types.cpp b/adb/types.cpp
new file mode 100644
index 0000000..26b77ab
--- /dev/null
+++ b/adb/types.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 "types.h"
+
+IOVector& IOVector::operator=(IOVector&& move) noexcept {
+ chain_ = std::move(move.chain_);
+ chain_length_ = move.chain_length_;
+ begin_offset_ = move.begin_offset_;
+ start_index_ = move.start_index_;
+
+ move.clear();
+ return *this;
+}
+
+IOVector::block_type IOVector::clear() {
+ chain_length_ = 0;
+ begin_offset_ = 0;
+ start_index_ = 0;
+ block_type res;
+ if (!chain_.empty()) {
+ res = std::move(chain_.back());
+ }
+ chain_.clear();
+ return res;
+}
+
+void IOVector::drop_front(IOVector::size_type len) {
+ if (len == 0) {
+ return;
+ }
+ if (len == size()) {
+ clear();
+ return;
+ }
+ CHECK_LT(len, size());
+
+ auto dropped = 0u;
+ while (dropped < len) {
+ const auto next = chain_[start_index_].size() - begin_offset_;
+ if (dropped + next < len) {
+ pop_front_block();
+ dropped += next;
+ } else {
+ const auto taken = len - dropped;
+ begin_offset_ += taken;
+ break;
+ }
+ }
+}
+
+IOVector IOVector::take_front(IOVector::size_type len) {
+ if (len == 0) {
+ return {};
+ }
+ if (len == size()) {
+ return std::move(*this);
+ }
+
+ CHECK_GE(size(), len);
+ IOVector res;
+ // first iterate over the blocks that completely go into the other vector
+ while (chain_[start_index_].size() - begin_offset_ <= len) {
+ chain_length_ -= chain_[start_index_].size();
+ len -= chain_[start_index_].size() - begin_offset_;
+ if (chain_[start_index_].size() > begin_offset_) {
+ res.append(std::move(chain_[start_index_]));
+ if (begin_offset_) {
+ res.begin_offset_ = std::exchange(begin_offset_, 0);
+ }
+ } else {
+ begin_offset_ = 0;
+ }
+ ++start_index_;
+ }
+
+ if (len > 0) {
+ // what's left is a single buffer that needs to be split between the |res| and |this|
+ // we know that it has to be split - there was a check for the case when it has to
+ // go away as a whole.
+ if (begin_offset_ != 0 || len < chain_[start_index_].size() / 2) {
+ // let's memcpy the data out
+ block_type block(chain_[start_index_].begin() + begin_offset_,
+ chain_[start_index_].begin() + begin_offset_ + len);
+ res.append(std::move(block));
+ begin_offset_ += len;
+ } else {
+ CHECK_EQ(begin_offset_, 0u);
+ // move out the internal buffer out and copy only the tail of it back in
+ block_type block(chain_[start_index_].begin() + len, chain_[start_index_].end());
+ chain_length_ -= chain_[start_index_].size();
+ chain_[start_index_].resize(len);
+ res.append(std::move(chain_[start_index_]));
+ chain_length_ += block.size();
+ chain_[start_index_] = std::move(block);
+ }
+ }
+ return res;
+}
+
+void IOVector::trim_front() {
+ if ((begin_offset_ == 0 && start_index_ == 0) || chain_.empty()) {
+ return;
+ }
+ block_type& first_block = chain_[start_index_];
+ if (begin_offset_ == first_block.size()) {
+ ++start_index_;
+ } else {
+ memmove(first_block.data(), first_block.data() + begin_offset_,
+ first_block.size() - begin_offset_);
+ first_block.resize(first_block.size() - begin_offset_);
+ }
+ chain_length_ -= begin_offset_;
+ begin_offset_ = 0;
+ trim_chain_front();
+}
+
+void IOVector::trim_chain_front() {
+ if (start_index_) {
+ chain_.erase(chain_.begin(), chain_.begin() + start_index_);
+ start_index_ = 0;
+ }
+}
+
+void IOVector::pop_front_block() {
+ chain_length_ -= chain_[start_index_].size();
+ begin_offset_ = 0;
+ chain_[start_index_].clear();
+ ++start_index_;
+ if (start_index_ > std::max<size_t>(4, chain_.size() / 2)) {
+ trim_chain_front();
+ }
+}
+
+IOVector::block_type IOVector::coalesce() && {
+ // Destructive coalesce() may optimize for several cases when it doesn't need to allocate
+ // new buffer, or even return one of the existing blocks as is. The only guarantee is that
+ // after this call the IOVector is in some valid state. Nothing is guaranteed about the
+ // specifics.
+ if (size() == 0) {
+ return {};
+ }
+ if (begin_offset_ == chain_[start_index_].size() && chain_.size() == start_index_ + 2) {
+ chain_length_ -= chain_.back().size();
+ auto res = std::move(chain_.back());
+ chain_.pop_back();
+ return res;
+ }
+ if (chain_.size() == start_index_ + 1) {
+ chain_length_ -= chain_.back().size();
+ auto res = std::move(chain_.back());
+ chain_.pop_back();
+ if (begin_offset_ != 0) {
+ memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+ res.resize(res.size() - begin_offset_);
+ begin_offset_ = 0;
+ }
+ return res;
+ }
+ if (auto& firstBuffer = chain_[start_index_]; firstBuffer.capacity() >= size()) {
+ auto res = std::move(chain_[start_index_]);
+ auto size = res.size();
+ chain_length_ -= size;
+ if (begin_offset_ != 0) {
+ memmove(res.data(), res.data() + begin_offset_, res.size() - begin_offset_);
+ size -= begin_offset_;
+ begin_offset_ = 0;
+ }
+ for (auto i = start_index_ + 1; i < chain_.size(); ++i) {
+ memcpy(res.data() + size, chain_[i].data(), chain_[i].size());
+ size += chain_[i].size();
+ }
+ res.resize(size);
+ ++start_index_;
+ return res;
+ }
+ return const_cast<const IOVector*>(this)->coalesce<>();
+}
+
+std::vector<adb_iovec> IOVector::iovecs() const {
+ std::vector<adb_iovec> result;
+ result.reserve(chain_.size() - start_index_);
+ iterate_blocks([&result](const char* data, size_t len) {
+ adb_iovec iov;
+ iov.iov_base = const_cast<char*>(data);
+ iov.iov_len = len;
+ result.emplace_back(iov);
+ });
+
+ return result;
+}
diff --git a/adb/types.h b/adb/types.h
index 8bd66be..6b00224 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -19,9 +19,7 @@
#include <string.h>
#include <algorithm>
-#include <deque>
#include <memory>
-#include <type_traits>
#include <utility>
#include <vector>
@@ -33,7 +31,7 @@
struct Block {
using iterator = char*;
- Block() {}
+ Block() = default;
explicit Block(size_t size) { allocate(size); }
@@ -43,24 +41,21 @@
}
Block(const Block& copy) = delete;
- Block(Block&& move) noexcept {
- std::swap(data_, move.data_);
- std::swap(capacity_, move.capacity_);
- std::swap(size_, move.size_);
- }
+ Block(Block&& move) noexcept
+ : data_(std::exchange(move.data_, nullptr)),
+ capacity_(std::exchange(move.capacity_, 0)),
+ size_(std::exchange(move.size_, 0)) {}
Block& operator=(const Block& copy) = delete;
Block& operator=(Block&& move) noexcept {
clear();
-
- std::swap(data_, move.data_);
- std::swap(capacity_, move.capacity_);
- std::swap(size_, move.size_);
-
+ data_ = std::exchange(move.data_, nullptr);
+ capacity_ = std::exchange(move.capacity_, 0);
+ size_ = std::exchange(move.size_, 0);
return *this;
}
- ~Block() { clear(); }
+ ~Block() = default;
void resize(size_t new_size) {
if (!data_) {
@@ -144,146 +139,63 @@
using block_type = Block;
using size_type = size_t;
- IOVector() {}
+ IOVector() = default;
- explicit IOVector(std::unique_ptr<block_type> block) {
- append(std::move(block));
- }
+ explicit IOVector(block_type&& block) { append(std::move(block)); }
IOVector(const IOVector& copy) = delete;
IOVector(IOVector&& move) noexcept : IOVector() { *this = std::move(move); }
IOVector& operator=(const IOVector& copy) = delete;
- IOVector& operator=(IOVector&& move) noexcept {
- chain_ = std::move(move.chain_);
- chain_length_ = move.chain_length_;
- begin_offset_ = move.begin_offset_;
- end_offset_ = move.end_offset_;
+ IOVector& operator=(IOVector&& move) noexcept;
- move.chain_.clear();
- move.chain_length_ = 0;
- move.begin_offset_ = 0;
- move.end_offset_ = 0;
-
- return *this;
- }
-
- size_type size() const { return chain_length_ - begin_offset_ - end_offset_; }
+ size_type size() const { return chain_length_ - begin_offset_; }
bool empty() const { return size() == 0; }
- void clear() {
- chain_length_ = 0;
- begin_offset_ = 0;
- end_offset_ = 0;
- chain_.clear();
- }
+ // Return the last block so the caller can still reuse its allocated capacity
+ // or it can be simply ignored.
+ block_type clear();
+
+ void drop_front(size_type len);
// Split the first |len| bytes out of this chain into its own.
- IOVector take_front(size_type len) {
- IOVector head;
-
- if (len == 0) {
- return head;
- }
- CHECK_GE(size(), len);
-
- std::shared_ptr<const block_type> first_block = chain_.front();
- CHECK_GE(first_block->size(), begin_offset_);
- head.append_shared(std::move(first_block));
- head.begin_offset_ = begin_offset_;
-
- while (head.size() < len) {
- pop_front_block();
- CHECK(!chain_.empty());
-
- head.append_shared(chain_.front());
- }
-
- if (head.size() == len) {
- // Head takes full ownership of the last block it took.
- head.end_offset_ = 0;
- begin_offset_ = 0;
- pop_front_block();
- } else {
- // Head takes partial ownership of the last block it took.
- size_t bytes_taken = head.size() - len;
- head.end_offset_ = bytes_taken;
- CHECK_GE(chain_.front()->size(), bytes_taken);
- begin_offset_ = chain_.front()->size() - bytes_taken;
- }
-
- return head;
- }
+ IOVector take_front(size_type len);
// Add a nonempty block to the chain.
- // The end of the chain must be a complete block (i.e. end_offset_ == 0).
- void append(std::unique_ptr<const block_type> block) {
- if (block->size() == 0) {
+ void append(block_type&& block) {
+ if (block.size() == 0) {
return;
}
-
- CHECK_EQ(0ULL, end_offset_);
- chain_length_ += block->size();
+ CHECK_NE(0ULL, block.size());
+ chain_length_ += block.size();
chain_.emplace_back(std::move(block));
}
- void append(block_type&& block) { append(std::make_unique<block_type>(std::move(block))); }
-
- void trim_front() {
- if (begin_offset_ == 0) {
- return;
- }
-
- const block_type* first_block = chain_.front().get();
- auto copy = std::make_unique<block_type>(first_block->size() - begin_offset_);
- memcpy(copy->data(), first_block->data() + begin_offset_, copy->size());
- chain_.front() = std::move(copy);
-
- chain_length_ -= begin_offset_;
- begin_offset_ = 0;
- }
+ void trim_front();
private:
- // append, except takes a shared_ptr.
- // Private to prevent exterior mutation of blocks.
- void append_shared(std::shared_ptr<const block_type> block) {
- CHECK_NE(0ULL, block->size());
- CHECK_EQ(0ULL, end_offset_);
- chain_length_ += block->size();
- chain_.emplace_back(std::move(block));
- }
+ void trim_chain_front();
// Drop the front block from the chain, and update chain_length_ appropriately.
- void pop_front_block() {
- chain_length_ -= chain_.front()->size();
- begin_offset_ = 0;
- chain_.pop_front();
- }
+ void pop_front_block();
// Iterate over the blocks with a callback with an operator()(const char*, size_t).
template <typename Fn>
void iterate_blocks(Fn&& callback) const {
- if (chain_.size() == 0) {
+ if (size() == 0) {
return;
}
- for (size_t i = 0; i < chain_.size(); ++i) {
- const std::shared_ptr<const block_type>& block = chain_.at(i);
- const char* begin = block->data();
- size_t length = block->size();
+ for (size_t i = start_index_; i < chain_.size(); ++i) {
+ const auto& block = chain_[i];
+ const char* begin = block.data();
+ size_t length = block.size();
- // Note that both of these conditions can be true if there's only one block.
- if (i == 0) {
- CHECK_GE(block->size(), begin_offset_);
+ if (i == start_index_) {
+ CHECK_GE(block.size(), begin_offset_);
begin += begin_offset_;
length -= begin_offset_;
}
-
- if (i == chain_.size() - 1) {
- CHECK_GE(length, end_offset_);
- length -= end_offset_;
- }
-
callback(begin, length);
}
}
@@ -291,7 +203,7 @@
public:
// Copy all of the blocks into a single block.
template <typename CollectionType = block_type>
- CollectionType coalesce() const {
+ CollectionType coalesce() const& {
CollectionType result;
if (size() == 0) {
return result;
@@ -308,12 +220,13 @@
return result;
}
+ block_type coalesce() &&;
+
template <typename FunctionType>
- auto coalesced(FunctionType&& f) const ->
- typename std::result_of<FunctionType(const char*, size_t)>::type {
- if (chain_.size() == 1) {
+ auto coalesced(FunctionType&& f) const {
+ if (chain_.size() == start_index_ + 1) {
// If we only have one block, we can use it directly.
- return f(chain_.front()->data() + begin_offset_, size());
+ return f(chain_[start_index_].data() + begin_offset_, size());
} else {
// Otherwise, copy to a single block.
auto data = coalesce();
@@ -322,23 +235,13 @@
}
// Get a list of iovecs that can be used to write out all of the blocks.
- std::vector<adb_iovec> iovecs() const {
- std::vector<adb_iovec> result;
- iterate_blocks([&result](const char* data, size_t len) {
- adb_iovec iov;
- iov.iov_base = const_cast<char*>(data);
- iov.iov_len = len;
- result.emplace_back(iov);
- });
-
- return result;
- }
+ std::vector<adb_iovec> iovecs() const;
private:
// Total length of all of the blocks in the chain.
size_t chain_length_ = 0;
size_t begin_offset_ = 0;
- size_t end_offset_ = 0;
- std::deque<std::shared_ptr<const block_type>> chain_;
+ size_t start_index_ = 0;
+ std::vector<block_type> chain_;
};
diff --git a/adb/types_test.cpp b/adb/types_test.cpp
index 1fbd2ca..2c99f95 100644
--- a/adb/types_test.cpp
+++ b/adb/types_test.cpp
@@ -19,21 +19,21 @@
#include <memory>
#include "types.h"
-static std::unique_ptr<IOVector::block_type> create_block(const std::string& string) {
- return std::make_unique<IOVector::block_type>(string.begin(), string.end());
+static IOVector::block_type create_block(const std::string& string) {
+ return IOVector::block_type(string.begin(), string.end());
}
-static std::unique_ptr<IOVector::block_type> create_block(char value, size_t len) {
- auto block = std::make_unique<IOVector::block_type>();
- block->resize(len);
- memset(&(*block)[0], value, len);
+static IOVector::block_type create_block(char value, size_t len) {
+ auto block = IOVector::block_type();
+ block.resize(len);
+ memset(&(block)[0], value, len);
return block;
}
template <typename T>
-static std::unique_ptr<IOVector::block_type> copy_block(T&& block) {
- auto copy = std::make_unique<IOVector::block_type>();
- copy->assign(block->begin(), block->end());
+static IOVector::block_type copy_block(const T& block) {
+ auto copy = IOVector::block_type();
+ copy.assign(block.begin(), block.end());
return copy;
}
@@ -50,7 +50,7 @@
bc.append(copy_block(block));
ASSERT_EQ(100ULL, bc.size());
auto coalesced = bc.coalesce();
- ASSERT_EQ(*block, coalesced);
+ ASSERT_EQ(block, coalesced);
}
TEST(IOVector, single_block_split) {
@@ -60,8 +60,8 @@
IOVector foo = bc.take_front(3);
ASSERT_EQ(3ULL, foo.size());
ASSERT_EQ(3ULL, bc.size());
- ASSERT_EQ(*create_block("foo"), foo.coalesce());
- ASSERT_EQ(*create_block("bar"), bc.coalesce());
+ ASSERT_EQ(create_block("foo"), foo.coalesce());
+ ASSERT_EQ(create_block("bar"), bc.coalesce());
}
TEST(IOVector, aligned_split) {
@@ -73,15 +73,15 @@
IOVector foo = bc.take_front(3);
ASSERT_EQ(3ULL, foo.size());
- ASSERT_EQ(*create_block("foo"), foo.coalesce());
+ ASSERT_EQ(create_block("foo"), foo.coalesce());
IOVector bar = bc.take_front(3);
ASSERT_EQ(3ULL, bar.size());
- ASSERT_EQ(*create_block("bar"), bar.coalesce());
+ ASSERT_EQ(create_block("bar"), bar.coalesce());
IOVector baz = bc.take_front(3);
ASSERT_EQ(3ULL, baz.size());
- ASSERT_EQ(*create_block("baz"), baz.coalesce());
+ ASSERT_EQ(create_block("baz"), baz.coalesce());
ASSERT_EQ(0ULL, bc.size());
}
@@ -97,23 +97,23 @@
// Aligned left, misaligned right, across multiple blocks.
IOVector foob = bc.take_front(4);
ASSERT_EQ(4ULL, foob.size());
- ASSERT_EQ(*create_block("foob"), foob.coalesce());
+ ASSERT_EQ(create_block("foob"), foob.coalesce());
// Misaligned left, misaligned right, in one block.
IOVector a = bc.take_front(1);
ASSERT_EQ(1ULL, a.size());
- ASSERT_EQ(*create_block("a"), a.coalesce());
+ ASSERT_EQ(create_block("a"), a.coalesce());
// Misaligned left, misaligned right, across two blocks.
IOVector rba = bc.take_front(3);
ASSERT_EQ(3ULL, rba.size());
- ASSERT_EQ(*create_block("rba"), rba.coalesce());
+ ASSERT_EQ(create_block("rba"), rba.coalesce());
// Misaligned left, misaligned right, across three blocks.
IOVector zquxquu = bc.take_front(7);
ASSERT_EQ(7ULL, zquxquu.size());
- ASSERT_EQ(*create_block("zquxquu"), zquxquu.coalesce());
+ ASSERT_EQ(create_block("zquxquu"), zquxquu.coalesce());
ASSERT_EQ(1ULL, bc.size());
- ASSERT_EQ(*create_block("x"), bc.coalesce());
+ ASSERT_EQ(create_block("x"), bc.coalesce());
}
diff --git a/base/Android.bp b/base/Android.bp
index 25ae535..aeb8864 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -58,6 +58,7 @@
"file.cpp",
"logging.cpp",
"mapped_file.cpp",
+ "parsebool.cpp",
"parsenetaddress.cpp",
"process.cpp",
"properties.cpp",
@@ -149,6 +150,7 @@
"macros_test.cpp",
"mapped_file_test.cpp",
"parsedouble_test.cpp",
+ "parsebool_test.cpp",
"parseint_test.cpp",
"parsenetaddress_test.cpp",
"process_test.cpp",
diff --git a/base/include/android-base/parsebool.h b/base/include/android-base/parsebool.h
new file mode 100644
index 0000000..b2bd021
--- /dev/null
+++ b/base/include/android-base/parsebool.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_view>
+
+namespace android {
+namespace base {
+
+// Parse the given string as yes or no inactivation of some sort. Return one of the
+// ParseBoolResult enumeration values.
+//
+// The following values parse as true:
+//
+// 1
+// on
+// true
+// y
+// yes
+//
+//
+// The following values parse as false:
+//
+// 0
+// false
+// n
+// no
+// off
+//
+// Anything else is a parse error.
+//
+// The purpose of this function is to have a single canonical parser for yes-or-no indications
+// throughout the system.
+
+enum class ParseBoolResult {
+ kError,
+ kFalse,
+ kTrue,
+};
+
+ParseBoolResult ParseBool(std::string_view s);
+
+} // namespace base
+} // namespace android
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index b1c22c9..14d534a 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -85,5 +85,10 @@
return true;
}
+// Replaces `from` with `to` in `s`, once if `all == false`, or as many times as
+// there are matches if `all == true`.
+[[nodiscard]] std::string StringReplace(std::string_view s, std::string_view from,
+ std::string_view to, bool all);
+
} // namespace base
} // namespace android
diff --git a/base/parsebool.cpp b/base/parsebool.cpp
new file mode 100644
index 0000000..ff96fe9
--- /dev/null
+++ b/base/parsebool.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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/parsebool.h"
+#include <errno.h>
+
+namespace android {
+namespace base {
+
+ParseBoolResult ParseBool(std::string_view s) {
+ if (s == "1" || s == "y" || s == "yes" || s == "on" || s == "true") {
+ return ParseBoolResult::kTrue;
+ }
+ if (s == "0" || s == "n" || s == "no" || s == "off" || s == "false") {
+ return ParseBoolResult::kFalse;
+ }
+ return ParseBoolResult::kError;
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/parsebool_test.cpp b/base/parsebool_test.cpp
new file mode 100644
index 0000000..a081994
--- /dev/null
+++ b/base/parsebool_test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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/parsebool.h"
+
+#include <errno.h>
+
+#include <gtest/gtest.h>
+#include <string_view>
+
+using android::base::ParseBool;
+using android::base::ParseBoolResult;
+
+TEST(parsebool, true_) {
+ static const char* yes[] = {
+ "1", "on", "true", "y", "yes",
+ };
+ for (const char* s : yes) {
+ ASSERT_EQ(ParseBoolResult::kTrue, ParseBool(s));
+ }
+}
+
+TEST(parsebool, false_) {
+ static const char* no[] = {
+ "0", "false", "n", "no", "off",
+ };
+ for (const char* s : no) {
+ ASSERT_EQ(ParseBoolResult::kFalse, ParseBool(s));
+ }
+}
+
+TEST(parsebool, invalid) {
+ ASSERT_EQ(ParseBoolResult::kError, ParseBool("blarg"));
+ ASSERT_EQ(ParseBoolResult::kError, ParseBool(""));
+}
diff --git a/base/properties.cpp b/base/properties.cpp
index d5a5918..4731bf2 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -28,19 +28,22 @@
#include <map>
#include <string>
+#include <android-base/parsebool.h>
#include <android-base/parseint.h>
namespace android {
namespace base {
bool GetBoolProperty(const std::string& key, bool default_value) {
- std::string value = GetProperty(key, "");
- if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") {
- return true;
- } else if (value == "0" || value == "n" || value == "no" || value == "off" || value == "false") {
- return false;
+ switch (ParseBool(GetProperty(key, ""))) {
+ case ParseBoolResult::kError:
+ return default_value;
+ case ParseBoolResult::kFalse:
+ return false;
+ case ParseBoolResult::kTrue:
+ return true;
}
- return default_value;
+ __builtin_unreachable();
}
template <typename T>
diff --git a/base/strings.cpp b/base/strings.cpp
index bb3167e..40b2bf2 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -116,5 +116,24 @@
return lhs.size() == rhs.size() && strncasecmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
+std::string StringReplace(std::string_view s, std::string_view from, std::string_view to,
+ bool all) {
+ if (from.empty()) return std::string(s);
+
+ std::string result;
+ std::string_view::size_type start_pos = 0;
+ do {
+ std::string_view::size_type pos = s.find(from, start_pos);
+ if (pos == std::string_view::npos) break;
+
+ result.append(s.data() + start_pos, pos - start_pos);
+ result.append(to.data(), to.size());
+
+ start_pos = pos + from.size();
+ } while (all);
+ result.append(s.data() + start_pos, s.size() - start_pos);
+ return result;
+}
+
} // namespace base
} // namespace android
diff --git a/base/strings_test.cpp b/base/strings_test.cpp
index ca3c0b8..5ae3094 100644
--- a/base/strings_test.cpp
+++ b/base/strings_test.cpp
@@ -311,3 +311,46 @@
ASSERT_TRUE(android::base::ConsumeSuffix(&s, ".bar"));
ASSERT_EQ("foo", s);
}
+
+TEST(strings, StringReplace_false) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", false));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", false));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", false));
+
+ // Equal lengths.
+ ASSERT_EQ("Abcabc", android::base::StringReplace("abcabc", "a", "A", false));
+ ASSERT_EQ("aBcabc", android::base::StringReplace("abcabc", "b", "B", false));
+ ASSERT_EQ("abCabc", android::base::StringReplace("abcabc", "c", "C", false));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcabc", android::base::StringReplace("abcabc", "a", "foo", false));
+ ASSERT_EQ("afoocabc", android::base::StringReplace("abcabc", "b", "foo", false));
+ ASSERT_EQ("abfooabc", android::base::StringReplace("abcabc", "c", "foo", false));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyz", android::base::StringReplace("abcxyz", "abc", "x", false));
+ ASSERT_EQ("axyz", android::base::StringReplace("abcxyz", "bcx", "x", false));
+ ASSERT_EQ("abcx", android::base::StringReplace("abcxyz", "xyz", "x", false));
+}
+
+TEST(strings, StringReplace_true) {
+ // No change.
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "z", "Z", true));
+ ASSERT_EQ("", android::base::StringReplace("", "z", "Z", true));
+ ASSERT_EQ("abcabc", android::base::StringReplace("abcabc", "", "Z", true));
+
+ // Equal lengths.
+ ASSERT_EQ("AbcAbc", android::base::StringReplace("abcabc", "a", "A", true));
+ ASSERT_EQ("aBcaBc", android::base::StringReplace("abcabc", "b", "B", true));
+ ASSERT_EQ("abCabC", android::base::StringReplace("abcabc", "c", "C", true));
+
+ // Longer replacement.
+ ASSERT_EQ("foobcfoobc", android::base::StringReplace("abcabc", "a", "foo", true));
+ ASSERT_EQ("afoocafooc", android::base::StringReplace("abcabc", "b", "foo", true));
+ ASSERT_EQ("abfooabfoo", android::base::StringReplace("abcabc", "c", "foo", true));
+
+ // Shorter replacement.
+ ASSERT_EQ("xxyzx", android::base::StringReplace("abcxyzabc", "abc", "x", true));
+ ASSERT_EQ("<xx>", android::base::StringReplace("<abcabc>", "abc", "x", true));
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index f189c45..75bac87 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -20,6 +20,7 @@
#include <inttypes.h>
#include <signal.h>
+#include <stdarg.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -71,6 +72,7 @@
// Log information onto the tombstone.
void _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));
+void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
namespace unwindstack {
class Unwinder;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 1993840..236fcf7 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -428,7 +428,7 @@
std::vector<std::pair<std::string, uint64_t>> special_row;
#if defined(__arm__) || defined(__aarch64__)
- static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc"};
+ static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc", "pst"};
#elif defined(__i386__)
static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
#elif defined(__x86_64__)
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 9b2779a..5ce26fc 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -67,6 +67,14 @@
__attribute__((__weak__, visibility("default")))
void _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ _VLOG(log, ltype, fmt, ap);
+ va_end(ap);
+}
+
+__attribute__((__weak__, visibility("default")))
+void _VLOG(log_t* log, enum logtype ltype, const char* fmt, va_list ap) {
bool write_to_tombstone = (log->tfd != -1);
bool write_to_logcat = is_allowed_in_logcat(ltype)
&& log->crashed_tid != -1
@@ -75,10 +83,7 @@
static bool write_to_kmsg = should_write_to_kmsg();
std::string msg;
- va_list ap;
- va_start(ap, fmt);
android::base::StringAppendV(&msg, fmt, ap);
- va_end(ap);
if (msg.empty()) return;
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 02a887e..a757d56 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -137,12 +137,14 @@
"libhidlbase",
"liblog",
"liblp",
+ "libprotobuf-cpp-lite",
"libsparse",
"libutils",
],
static_libs: [
"libhealthhalutils",
+ "libsnapshot_nobinder",
],
header_libs: [
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index a2c95d6..1a745ab 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -19,6 +19,8 @@
#include <sys/socket.h>
#include <sys/un.h>
+#include <unordered_set>
+
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
@@ -33,6 +35,7 @@
#include <libgsi/libgsi.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
+#include <libsnapshot/snapshot.h>
#include <uuid/uuid.h>
#include "constants.h"
@@ -48,6 +51,7 @@
using ::android::hardware::boot::V1_1::MergeStatus;
using ::android::hardware::fastboot::V1_0::Result;
using ::android::hardware::fastboot::V1_0::Status;
+using android::snapshot::SnapshotManager;
using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
struct VariableHandlers {
@@ -57,6 +61,24 @@
std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
};
+static bool IsSnapshotUpdateInProgress(FastbootDevice* device) {
+ auto hal = device->boot1_1();
+ if (!hal) {
+ return false;
+ }
+ auto merge_status = hal->getSnapshotMergeStatus();
+ return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+}
+
+static bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {
+ static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {
+ "userdata", "metadata", "misc"};
+ if (ProtectedPartitionsDuringMerge.count(name) == 0) {
+ return false;
+ }
+ return IsSnapshotUpdateInProgress(device);
+}
+
static void GetAllVars(FastbootDevice* device, const std::string& name,
const VariableHandlers& handlers) {
if (!handlers.get_all_args) {
@@ -143,8 +165,14 @@
return device->WriteStatus(FastbootResult::FAIL, "Erase is not allowed on locked devices");
}
+ const auto& partition_name = args[1];
+ if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+ auto message = "Cannot erase " + partition_name + " while a snapshot update is in progress";
+ return device->WriteFail(message);
+ }
+
PartitionHandle handle;
- if (!OpenPartition(device, args[1], &handle)) {
+ if (!OpenPartition(device, partition_name, &handle)) {
return device->WriteStatus(FastbootResult::FAIL, "Partition doesn't exist");
}
if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {
@@ -209,9 +237,9 @@
"set_active command is not allowed on locked devices");
}
- // Slot suffix needs to be between 'a' and 'z'.
Slot slot;
if (!GetSlotNumber(args[1], &slot)) {
+ // Slot suffix needs to be between 'a' and 'z'.
return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
}
@@ -224,6 +252,32 @@
if (slot >= boot_control_hal->getNumberSlots()) {
return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
}
+
+ // If the slot is not changing, do nothing.
+ if (slot == boot_control_hal->getCurrentSlot()) {
+ return device->WriteOkay("");
+ }
+
+ // Check how to handle the current snapshot state.
+ if (auto hal11 = device->boot1_1()) {
+ auto merge_status = hal11->getSnapshotMergeStatus();
+ if (merge_status == MergeStatus::MERGING) {
+ return device->WriteFail("Cannot change slots while a snapshot update is in progress");
+ }
+ // Note: we allow the slot change if the state is SNAPSHOTTED. First-
+ // stage init does not have access to the HAL, and uses the slot number
+ // and /metadata OTA state to determine whether a slot change occurred.
+ // Booting into the old slot would erase the OTA, and switching A->B->A
+ // would simply resume it if no boots occur in between. Re-flashing
+ // partitions implicitly cancels the OTA, so leaving the state as-is is
+ // safe.
+ if (merge_status == MergeStatus::SNAPSHOTTED) {
+ device->WriteInfo(
+ "Changing the active slot with a snapshot applied may cancel the"
+ " update.");
+ }
+ }
+
CommandResult ret;
auto cb = [&ret](CommandResult result) { ret = result; };
auto result = boot_control_hal->setActiveBootSlot(slot, cb);
@@ -467,6 +521,11 @@
}
const auto& partition_name = args[1];
+ if (IsProtectedPartitionDuringMerge(device, partition_name)) {
+ auto message = "Cannot flash " + partition_name + " while a snapshot update is in progress";
+ return device->WriteFail(message);
+ }
+
if (LogicalPartitionExists(device, partition_name)) {
CancelPartitionSnapshot(device, partition_name);
}
@@ -556,12 +615,9 @@
bool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
- auto hal = device->boot_control_hal();
+ auto hal = device->boot1_1();
if (!hal) return device->WriteFail("Not supported");
- android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
- if (!hal11) return device->WriteFail("Not supported");
-
// If no arguments, return the same thing as a getvar. Note that we get the
// HAL first so we can return "not supported" before we return the less
// specific error message below.
@@ -574,18 +630,34 @@
return device->WriteOkay("");
}
- if (args.size() != 2 || args[1] != "cancel") {
+ MergeStatus status = hal->getSnapshotMergeStatus();
+
+ if (args.size() != 2) {
return device->WriteFail("Invalid arguments");
}
+ if (args[1] == "cancel") {
+ switch (status) {
+ case MergeStatus::SNAPSHOTTED:
+ case MergeStatus::MERGING:
+ hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+ break;
+ default:
+ break;
+ }
+ } else if (args[1] == "merge") {
+ if (status != MergeStatus::MERGING) {
+ return device->WriteFail("No snapshot merge is in progress");
+ }
- MergeStatus status = hal11->getSnapshotMergeStatus();
- switch (status) {
- case MergeStatus::SNAPSHOTTED:
- case MergeStatus::MERGING:
- hal11->setSnapshotMergeStatus(MergeStatus::CANCELLED);
- break;
- default:
- break;
+ auto sm = SnapshotManager::NewForFirstStageMount();
+ if (!sm) {
+ return device->WriteFail("Unable to create SnapshotManager");
+ }
+ if (!sm->HandleImminentDataWipe()) {
+ return device->WriteFail("Unable to finish snapshot merge");
+ }
+ } else {
+ return device->WriteFail("Invalid parameter to snapshot-update");
}
return device->WriteStatus(FastbootResult::OKAY, "Success");
}
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index d3c2bda..31fc359 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -60,7 +60,11 @@
boot_control_hal_(IBootControl::getService()),
health_hal_(get_health_service()),
fastboot_hal_(IFastboot::getService()),
- active_slot_("") {}
+ active_slot_("") {
+ if (boot_control_hal_) {
+ boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
+ }
+}
FastbootDevice::~FastbootDevice() {
CloseDevice();
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 091aadf..bbe8172 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -23,6 +23,7 @@
#include <vector>
#include <android/hardware/boot/1.0/IBootControl.h>
+#include <android/hardware/boot/1.1/IBootControl.h>
#include <android/hardware/fastboot/1.0/IFastboot.h>
#include <android/hardware/health/2.0/IHealth.h>
@@ -51,6 +52,7 @@
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
return boot_control_hal_;
}
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal() {
return fastboot_hal_;
}
@@ -63,6 +65,7 @@
std::unique_ptr<Transport> transport_;
android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
+ android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
android::sp<android::hardware::fastboot::V1_0::IFastboot> fastboot_hal_;
std::vector<char> download_data_;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 717db06..10eac01 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -432,19 +432,13 @@
std::string* message) {
// Note that we use the HAL rather than mounting /metadata, since we want
// our results to match the bootloader.
- auto hal = device->boot_control_hal();
+ auto hal = device->boot1_1();
if (!hal) {
*message = "not supported";
return false;
}
- android::sp<IBootControl1_1> hal11 = IBootControl1_1::castFrom(hal);
- if (!hal11) {
- *message = "not supported";
- return false;
- }
-
- MergeStatus status = hal11->getSnapshotMergeStatus();
+ MergeStatus status = hal->getSnapshotMergeStatus();
switch (status) {
case MergeStatus::SNAPSHOTTED:
*message = "snapshotted";
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7ce7c7c..cbd42b1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -399,6 +399,9 @@
" snapshot-update cancel On devices that support snapshot-based updates, cancel\n"
" an in-progress update. This may make the device\n"
" unbootable until it is reflashed.\n"
+ " snapshot-update merge On devices that support snapshot-based updates, finish\n"
+ " an in-progress update if it is in the \"merging\"\n"
+ " phase.\n"
"\n"
"boot image:\n"
" boot KERNEL [RAMDISK [SECOND]]\n"
@@ -2089,8 +2092,8 @@
if (!args.empty()) {
arg = next_arg(&args);
}
- if (!arg.empty() && arg != "cancel") {
- syntax_error("expected: snapshot-update [cancel]");
+ if (!arg.empty() && (arg != "cancel" && arg != "merge")) {
+ syntax_error("expected: snapshot-update [cancel|merge]");
}
fb->SnapshotUpdateCommand(arg);
} else {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 6a5ad20..8d534ea 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -124,8 +124,11 @@
RetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,
std::vector<std::string>* info) {
+ prolog_(StringPrintf("Snapshot %s", command.c_str()));
std::string raw = FB_CMD_SNAPSHOT_UPDATE ":" + command;
- return RawCommand(raw, response, info);
+ auto result = RawCommand(raw, response, info);
+ epilog_(result);
+ return result;
}
RetCode FastBootDriver::FlashPartition(const std::string& partition,
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 75ebd94..cb69037 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1351,38 +1351,9 @@
return ret;
}
-static std::string GetUserdataBlockDevice() {
- Fstab fstab;
- if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
- LERROR << "Failed to read /proc/mounts";
- return "";
- }
- auto entry = GetEntryForMountPoint(&fstab, "/data");
- if (entry == nullptr) {
- LERROR << "Didn't find /data mount point in /proc/mounts";
- return "";
- }
- return entry->blk_device;
-}
-
int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
- const std::string& block_device = GetUserdataBlockDevice();
- LINFO << "Userdata is mounted on " << block_device;
- auto entry = std::find_if(fstab->begin(), fstab->end(), [&block_device](const FstabEntry& e) {
- if (e.mount_point != "/data") {
- return false;
- }
- if (e.blk_device == block_device) {
- return true;
- }
- DeviceMapper& dm = DeviceMapper::Instance();
- std::string path;
- if (!dm.GetDmDevicePathByName("userdata", &path)) {
- return false;
- }
- return path == block_device;
- });
- if (entry == fstab->end()) {
+ auto entry = GetMountedEntryForUserdata(fstab);
+ if (entry == nullptr) {
LERROR << "Can't find /data in fstab";
return -1;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d216458..c81a079 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -40,6 +40,7 @@
using android::base::ParseByteCount;
using android::base::ParseInt;
using android::base::ReadFileToString;
+using android::base::Readlink;
using android::base::Split;
using android::base::StartsWith;
@@ -809,6 +810,89 @@
return entries;
}
+static std::string ResolveBlockDevice(const std::string& block_device) {
+ if (!StartsWith(block_device, "/dev/block/")) {
+ LWARNING << block_device << " is not a block device";
+ return block_device;
+ }
+ std::string name = block_device.substr(5);
+ if (!StartsWith(name, "block/dm-")) {
+ // Not a dm-device, but might be a symlink. Optimistically try to readlink.
+ std::string result;
+ if (Readlink(block_device, &result)) {
+ return result;
+ } else if (errno == EINVAL) {
+ // After all, it wasn't a symlink.
+ return block_device;
+ } else {
+ LERROR << "Failed to readlink " << block_device;
+ return "";
+ }
+ }
+ // It's a dm-device, let's find what's inside!
+ std::string sys_dir = "/sys/" + name;
+ while (true) {
+ std::string slaves_dir = sys_dir + "/slaves";
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(slaves_dir.c_str()), closedir);
+ if (!dir) {
+ LERROR << "Failed to open " << slaves_dir;
+ return "";
+ }
+ std::string sub_device_name = "";
+ for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
+ if (entry->d_type != DT_LNK) continue;
+ if (!sub_device_name.empty()) {
+ LERROR << "Too many slaves in " << slaves_dir;
+ return "";
+ }
+ sub_device_name = entry->d_name;
+ }
+ if (sub_device_name.empty()) {
+ LERROR << "No slaves in " << slaves_dir;
+ return "";
+ }
+ if (!StartsWith(sub_device_name, "dm-")) {
+ // Not a dm-device! We can stop now.
+ return "/dev/block/" + sub_device_name;
+ }
+ // Still a dm-device, keep digging.
+ sys_dir = "/sys/block/" + sub_device_name;
+ }
+}
+
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab) {
+ Fstab mounts;
+ if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
+ LERROR << "Failed to read /proc/mounts";
+ return nullptr;
+ }
+ auto mounted_entry = GetEntryForMountPoint(&mounts, "/data");
+ if (mounted_entry == nullptr) {
+ LWARNING << "/data is not mounted";
+ return nullptr;
+ }
+ std::string resolved_block_device = ResolveBlockDevice(mounted_entry->blk_device);
+ if (resolved_block_device.empty()) {
+ return nullptr;
+ }
+ LINFO << "/data is mounted on " << resolved_block_device;
+ for (auto& entry : *fstab) {
+ if (entry.mount_point != "/data") {
+ continue;
+ }
+ std::string block_device;
+ if (!Readlink(entry.blk_device, &block_device)) {
+ LWARNING << "Failed to readlink " << entry.blk_device;
+ block_device = entry.blk_device;
+ }
+ if (block_device == resolved_block_device) {
+ return &entry;
+ }
+ }
+ LERROR << "Didn't find entry that was used to mount /data";
+ return nullptr;
+}
+
std::set<std::string> GetBootDevices() {
// First check the kernel commandline, then try the device tree otherwise
std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c6a16e3..80deaef 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -102,6 +102,7 @@
FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
// The Fstab can contain multiple entries for the same mount point with different configurations.
std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
+FstabEntry* GetMountedEntryForUserdata(Fstab* fstab);
// This method builds DSU fstab entries and transfer the fstab.
//
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 6b842b3..e4d92ca 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -110,7 +110,7 @@
return ReadFromImageFile(fd);
}
-bool WriteToImageFile(int fd, const LpMetadata& input) {
+bool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {
std::string geometry = SerializeGeometry(input.geometry);
std::string metadata = SerializeMetadata(input);
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a284d2e..88e5882 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -29,8 +29,6 @@
// Helper function to serialize geometry and metadata to a normal file, for
// flashing or debugging.
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
-bool WriteToImageFile(const char* file, const LpMetadata& metadata);
-bool WriteToImageFile(int fd, const LpMetadata& metadata);
// We use an object to build the image file since it requires that data
// pointers be held alive until the sparse file is destroyed. It's easier
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index cd860cd..04f8987 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -76,12 +76,15 @@
// supported). It is a format specifically for storing only metadata.
bool IsEmptySuperImage(const std::string& file);
-// Read/Write logical partition metadata to an image file, for diagnostics or
-// flashing. If no partition images are specified, the file will be in the
-// empty format.
+// Read/Write logical partition metadata and contents to an image file, for
+// flashing.
bool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,
const std::map<std::string, std::string>& images, bool sparsify);
+
+// Read/Write logical partition metadata to an image file, for producing a
+// super_empty.img (for fastboot wipe-super/update-super) or for diagnostics.
bool WriteToImageFile(const std::string& file, const LpMetadata& metadata);
+bool WriteToImageFile(android::base::borrowed_fd fd, const LpMetadata& metadata);
std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 8a983ad..bb24069 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -83,8 +83,9 @@
// Perform sanity checks so we don't accidentally overwrite valid metadata
// with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
- const std::string& slot_suffix, std::string* blob) {
+static bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,
+ const LpMetadata& metadata, const std::string& slot_suffix,
+ std::string* blob) {
const LpMetadataGeometry& geometry = metadata.geometry;
*blob = SerializeMetadata(metadata);
@@ -128,6 +129,10 @@
<< block_device.first_logical_sector << " for size " << block_device.size;
return false;
}
+
+ // When flashing on the device, check partition sizes. Don't do this on
+ // the host since there is no way to verify.
+#if defined(__ANDROID__)
BlockDeviceInfo info;
if (!opener.GetInfo(partition_name, &info)) {
PERROR << partition_name << ": ioctl";
@@ -138,6 +143,7 @@
<< block_device.size << ", got " << info.size << ")";
return false;
}
+#endif
}
// Make sure all partition entries reference valid extents.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8f24709..7411e5a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -224,7 +224,8 @@
bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
// This method should be called preceding any wipe or flash of metadata or
- // userdata. It is only valid in recovery.
+ // userdata. It is only valid in recovery or fastbootd, and it ensures that
+ // a merge has been completed.
//
// When userdata will be wiped or flashed, it is necessary to clean up any
// snapshot state. If a merge is in progress, the merge must be finished.
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 1de7008..0fb4af9 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -300,7 +300,7 @@
if (!ReadSnapshotStatus(lock, name, &status)) {
return false;
}
- if (status.state() == SnapshotState::MERGE_COMPLETED) {
+ if (status.state() == SnapshotState::NONE || status.state() == SnapshotState::MERGE_COMPLETED) {
LOG(ERROR) << "Should not create a snapshot device for " << name
<< " after merging has completed.";
return false;
@@ -1376,6 +1376,17 @@
if (live_snapshot_status->state() == SnapshotState::MERGE_COMPLETED) {
live_snapshot_status.reset();
}
+
+ if (live_snapshot_status->state() == SnapshotState::NONE ||
+ live_snapshot_status->cow_partition_size() + live_snapshot_status->cow_file_size() ==
+ 0) {
+ LOG(WARNING) << "Snapshot status for " << params.GetPartitionName()
+ << " is invalid, ignoring: state = "
+ << SnapshotState_Name(live_snapshot_status->state())
+ << ", cow_partition_size = " << live_snapshot_status->cow_partition_size()
+ << ", cow_file_size = " << live_snapshot_status->cow_file_size();
+ live_snapshot_status.reset();
+ }
} while (0);
if (live_snapshot_status.has_value()) {
@@ -1663,10 +1674,6 @@
if (contents.empty()) return false;
if (!Truncate(file)) return false;
- if (!android::base::WriteStringToFd(contents, file->fd())) {
- PLOG(ERROR) << "Could not write to state file";
- return false;
- }
#ifdef LIBSNAPSHOT_USE_HAL
auto merge_status = MergeStatus::UNKNOWN;
@@ -1692,7 +1699,21 @@
LOG(ERROR) << "Unexpected update status: " << state;
break;
}
- if (!device_->SetBootControlMergeStatus(merge_status)) {
+
+ bool set_before_write =
+ merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;
+ if (set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {
+ return false;
+ }
+#endif
+
+ if (!android::base::WriteStringToFd(contents, file->fd())) {
+ PLOG(ERROR) << "Could not write to state file";
+ return false;
+ }
+
+#ifdef LIBSNAPSHOT_USE_HAL
+ if (!set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {
return false;
}
#endif
@@ -2150,6 +2171,15 @@
}
UpdateState SnapshotManager::InitiateMergeAndWait() {
+ {
+ auto lock = LockExclusive();
+ // Sync update state from file with bootloader.
+ if (!WriteUpdateState(lock.get(), ReadUpdateState(lock.get()))) {
+ LOG(WARNING) << "Unable to sync write update state, fastboot may "
+ << "reject / accept wipes incorrectly!";
+ }
+ }
+
LOG(INFO) << "Waiting for any previous merge request to complete. "
<< "This can take up to several minutes.";
auto state = ProcessUpdateState();
@@ -2185,6 +2215,15 @@
return true;
}
+ // Check this early, so we don't accidentally start trying to populate
+ // the state file in recovery. Note we don't call GetUpdateState since
+ // we want errors in acquiring the lock to be propagated, instead of
+ // returning UpdateState::None.
+ auto state_file = GetStateFilePath();
+ if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {
+ return true;
+ }
+
auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
auto super_path = device_->GetSuperDevice(slot_number);
if (!CreateLogicalAndSnapshotPartitions(super_path)) {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 8783526..7d6e78f 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -71,6 +71,8 @@
TestDeviceInfo* test_device = nullptr;
std::string fake_super;
+void MountMetadata();
+
class SnapshotTest : public ::testing::Test {
public:
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
@@ -87,7 +89,7 @@
InitializeState();
CleanupTestArtifacts();
FormatFakeSuper();
-
+ MountMetadata();
ASSERT_TRUE(sm->BeginUpdate());
}
@@ -575,7 +577,7 @@
ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));
- ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
+ ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);
ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));
ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);
@@ -662,6 +664,7 @@
if (!image_manager_) {
InitializeState();
}
+ MountMetadata();
for (const auto& suffix : {"_a", "_b"}) {
test_device->set_slot_suffix(suffix);
EXPECT_TRUE(sm->CancelUpdate()) << suffix;
@@ -1206,6 +1209,10 @@
Fstab fstab_;
};
+void MountMetadata() {
+ MetadataMountedTest().TearDown();
+}
+
TEST_F(MetadataMountedTest, Android) {
auto device = sm->EnsureMetadataMounted();
EXPECT_NE(nullptr, device);
@@ -1277,6 +1284,8 @@
auto new_sm = SnapshotManager::NewForFirstStageMount(test_device);
ASSERT_TRUE(new_sm->HandleImminentDataWipe());
+ // Manually mount metadata so that we can call GetUpdateState() below.
+ MountMetadata();
EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
EXPECT_TRUE(test_device->IsSlotUnbootable(1));
EXPECT_FALSE(test_device->IsSlotUnbootable(0));
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1cbaf45..c5adea6 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -969,3 +969,14 @@
ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, "/data"))
<< "Default fstab doesn't contain /data entry";
}
+
+TEST(fs_mgr, UserdataMountedFromDefaultFstab) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Must be run as root.";
+ return;
+ }
+ Fstab fstab;
+ ASSERT_TRUE(ReadDefaultFstab(&fstab)) << "Failed to read default fstab";
+ ASSERT_NE(nullptr, GetMountedEntryForUserdata(&fstab))
+ << "/data wasn't mounted from default fstab";
+}
diff --git a/init/Android.bp b/init/Android.bp
index c7021c3..9529617 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -152,6 +152,7 @@
whole_static_libs: [
"libcap",
"com.android.sysprop.apex",
+ "com.android.sysprop.init",
],
header_libs: ["bootimg_headers"],
proto: {
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8f58145..485806b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -45,6 +45,7 @@
#include <memory>
#include <ApexProperties.sysprop.h>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -1222,7 +1223,9 @@
boot_clock::time_point now = boot_clock::now();
property_set("sys.init.userspace_reboot.last_finished",
std::to_string(now.time_since_epoch().count()));
- property_set(kUserspaceRebootInProgress, "0");
+ if (!android::sysprop::InitProperties::userspace_reboot_in_progress(false)) {
+ return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
+ }
return {};
}
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 7040f26..13cebcc 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -38,6 +38,7 @@
#include <thread>
#include <vector>
+#include <InitProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
@@ -743,8 +744,8 @@
// actions. We should make sure, that all of them are propagated before
// proceeding with userspace reboot. Synchronously setting kUserspaceRebootInProgress property
// is not perfect, but it should do the trick.
- if (property_set(kUserspaceRebootInProgress, "1") != 0) {
- return Error() << "Failed to set property " << kUserspaceRebootInProgress;
+ if (!android::sysprop::InitProperties::userspace_reboot_in_progress(true)) {
+ return Error() << "Failed to set sys.init.userspace_reboot.in_progress property";
}
EnterShutdown();
std::vector<Service*> stop_first;
@@ -760,6 +761,12 @@
were_enabled.push_back(s);
}
}
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() before terminating services...";
+ sync();
+ LOG(INFO) << "sync() took " << sync_timer;
+ }
// TODO(b/135984674): do we need shutdown animation for userspace reboot?
// TODO(b/135984674): control userspace timeout via read-only property?
StopServicesAndLogViolations(stop_first, 10s, true /* SIGTERM */);
@@ -775,6 +782,12 @@
// TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() after stopping services...";
+ sync();
+ LOG(INFO) << "sync() took " << sync_timer;
+ }
if (auto result = UnmountAllApexes(); !result) {
return result;
}
@@ -792,7 +805,38 @@
return {};
}
+static void UserspaceRebootWatchdogThread() {
+ if (!WaitForProperty("sys.init.userspace_reboot_in_progress", "1", 20s)) {
+ // TODO(b/135984674): should we reboot instead?
+ LOG(WARNING) << "Userspace reboot didn't start in 20 seconds. Stopping watchdog";
+ return;
+ }
+ LOG(INFO) << "Starting userspace reboot watchdog";
+ // TODO(b/135984674): this should be configured via a read-only sysprop.
+ std::chrono::milliseconds timeout = 60s;
+ if (!WaitForProperty("sys.boot_completed", "1", timeout)) {
+ LOG(ERROR) << "Failed to boot in " << timeout.count() << "ms. Switching to full reboot";
+ // In this case device is in a boot loop. Only way to recover is to do dirty reboot.
+ RebootSystem(ANDROID_RB_RESTART2, "userspace-reboot-watchdog-triggered");
+ }
+ LOG(INFO) << "Device booted, stopping userspace reboot watchdog";
+}
+
static void HandleUserspaceReboot() {
+ // Spinnig up a separate thread will fail the setns call later in the boot sequence.
+ // Fork a new process to monitor userspace reboot while we are investigating a better solution.
+ pid_t pid = fork();
+ if (pid < 0) {
+ PLOG(ERROR) << "Failed to fork process for userspace reboot watchdog. Switching to full "
+ << "reboot";
+ trigger_shutdown("reboot,userspace-reboot-failed-to-fork");
+ return;
+ }
+ if (pid == 0) {
+ // Child
+ UserspaceRebootWatchdogThread();
+ _exit(EXIT_SUCCESS);
+ }
LOG(INFO) << "Clearing queue and starting userspace-reboot-requested trigger";
auto& am = ActionManager::GetInstance();
am.ClearQueue();
diff --git a/init/reboot.h b/init/reboot.h
index cdfa024..81c3edc 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -22,8 +22,6 @@
namespace android {
namespace init {
-static const constexpr char* kUserspaceRebootInProgress = "sys.init.userspace_reboot.in_progress";
-
// Parses and handles a setprop sys.powerctl message.
void HandlePowerctlMessage(const std::string& command);
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index e55265b..bebcc77 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -209,8 +209,12 @@
PLOG(FATAL) << "Could not dup child_fd";
}
- if (setexeccon(context_.c_str()) < 0) {
- PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+ // We don't switch contexts if we're running the unit tests. We don't use std::optional,
+ // since we still need a real context string to pass to the builtin functions.
+ if (context_ != kTestContext) {
+ if (setexeccon(context_.c_str()) < 0) {
+ PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+ }
}
auto init_path = GetExecutablePath();
diff --git a/init/subcontext.h b/init/subcontext.h
index bcaad29..5e1d8a8 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -32,6 +32,7 @@
static constexpr const char kInitContext[] = "u:r:init:s0";
static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
+static constexpr const char kTestContext[] = "test-test-test";
class Subcontext {
public:
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 9c1a788..2e5a256 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -39,24 +39,12 @@
namespace android {
namespace init {
-// I would use test fixtures, but I cannot skip the test if not root with them, so instead we have
-// this test runner.
template <typename F>
void RunTest(F&& test_function) {
- if (getuid() != 0) {
- GTEST_SKIP() << "Skipping test, must be run as root.";
- return;
- }
-
- char* context;
- ASSERT_EQ(0, getcon(&context));
- auto context_string = std::string(context);
- free(context);
-
- auto subcontext = Subcontext({"dummy_path"}, context_string);
+ auto subcontext = Subcontext({"dummy_path"}, kTestContext);
ASSERT_NE(0, subcontext.pid());
- test_function(subcontext, context_string);
+ test_function(subcontext);
if (subcontext.pid() > 0) {
kill(subcontext.pid(), SIGTERM);
@@ -65,7 +53,7 @@
}
TEST(subcontext, CheckDifferentPid) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
ASSERT_FALSE(result);
@@ -78,7 +66,12 @@
}
TEST(subcontext, SetProp) {
- RunTest([](auto& subcontext, auto& context_string) {
+ if (getuid() != 0) {
+ GTEST_SKIP() << "Skipping test, must be run as root.";
+ return;
+ }
+
+ RunTest([](auto& subcontext) {
SetProperty("init.test.subcontext", "fail");
WaitForProperty("init.test.subcontext", "fail");
@@ -95,7 +88,7 @@
}
TEST(subcontext, MultipleCommands) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto first_pid = subcontext.pid();
auto expected_words = std::vector<std::string>{
@@ -122,7 +115,7 @@
}
TEST(subcontext, RecoverAfterAbort) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto first_pid = subcontext.pid();
auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
@@ -136,10 +129,10 @@
}
TEST(subcontext, ContextString) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
ASSERT_FALSE(result);
- ASSERT_EQ(context_string, result.error().message());
+ ASSERT_EQ(kTestContext, result.error().message());
});
}
@@ -147,7 +140,7 @@
static constexpr const char kTestShutdownCommand[] = "reboot,test-shutdown-command";
static std::string trigger_shutdown_command;
trigger_shutdown = [](const std::string& command) { trigger_shutdown_command = command; };
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto result = subcontext.Execute(
std::vector<std::string>{"trigger_shutdown", kTestShutdownCommand});
ASSERT_TRUE(result);
@@ -156,7 +149,7 @@
}
TEST(subcontext, ExpandArgs) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto args = std::vector<std::string>{
"first",
"${ro.hardware}",
@@ -172,7 +165,7 @@
}
TEST(subcontext, ExpandArgsFailure) {
- RunTest([](auto& subcontext, auto& context_string) {
+ RunTest([](auto& subcontext) {
auto args = std::vector<std::string>{
"first",
"${",
diff --git a/init/sysprop/Android.bp b/init/sysprop/Android.bp
new file mode 100644
index 0000000..7582875
--- /dev/null
+++ b/init/sysprop/Android.bp
@@ -0,0 +1,7 @@
+sysprop_library {
+ name: "com.android.sysprop.init",
+ srcs: ["InitProperties.sysprop"],
+ property_owner: "Platform",
+ api_packages: ["android.sysprop"],
+ recovery_available: true,
+}
diff --git a/init/sysprop/InitProperties.sysprop b/init/sysprop/InitProperties.sysprop
new file mode 100644
index 0000000..d6a1ab6
--- /dev/null
+++ b/init/sysprop/InitProperties.sysprop
@@ -0,0 +1,27 @@
+# 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.
+
+owner: Platform
+module: "android.sysprop.InitProperties"
+
+# Serves as a signal to all processes that userspace reboot is happening.
+prop {
+ api_name: "userspace_reboot_in_progress"
+ type: Boolean
+ scope: Public
+ access: ReadWrite
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+}
+
diff --git a/init/sysprop/api/com.android.sysprop.init-current.txt b/init/sysprop/api/com.android.sysprop.init-current.txt
new file mode 100644
index 0000000..8da50e0
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-current.txt
@@ -0,0 +1,9 @@
+props {
+ module: "android.sysprop.InitProperties"
+ prop {
+ api_name: "userspace_reboot_in_progress"
+ access: ReadWrite
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+ }
+}
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
new file mode 100644
index 0000000..c835b95
--- /dev/null
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -0,0 +1,9 @@
+props {
+ module: "android.sysprop.InitProperties"
+ prop {
+ api_name: "userspace_reboot_in_progress"
+ scope: Public
+ prop_name: "sys.init.userspace_reboot.in_progress"
+ integer_as_bool: true
+ }
+}
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 22cf43b..2886289 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -494,7 +494,7 @@
// Cache miss, go to logd to acquire a public reference.
// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag(EventTagMap* map, unsigned int tag) {
+static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
// call event tag service to arrange for a new tag
char* buf = NULL;
// Can not use android::base::StringPrintf, asprintf + free instead.
@@ -515,8 +515,9 @@
} else {
size = ret;
}
+#ifdef __ANDROID__
// Ask event log tag service for an existing entry
- if (__send_log_msg(buf, size) >= 0) {
+ if (SendLogdControlMessage(buf, size) >= 0) {
buf[size - 1] = '\0';
char* ep;
unsigned long val = strtoul(buf, &ep, 10); // return size
@@ -529,6 +530,7 @@
}
}
}
+#endif
free(buf);
}
return NULL;
@@ -618,8 +620,9 @@
} else {
size = ret;
}
+#ifdef __ANDROID__
// Ask event log tag service for an allocation
- if (__send_log_msg(buf, size) >= 0) {
+ if (SendLogdControlMessage(buf, size) >= 0) {
buf[size - 1] = '\0';
unsigned long val = strtoul(buf, &cp, 10); // return size
if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
@@ -635,6 +638,7 @@
}
}
}
+#endif
free(buf);
}
diff --git a/liblog/fake_log_device.cpp b/liblog/fake_log_device.cpp
index 428a482..f61bbdc 100644
--- a/liblog/fake_log_device.cpp
+++ b/liblog/fake_log_device.cpp
@@ -650,10 +650,6 @@
return fd;
}
-ssize_t __send_log_msg(char*, size_t) {
- return -ENODEV;
-}
-
int __android_log_is_loggable(int prio, const char*, int def) {
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index ce54db2..bd2256c 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -29,7 +29,6 @@
int fakeLogClose(int fd);
ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
-ssize_t __send_log_msg(char*, size_t);
int __android_log_is_loggable(int prio, const char*, int def);
int __android_log_is_loggable_len(int prio, const char*, size_t, int def);
int __android_log_is_debuggable();
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index eaa157a..96e7a61 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "logd_reader.h"
+
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -35,62 +37,8 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "log_portability.h"
-#include "logd_reader.h"
#include "logger.h"
-static int LogdAvailable(log_id_t LogId);
-static int LogdVersion(struct logger* logger, struct android_log_transport_context* transp);
-static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
- struct log_msg* log_msg);
-static int LogdPoll(struct logger_list* logger_list, struct android_log_transport_context* transp);
-static void LogdClose(struct logger_list* logger_list,
- struct android_log_transport_context* transp);
-static int LogdClear(struct logger* logger, struct android_log_transport_context* transp);
-static ssize_t LogdSetSize(struct logger* logger, struct android_log_transport_context* transp,
- size_t size);
-static ssize_t LogdGetSize(struct logger* logger, struct android_log_transport_context* transp);
-static ssize_t LogdGetReadableSize(struct logger* logger,
- struct android_log_transport_context* transp);
-static ssize_t LogdGetPrune(struct logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t LogdSetPrune(struct logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
-static ssize_t LogdGetStats(struct logger_list* logger,
- struct android_log_transport_context* transp, char* buf, size_t len);
-
-struct android_log_transport_read logdLoggerRead = {
- .name = "logd",
- .available = LogdAvailable,
- .version = LogdVersion,
- .close = LogdClose,
- .read = LogdRead,
- .poll = LogdPoll,
- .clear = LogdClear,
- .setSize = LogdSetSize,
- .getSize = LogdGetSize,
- .getReadableSize = LogdGetReadableSize,
- .getPrune = LogdGetPrune,
- .setPrune = LogdSetPrune,
- .getStats = LogdGetStats,
-};
-
-static int LogdAvailable(log_id_t logId) {
- if (logId >= LOG_ID_MAX) {
- return -EINVAL;
- }
- if (logId == LOG_ID_SECURITY) {
- uid_t uid = __android_log_uid();
- if (uid != AID_SYSTEM) {
- return -EPERM;
- }
- }
- if (access("/dev/socket/logdw", W_OK) == 0) {
- return 0;
- }
- return -EBADF;
-}
-
// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
// O_CLOEXEC is always set.
static int socket_local_client(const std::string& name, int type) {
@@ -116,7 +64,7 @@
}
/* worker for sending the command to the logger */
-static ssize_t send_log_msg(struct logger* logger, const char* msg, char* buf, size_t buf_size) {
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
ssize_t ret;
size_t len;
char* cp;
@@ -126,10 +74,6 @@
return sock;
}
- if (msg) {
- snprintf(buf, buf_size, msg, logger ? logger->logId : (unsigned)-1);
- }
-
len = strlen(buf) + 1;
ret = TEMP_FAILURE_RETRY(write(sock, buf, len));
if (ret <= 0) {
@@ -180,10 +124,6 @@
return ret;
}
-ssize_t __send_log_msg(char* buf, size_t buf_size) {
- return send_log_msg(NULL, NULL, buf, buf_size);
-}
-
static int check_log_success(char* buf, ssize_t ret) {
if (ret < 0) {
return ret;
@@ -197,17 +137,28 @@
return 0;
}
-static int LogdClear(struct logger* logger, struct android_log_transport_context*) {
+int android_logger_clear(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+ uint32_t log_id = android_logger_get_id(logger);
char buf[512];
+ snprintf(buf, sizeof(buf), "clear %" PRIu32, log_id);
- return check_log_success(buf, send_log_msg(logger, "clear %d", buf, sizeof(buf)));
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
/* returns the total size of the log's ring buffer */
-static ssize_t LogdGetSize(struct logger* logger, struct android_log_transport_context*) {
- char buf[512];
+long android_logger_get_log_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
- ssize_t ret = send_log_msg(logger, "getLogSize %d", buf, sizeof(buf));
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -219,23 +170,32 @@
return atol(buf);
}
-static ssize_t LogdSetSize(struct logger* logger, struct android_log_transport_context*,
- size_t size) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
+
+ uint32_t log_id = android_logger_get_id(logger);
char buf[512];
+ snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
- snprintf(buf, sizeof(buf), "setLogSize %d %zu", logger->logId, size);
-
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, sizeof(buf)));
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
/*
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-static ssize_t LogdGetReadableSize(struct logger* logger, struct android_log_transport_context*) {
- char buf[512];
+long android_logger_get_log_readable_size(struct logger* logger) {
+ if (!android_logger_is_logd(logger)) {
+ return -EINVAL;
+ }
- ssize_t ret = send_log_msg(logger, "getLogSizeUsed %d", buf, sizeof(buf));
+ uint32_t log_id = android_logger_get_id(logger);
+ char buf[512];
+ snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+
+ ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
@@ -247,20 +207,15 @@
return atol(buf);
}
-/*
- * returns the logger version
- */
-static int LogdVersion(struct logger*, struct android_log_transport_context*) {
- uid_t uid = __android_log_uid();
- return ((uid != AID_ROOT) && (uid != AID_LOG) && (uid != AID_SYSTEM)) ? 3 : 4;
+int android_logger_get_log_version(struct logger*) {
+ return 4;
}
-/*
- * returns statistics
- */
-static ssize_t LogdGetStats(struct logger_list* logger_list, struct android_log_transport_context*,
- char* buf, size_t len) {
- struct logger* logger;
+ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
char* cp = buf;
size_t remaining = len;
size_t n;
@@ -270,27 +225,35 @@
remaining -= n;
cp += n;
- logger_for_each(logger, logger_list) {
- n = snprintf(cp, remaining, " %d", logger->logId);
- n = MIN(n, remaining);
- remaining -= n;
- cp += n;
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ n = snprintf(cp, remaining, " %zu", log_id);
+ n = MIN(n, remaining);
+ remaining -= n;
+ cp += n;
+ }
}
if (logger_list->pid) {
snprintf(cp, remaining, " pid=%u", logger_list->pid);
}
- return send_log_msg(NULL, NULL, buf, len);
+ return SendLogdControlMessage(buf, len);
+}
+ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
+
+ snprintf(buf, len, "getPruneList");
+ return SendLogdControlMessage(buf, len);
}
-static ssize_t LogdGetPrune(struct logger_list*, struct android_log_transport_context*, char* buf,
- size_t len) {
- return send_log_msg(NULL, "getPruneList", buf, len);
-}
+int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ return -EINVAL;
+ }
-static ssize_t LogdSetPrune(struct logger_list*, struct android_log_transport_context*, char* buf,
- size_t len) {
const char cmd[] = "setPruneList ";
const size_t cmdlen = sizeof(cmd) - 1;
@@ -301,19 +264,14 @@
buf[len - 1] = '\0';
memcpy(buf, cmd, cmdlen);
- return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
+ return check_log_success(buf, SendLogdControlMessage(buf, len));
}
-static int logdOpen(struct logger_list* logger_list, struct android_log_transport_context* transp) {
- struct logger* logger;
+static int logdOpen(struct logger_list* logger_list) {
char buffer[256], *cp, c;
int ret, remaining, sock;
- if (!logger_list) {
- return -EINVAL;
- }
-
- sock = atomic_load(&transp->context.sock);
+ sock = atomic_load(&logger_list->fd);
if (sock > 0) {
return sock;
}
@@ -333,12 +291,15 @@
cp += 5;
c = '=';
remaining = sizeof(buffer) - (cp - buffer);
- logger_for_each(logger, logger_list) {
- ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
- ret = MIN(ret, remaining);
- remaining -= ret;
- cp += ret;
- c = ',';
+
+ for (size_t log_id = 0; log_id < LOG_ID_MAX; ++log_id) {
+ if ((1 << log_id) & logger_list->log_mask) {
+ ret = snprintf(cp, remaining, "%c%zu", c, log_id);
+ ret = MIN(ret, remaining);
+ remaining -= ret;
+ cp += ret;
+ c = ',';
+ }
}
if (logger_list->tail) {
@@ -383,7 +344,7 @@
return ret;
}
- ret = atomic_exchange(&transp->context.sock, sock);
+ ret = atomic_exchange(&logger_list->fd, sock);
if ((ret > 0) && (ret != sock)) {
close(ret);
}
@@ -391,15 +352,12 @@
}
/* Read from the selected logs */
-static int LogdRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
- int ret = logdOpen(logger_list, transp);
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg) {
+ int ret = logdOpen(logger_list);
if (ret < 0) {
return ret;
}
- memset(log_msg, 0, sizeof(*log_msg));
-
/* NOTE: SOCK_SEQPACKET guarantees we read exactly one full entry */
ret = TEMP_FAILURE_RETRY(recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0));
if ((logger_list->mode & ANDROID_LOG_NONBLOCK) && ret == 0) {
@@ -412,30 +370,9 @@
return ret;
}
-static int LogdPoll(struct logger_list* logger_list, struct android_log_transport_context* transp) {
- struct pollfd p;
-
- int ret = logdOpen(logger_list, transp);
- if (ret < 0) {
- return ret;
- }
-
- memset(&p, 0, sizeof(p));
- p.fd = ret;
- p.events = POLLIN;
- ret = poll(&p, 1, 20);
- if ((ret > 0) && !(p.revents & POLLIN)) {
- ret = 0;
- }
- if ((ret == -1) && errno) {
- return -errno;
- }
- return ret;
-}
-
/* Close all the logs */
-static void LogdClose(struct logger_list*, struct android_log_transport_context* transp) {
- int sock = atomic_exchange(&transp->context.sock, -1);
+void LogdClose(struct logger_list* logger_list) {
+ int sock = atomic_exchange(&logger_list->fd, -1);
if (sock > 0) {
close(sock);
}
diff --git a/liblog/logd_reader.h b/liblog/logd_reader.h
index 7c53cbb..2d032fa 100644
--- a/liblog/logd_reader.h
+++ b/liblog/logd_reader.h
@@ -18,10 +18,14 @@
#include <unistd.h>
+#include "log/log_read.h"
#include "log_portability.h"
__BEGIN_DECLS
-ssize_t __send_log_msg(char* buf, size_t buf_size);
+int LogdRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void LogdClose(struct logger_list* logger_list);
+
+ssize_t SendLogdControlMessage(char* buf, size_t buf_size);
__END_DECLS
diff --git a/liblog/logger.h b/liblog/logger.h
index 9c488b6..9d74d29 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -46,69 +46,28 @@
size_t nr);
};
-struct android_log_transport_context;
-
-struct android_log_transport_read {
- const char* name; /* human name to describe the transport */
-
- /* Does not cause resources to be taken */
- int (*available)(log_id_t logId);
- int (*version)(struct logger* logger, struct android_log_transport_context* transp);
- /* Release resources taken by the following interfaces */
- void (*close)(struct logger_list* logger_list, struct android_log_transport_context* transp);
- /*
- * Expect all to instantiate open automagically on any call,
- * so we do not have an explicit open call.
- */
- int (*read)(struct logger_list* logger_list, struct android_log_transport_context* transp,
- struct log_msg* log_msg);
- /* Must only be called if not ANDROID_LOG_NONBLOCK (blocking) */
- int (*poll)(struct logger_list* logger_list, struct android_log_transport_context* transp);
-
- int (*clear)(struct logger* logger, struct android_log_transport_context* transp);
- ssize_t (*setSize)(struct logger* logger, struct android_log_transport_context* transp,
- size_t size);
- ssize_t (*getSize)(struct logger* logger, struct android_log_transport_context* transp);
- ssize_t (*getReadableSize)(struct logger* logger, struct android_log_transport_context* transp);
-
- ssize_t (*getPrune)(struct logger_list* logger_list, struct android_log_transport_context* transp,
- char* buf, size_t len);
- ssize_t (*setPrune)(struct logger_list* logger_list, struct android_log_transport_context* transp,
- char* buf, size_t len);
- ssize_t (*getStats)(struct logger_list* logger_list, struct android_log_transport_context* transp,
- char* buf, size_t len);
-};
-
-struct android_log_transport_context {
- union android_log_context_union context; /* zero init per-transport context */
-
- struct android_log_transport_read* transport;
- unsigned logMask; /* mask of requested log buffers */
-};
-
struct logger_list {
- struct listnode logger;
- android_log_transport_context transport_context;
- bool transport_initialized;
+ atomic_int fd;
int mode;
unsigned int tail;
log_time start;
pid_t pid;
+ uint32_t log_mask;
};
-struct logger {
- struct listnode node;
- struct logger_list* parent;
+// Format for a 'logger' entry: uintptr_t where only the bottom 32 bits are used.
+// bit 31: Set if this 'logger' is for logd.
+// bit 30: Set if this 'logger' is for pmsg
+// bits 0-2: the decimal value of the log buffer.
+// Other bits are unused.
- log_id_t logId;
-};
+#define LOGGER_LOGD (1U << 31)
+#define LOGGER_PMSG (1U << 30)
+#define LOGGER_LOG_ID_MASK ((1U << 3) - 1)
-/* assumes caller has structures read-locked, single threaded, or fenced */
-#define logger_for_each(logp, logger_list) \
- for ((logp) = node_to_item((logger_list)->logger.next, struct logger, node); \
- ((logp) != node_to_item(&(logger_list)->logger, struct logger, node)) && \
- ((logp)->parent == (logger_list)); \
- (logp) = node_to_item((logp)->node.next, struct logger, node))
+inline bool android_logger_is_logd(struct logger* logger) {
+ return reinterpret_cast<uintptr_t>(logger) & LOGGER_LOGD;
+}
/* OS specific dribs and drabs */
diff --git a/liblog/logger_read.cpp b/liblog/logger_read.cpp
index 5e10ada..c65501c 100644
--- a/liblog/logger_read.cpp
+++ b/liblog/logger_read.cpp
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -30,213 +31,49 @@
#include <private/android_filesystem_config.h>
#include "log_portability.h"
+#include "logd_reader.h"
#include "logger.h"
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger* logger) {
- if (!logger) {
- return;
- }
-
- list_remove(&logger->node);
-
- free(logger);
-}
-
-/* android_logger_alloc unimplemented, no use case */
+#include "pmsg_reader.h"
/* method for getting the associated sublog id */
log_id_t android_logger_get_id(struct logger* logger) {
- return ((struct logger*)logger)->logId;
+ return static_cast<log_id_t>(reinterpret_cast<uintptr_t>(logger) & LOGGER_LOG_ID_MASK);
}
-static int init_transport_context(struct logger_list* logger_list) {
+static struct logger_list* android_logger_list_alloc_internal(int mode, unsigned int tail,
+ log_time start, pid_t pid) {
+ auto* logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(struct logger_list)));
if (!logger_list) {
- return -EINVAL;
+ return nullptr;
}
- if (list_empty(&logger_list->logger)) {
- return -EINVAL;
- }
-
- if (logger_list->transport_initialized) {
- return 0;
- }
-
-#if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_read logdLoggerRead;
- extern struct android_log_transport_read pmsgLoggerRead;
-
- struct android_log_transport_read* transport;
- transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
-
- struct logger* logger;
- unsigned logMask = 0;
-
- logger_for_each(logger, logger_list) {
- log_id_t logId = logger->logId;
-
- if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) {
- continue;
- }
- if (transport->read && (!transport->available || transport->available(logId) >= 0)) {
- logMask |= 1 << logId;
- }
- }
- if (!logMask) {
- return -ENODEV;
- }
-
- logger_list->transport_context.transport = transport;
- logger_list->transport_context.logMask = logMask;
-#endif
- return 0;
-}
-
-#define LOGGER_FUNCTION(logger, def, func, args...) \
- ssize_t ret = -EINVAL; \
- \
- if (!logger) { \
- return ret; \
- } \
- ret = init_transport_context(logger->parent); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- android_log_transport_context* transport_context = &logger->parent->transport_context; \
- if (transport_context->logMask & (1 << logger->logId) && transport_context->transport && \
- transport_context->transport->func) { \
- ssize_t retval = (transport_context->transport->func)(logger, transport_context, ##args); \
- if (ret >= 0 || ret == (def)) { \
- ret = retval; \
- } \
- } \
- return ret
-
-int android_logger_clear(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, clear);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, getSize);
-}
-
-int android_logger_set_log_size(struct logger* logger, unsigned long size) {
- LOGGER_FUNCTION(logger, -ENODEV, setSize, size);
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger* logger) {
- LOGGER_FUNCTION(logger, -ENODEV, getReadableSize);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger* logger) {
- LOGGER_FUNCTION(logger, 4, version);
-}
-
-#define LOGGER_LIST_FUNCTION(logger_list, def, func, args...) \
- ssize_t ret = init_transport_context(logger_list); \
- if (ret < 0) { \
- return ret; \
- } \
- \
- ret = (def); \
- android_log_transport_context* transport_context = &logger_list->transport_context; \
- if (transport_context->transport && transport_context->transport->func) { \
- ssize_t retval = (transport_context->transport->func)(logger_list, transport_context, ##args); \
- if (ret >= 0 || ret == (def)) { \
- ret = retval; \
- } \
- } \
- return ret
-
-/*
- * returns statistics
- */
-ssize_t android_logger_get_statistics(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getStats, buf, len);
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, getPrune, buf, len);
-}
-
-int android_logger_set_prune_list(struct logger_list* logger_list, char* buf, size_t len) {
- LOGGER_LIST_FUNCTION(logger_list, -ENODEV, setPrune, buf, len);
-}
-
-struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
- struct logger_list* logger_list;
-
- logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->logger);
logger_list->mode = mode;
+ logger_list->start = start;
logger_list->tail = tail;
logger_list->pid = pid;
return logger_list;
}
-struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
- struct logger_list* logger_list;
-
- logger_list = static_cast<struct logger_list*>(calloc(1, sizeof(*logger_list)));
- if (!logger_list) {
- return NULL;
- }
-
- list_init(&logger_list->logger);
- logger_list->mode = mode;
- logger_list->start = start;
- logger_list->pid = pid;
-
- return logger_list;
+struct logger_list* android_logger_list_alloc(int mode, unsigned int tail, pid_t pid) {
+ return android_logger_list_alloc_internal(mode, tail, log_time(0, 0), pid);
}
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
+struct logger_list* android_logger_list_alloc_time(int mode, log_time start, pid_t pid) {
+ return android_logger_list_alloc_internal(mode, 0, start, pid);
+}
/* Open the named log and add it to the logger list */
struct logger* android_logger_open(struct logger_list* logger_list, log_id_t logId) {
- struct logger* logger;
-
if (!logger_list || (logId >= LOG_ID_MAX)) {
return nullptr;
}
- logger_for_each(logger, logger_list) {
- if (logger->logId == logId) {
- return reinterpret_cast<struct logger*>(logger);
- }
- }
+ logger_list->log_mask |= 1 << logId;
- logger = static_cast<struct logger*>(calloc(1, sizeof(*logger)));
- if (!logger) {
- return nullptr;
- }
-
- logger->logId = logId;
- list_add_tail(&logger_list->logger, &logger->node);
- logger->parent = logger_list;
-
- // Reset known transport to re-evaluate, since we added a new logger.
- logger_list->transport_initialized = false;
-
- return logger;
+ uintptr_t logger = logId;
+ logger |= (logger_list->mode & ANDROID_LOG_PSTORE) ? LOGGER_PMSG : LOGGER_LOGD;
+ return reinterpret_cast<struct logger*>(logger);
}
/* Open the single named log and make it part of a new logger list */
@@ -256,13 +93,22 @@
return logger_list;
}
-/* Validate log_msg packet, read function has already been null checked */
-static int android_transport_read(struct logger_list* logger_list,
- struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
- int ret = (*transp->transport->read)(logger_list, transp, log_msg);
+int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
+ if (logger_list == nullptr || logger_list->log_mask == 0) {
+ return -EINVAL;
+ }
- if (ret < 0) {
+ int ret = 0;
+
+#if (FAKE_LOG_DEVICE == 0)
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ ret = PmsgRead(logger_list, log_msg);
+ } else {
+ ret = LogdRead(logger_list, log_msg);
+ }
+#endif
+
+ if (ret <= 0) {
return ret;
}
@@ -285,34 +131,19 @@
return ret;
}
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) {
- int ret = init_transport_context(logger_list);
- if (ret < 0) {
- return ret;
- }
-
- android_log_transport_context* transport_context = &logger_list->transport_context;
- return android_transport_read(logger_list, transport_context, log_msg);
-}
-
/* Close all the logs */
void android_logger_list_free(struct logger_list* logger_list) {
if (logger_list == NULL) {
return;
}
- android_log_transport_context* transport_context = &logger_list->transport_context;
-
- if (transport_context->transport && transport_context->transport->close) {
- (*transport_context->transport->close)(logger_list, transport_context);
+#if (FAKE_LOG_DEVICE == 0)
+ if (logger_list->mode & ANDROID_LOG_PSTORE) {
+ PmsgClose(logger_list);
+ } else {
+ LogdClose(logger_list);
}
-
- while (!list_empty(&logger_list->logger)) {
- struct listnode* node = list_head(&logger_list->logger);
- struct logger* logger = node_to_item(node, struct logger, node);
- android_logger_free((struct logger*)logger);
- }
+#endif
free(logger_list);
}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index cd83161..9390fec 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "pmsg_reader.h"
+
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
@@ -26,53 +28,7 @@
#include "logger.h"
-static int PmsgAvailable(log_id_t logId);
-static int PmsgVersion(struct logger* logger, struct android_log_transport_context* transp);
-static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
- struct log_msg* log_msg);
-static void PmsgClose(struct logger_list* logger_list,
- struct android_log_transport_context* transp);
-static int PmsgClear(struct logger* logger, struct android_log_transport_context* transp);
-
-struct android_log_transport_read pmsgLoggerRead = {
- .name = "pmsg",
- .available = PmsgAvailable,
- .version = PmsgVersion,
- .close = PmsgClose,
- .read = PmsgRead,
- .poll = NULL,
- .clear = PmsgClear,
- .setSize = NULL,
- .getSize = NULL,
- .getReadableSize = NULL,
- .getPrune = NULL,
- .setPrune = NULL,
- .getStats = NULL,
-};
-
-static int PmsgAvailable(log_id_t logId) {
- if (logId > LOG_ID_SECURITY) {
- return -EINVAL;
- }
- if (access("/dev/pmsg0", W_OK) == 0) {
- return 0;
- }
- return -EBADF;
-}
-
-static int PmsgClear(struct logger*, struct android_log_transport_context*) {
- return unlink("/sys/fs/pstore/pmsg-ramoops-0");
-}
-
-/*
- * returns the logger version
- */
-static int PmsgVersion(struct logger*, struct android_log_transport_context*) {
- return 4;
-}
-
-static int PmsgRead(struct logger_list* logger_list, struct android_log_transport_context* transp,
- struct log_msg* log_msg) {
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg) {
ssize_t ret;
off_t current, next;
struct __attribute__((__packed__)) {
@@ -84,7 +40,7 @@
memset(log_msg, 0, sizeof(*log_msg));
- if (atomic_load(&transp->context.fd) <= 0) {
+ if (atomic_load(&logger_list->fd) <= 0) {
int i, fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY | O_CLOEXEC);
if (fd < 0) {
@@ -97,7 +53,7 @@
return -errno;
}
}
- i = atomic_exchange(&transp->context.fd, fd);
+ i = atomic_exchange(&logger_list->fd, fd);
if ((i > 0) && (i != fd)) {
close(i);
}
@@ -108,7 +64,7 @@
int fd;
if (preread_count < sizeof(buf)) {
- fd = atomic_load(&transp->context.fd);
+ fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
return -EBADF;
}
@@ -134,7 +90,7 @@
}
preread_count = 0;
- if ((transp->logMask & (1 << buf.l.id)) &&
+ if ((logger_list->log_mask & (1 << buf.l.id)) &&
((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
@@ -142,7 +98,7 @@
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
char* msg = log_msg->entry.msg;
*msg = buf.prio;
- fd = atomic_load(&transp->context.fd);
+ fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
return -EBADF;
}
@@ -166,7 +122,7 @@
return ret + sizeof(buf.prio) + log_msg->entry.hdr_size;
}
- fd = atomic_load(&transp->context.fd);
+ fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
return -EBADF;
}
@@ -174,7 +130,7 @@
if (current < 0) {
return -errno;
}
- fd = atomic_load(&transp->context.fd);
+ fd = atomic_load(&logger_list->fd);
if (fd <= 0) {
return -EBADF;
}
@@ -188,8 +144,8 @@
}
}
-static void PmsgClose(struct logger_list*, struct android_log_transport_context* transp) {
- int fd = atomic_exchange(&transp->context.fd, 0);
+void PmsgClose(struct logger_list* logger_list) {
+ int fd = atomic_exchange(&logger_list->fd, 0);
if (fd > 0) {
close(fd);
}
@@ -207,7 +163,6 @@
__android_log_pmsg_file_read_fn fn, void* arg) {
ssize_t ret;
struct logger_list logger_list;
- struct android_log_transport_context transp;
struct content {
struct listnode node;
struct logger_entry entry;
@@ -229,15 +184,14 @@
/* Add just enough clues in logger_list and transp to make API function */
memset(&logger_list, 0, sizeof(logger_list));
- memset(&transp, 0, sizeof(transp));
logger_list.mode = ANDROID_LOG_PSTORE | ANDROID_LOG_NONBLOCK | ANDROID_LOG_RDONLY;
- transp.logMask = (unsigned)-1;
+ logger_list.log_mask = (unsigned)-1;
if (logId != LOG_ID_ANY) {
- transp.logMask = (1 << logId);
+ logger_list.log_mask = (1 << logId);
}
- transp.logMask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
- if (!transp.logMask) {
+ logger_list.log_mask &= ~((1 << LOG_ID_KERNEL) | (1 << LOG_ID_EVENTS) | (1 << LOG_ID_SECURITY));
+ if (!logger_list.log_mask) {
return -EINVAL;
}
@@ -263,7 +217,7 @@
/* Read the file content */
log_msg log_msg;
- while (PmsgRead(&logger_list, &transp, &log_msg) > 0) {
+ while (PmsgRead(&logger_list, &log_msg) > 0) {
const char* cp;
size_t hdr_size = log_msg.entry.hdr_size;
@@ -421,7 +375,7 @@
}
list_add_head(node, &content->node);
}
- PmsgClose(&logger_list, &transp);
+ PmsgClose(&logger_list);
/* Progress through all the collected files */
list_for_each_safe(node, n, &name_list) {
diff --git a/liblog/pmsg_reader.h b/liblog/pmsg_reader.h
new file mode 100644
index 0000000..53746d8
--- /dev/null
+++ b/liblog/pmsg_reader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <unistd.h>
+
+#include "log/log_read.h"
+#include "log_portability.h"
+
+__BEGIN_DECLS
+
+int PmsgRead(struct logger_list* logger_list, struct log_msg* log_msg);
+void PmsgClose(struct logger_list* logger_list);
+
+__END_DECLS
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 56892a2..39ac7a5 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -913,7 +913,7 @@
}
BENCHMARK(BM_lookupEventTagNum);
-// Must be functionally identical to liblog internal __send_log_msg.
+// Must be functionally identical to liblog internal SendLogdControlMessage()
static void send_to_control(char* buf, size_t len) {
int sock =
socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM | SOCK_CLOEXEC);
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 2573b1c..512c962 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -77,6 +77,7 @@
],
cflags: [
+ "-DDEXFILE_SUPPORT",
"-Wexit-time-destructors",
],
@@ -89,20 +90,18 @@
],
},
vendor: {
- cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: [
"DexFile.cpp",
- "DexFiles.cpp",
],
exclude_shared_libs: [
"libdexfile_support",
],
},
recovery: {
- cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
+ cflags: ["-UDEXFILE_SUPPORT"],
exclude_srcs: [
"DexFile.cpp",
- "DexFiles.cpp",
],
exclude_shared_libs: [
"libdexfile_support",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index 63a77e5..2057fad 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -27,10 +27,21 @@
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#if defined(DEXFILE_SUPPORT)
#include "DexFile.h"
+#endif
namespace unwindstack {
+#if !defined(DEXFILE_SUPPORT)
+// Empty class definition.
+class DexFile {
+ public:
+ DexFile() = default;
+ virtual ~DexFile() = default;
+};
+#endif
+
struct DEXFileEntry32 {
uint32_t next;
uint32_t prev;
@@ -128,6 +139,7 @@
FindAndReadVariable(maps, "__dex_debug_descriptor");
}
+#if defined(DEXFILE_SUPPORT)
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
// Lock while processing the data.
DexFile* dex_file;
@@ -141,6 +153,11 @@
}
return dex_file;
}
+#else
+DexFile* DexFiles::GetDexFile(uint64_t, MapInfo*) {
+ return nullptr;
+}
+#endif
bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
if (index < addrs_.size()) {
@@ -154,6 +171,7 @@
return false;
}
+#if defined(DEXFILE_SUPPORT)
void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
std::string* method_name, uint64_t* method_offset) {
std::lock_guard<std::mutex> guard(lock_);
@@ -175,5 +193,8 @@
}
}
}
+#else
+void DexFiles::GetMethodInformation(Maps*, MapInfo*, uint64_t, std::string*, uint64_t*) {}
+#endif
} // namespace unwindstack
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index e9787aa..2e8af20 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -103,6 +103,7 @@
fn("sp", regs_[ARM64_REG_SP]);
fn("lr", regs_[ARM64_REG_LR]);
fn("pc", regs_[ARM64_REG_PC]);
+ fn("pst", regs_[ARM64_REG_PSTATE]);
}
Regs* RegsArm64::Read(void* remote_data) {
@@ -113,6 +114,7 @@
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
reg_data[ARM64_REG_PC] = user->pc;
reg_data[ARM64_REG_SP] = user->sp;
+ reg_data[ARM64_REG_PSTATE] = user->pstate;
return regs;
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 0b9b85c..1bb0319 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -34,9 +34,7 @@
#include <unwindstack/Memory.h>
#include <unwindstack/Unwinder.h>
-#if !defined(NO_LIBDEXFILE_SUPPORT)
#include <unwindstack/DexFiles.h>
-#endif
// Use the demangler from libc++.
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
@@ -84,7 +82,7 @@
return;
}
-#if !defined(NO_LIBDEXFILE_SUPPORT)
+#if defined(DEXFILE_SUPPORT)
if (dex_files_ == nullptr) {
return;
}
@@ -367,12 +365,10 @@
jit_debug_ = jit_debug;
}
-#if !defined(NO_LIBDEXFILE_SUPPORT)
void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
dex_files->SetArch(arch);
dex_files_ = dex_files;
}
-#endif
bool UnwinderFromPid::Init(ArchEnum arch) {
if (pid_ == getpid()) {
@@ -390,7 +386,7 @@
jit_debug_ptr_.reset(new JitDebug(process_memory_));
jit_debug_ = jit_debug_ptr_.get();
SetJitDebug(jit_debug_, arch);
-#if !defined(NO_LIBDEXFILE_SUPPORT)
+#if defined(DEXFILE_SUPPORT)
dex_files_ptr_.reset(new DexFiles(process_memory_));
dex_files_ = dex_files_ptr_.get();
SetDexFiles(dex_files_, arch);
diff --git a/libunwindstack/include/unwindstack/MachineArm64.h b/libunwindstack/include/unwindstack/MachineArm64.h
index e8b778b..e953335 100644
--- a/libunwindstack/include/unwindstack/MachineArm64.h
+++ b/libunwindstack/include/unwindstack/MachineArm64.h
@@ -55,6 +55,7 @@
ARM64_REG_R30,
ARM64_REG_R31,
ARM64_REG_PC,
+ ARM64_REG_PSTATE,
ARM64_REG_LAST,
ARM64_REG_SP = ARM64_REG_R31,
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 11ad9de..67762c0 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -107,9 +107,7 @@
void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
-#if !defined(NO_LIBDEXFILE_SUPPORT)
void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
-#endif
bool elf_from_memory_not_file() { return elf_from_memory_not_file_; }
@@ -128,9 +126,7 @@
std::vector<FrameData> frames_;
std::shared_ptr<Memory> process_memory_;
JitDebug* jit_debug_ = nullptr;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
DexFiles* dex_files_ = nullptr;
-#endif
bool resolve_names_ = true;
bool embedded_soname_ = true;
bool display_build_id_ = false;
@@ -151,9 +147,7 @@
pid_t pid_;
std::unique_ptr<Maps> maps_ptr_;
std::unique_ptr<JitDebug> jit_debug_ptr_;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
std::unique_ptr<DexFiles> dex_files_ptr_;
-#endif
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsIterateTest.cpp b/libunwindstack/tests/RegsIterateTest.cpp
index 7e36953..bc95851 100644
--- a/libunwindstack/tests/RegsIterateTest.cpp
+++ b/libunwindstack/tests/RegsIterateTest.cpp
@@ -114,6 +114,7 @@
result.push_back({"sp", ARM64_REG_SP});
result.push_back({"lr", ARM64_REG_LR});
result.push_back({"pc", ARM64_REG_PC});
+ result.push_back({"pst", ARM64_REG_PSTATE});
return result;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 98921be..efa4c41 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -125,6 +125,7 @@
native_bridge_supported: true,
srcs: [
+ "Errors.cpp",
"FileMap.cpp",
"JenkinsHash.cpp",
"NativeHandle.cpp",
diff --git a/libutils/Errors.cpp b/libutils/Errors.cpp
new file mode 100644
index 0000000..2dfd138
--- /dev/null
+++ b/libutils/Errors.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 <utils/Errors.h>
+
+namespace android {
+
+std::string statusToString(status_t s) {
+#define STATUS_CASE(STATUS) \
+ case STATUS: \
+ return #STATUS
+
+ switch (s) {
+ STATUS_CASE(OK);
+ STATUS_CASE(UNKNOWN_ERROR);
+ STATUS_CASE(NO_MEMORY);
+ STATUS_CASE(INVALID_OPERATION);
+ STATUS_CASE(BAD_VALUE);
+ STATUS_CASE(BAD_TYPE);
+ STATUS_CASE(NAME_NOT_FOUND);
+ STATUS_CASE(PERMISSION_DENIED);
+ STATUS_CASE(NO_INIT);
+ STATUS_CASE(ALREADY_EXISTS);
+ STATUS_CASE(DEAD_OBJECT);
+ STATUS_CASE(FAILED_TRANSACTION);
+ STATUS_CASE(BAD_INDEX);
+ STATUS_CASE(NOT_ENOUGH_DATA);
+ STATUS_CASE(WOULD_BLOCK);
+ STATUS_CASE(TIMED_OUT);
+ STATUS_CASE(UNKNOWN_TRANSACTION);
+ STATUS_CASE(FDS_NOT_ALLOWED);
+ STATUS_CASE(UNEXPECTED_NULL);
+#undef STATUS_CASE
+ }
+
+ return std::to_string(s) + ' ' + strerror(-s);
+}
+
+} // namespace android
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 1e03677..d14d223 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -19,6 +19,7 @@
#include <errno.h>
#include <stdint.h>
#include <sys/types.h>
+#include <string>
namespace android {
@@ -72,6 +73,9 @@
UNEXPECTED_NULL = (UNKNOWN_ERROR + 8),
};
+// Human readable name of error
+std::string statusToString(status_t status);
+
// Restore define; enumeration is in "android" namespace, so the value defined
// there won't work for Win32 code in a different namespace.
#ifdef _WIN32
diff --git a/libziparchive/unzip.cpp b/libziparchive/unzip.cpp
index 56f594a..11b575e 100644
--- a/libziparchive/unzip.cpp
+++ b/libziparchive/unzip.cpp
@@ -448,6 +448,7 @@
static const struct option opts[] = {
{"help", no_argument, 0, 'h'},
+ {},
};
if (role == kUnzip) {
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 70ccb80..2d14bf3 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -969,6 +969,16 @@
}
}
+ if (mode & ANDROID_LOG_PSTORE) {
+ if (clearLog) {
+ unlink("/sys/fs/pstore/pmsg-ramoops-0");
+ return EXIT_SUCCESS;
+ }
+ if (setLogSize || getLogSize || printStatistics || getPruneList || setPruneList) {
+ LogcatPanic(HELP_TRUE, "-L is incompatible with -g/-G, -S, and -p/-P");
+ }
+ }
+
std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{
nullptr, &android_logger_list_free};
if (tail_time != log_time::EPOCH) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index ba05a06..834b20b 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -638,6 +638,8 @@
if (stats.sizes(id) > (2 * log_buffer_size(id))) { // +100%
// A misbehaving or slow reader has its connection
// dropped if we hit too much memory pressure.
+ android::prdebug("Kicking blocked reader, pid %d, from LogBuffer::kickMe()\n",
+ me->mClient->getPid());
me->release_Locked();
} else if (me->mTimeout.tv_sec || me->mTimeout.tv_nsec) {
// Allow a blocked WRAP timeout reader to
@@ -645,6 +647,9 @@
me->triggerReader_Locked();
} else {
// tell slow reader to skip entries to catch up
+ android::prdebug(
+ "Skipping %lu entries from slow reader, pid %d, from LogBuffer::kickMe()\n",
+ pruneRows, me->mClient->getPid());
me->triggerSkip_Locked(id, pruneRows);
}
}
@@ -1051,6 +1056,9 @@
LogTimeEntry* entry = times->get();
// Killer punch
if (entry->isWatching(id)) {
+ android::prdebug(
+ "Kicking blocked reader, pid %d, from LogBuffer::clear()\n",
+ entry->mClient->getPid());
entry->release_Locked();
}
times++;
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 33da1f1..f4a846f 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -375,9 +375,11 @@
{"audio_hal.period_size", "u:object_r:default_prop:s0"},
{"bluetooth.enable_timeout_ms", "u:object_r:bluetooth_prop:s0"},
{"dalvik.vm.appimageformat", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.boot-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.boot-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.dexopt.secondary", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.heapgrowthlimit", "u:object_r:dalvik_prop:s0"},
@@ -388,6 +390,7 @@
{"dalvik.vm.heaptargetutilization", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xms", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-Xmx", "u:object_r:dalvik_prop:s0"},
+ {"dalvik.vm.image-dex2oat-cpu-set", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.image-dex2oat-threads", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.features", "u:object_r:dalvik_prop:s0"},
{"dalvik.vm.isa.arm.variant", "u:object_r:dalvik_prop:s0"},
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 2e9192a..782fb92 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -546,6 +546,7 @@
mkdir /data/misc/sms 0770 system radio
mkdir /data/misc/carrierid 0770 system radio
mkdir /data/misc/apns 0770 system radio
+ mkdir /data/misc/emergencynumberdb 0770 system radio
mkdir /data/misc/zoneinfo 0775 system system
mkdir /data/misc/network_watchlist 0774 system system
mkdir /data/misc/textclassifier 0771 system system