Revert deleting vsoc
Bug: 144973127
Test: git diff 698c8df9e00602c8cd82bbcc872693509a169fe9
Change-Id: I19d15f093a69e18f6fafa00e4808620297d37df8
diff --git a/Android.bp b/Android.bp
index 9fed081..a9ee724 100644
--- a/Android.bp
+++ b/Android.bp
@@ -110,3 +110,132 @@
host_supported: true,
defaults: ["cuttlefish_base"],
}
+
+cc_library_shared {
+ name: "vsoc_lib",
+ srcs: [
+ "common/vsoc/lib/compat.cpp",
+ "common/vsoc/lib/audio_data_layout.cpp",
+ "common/vsoc/lib/e2e_test_region_layout.cpp",
+ "common/vsoc/lib/gralloc_layout.cpp",
+ "common/vsoc/lib/input_events_layout.cpp",
+ "common/vsoc/lib/input_events_region_view.cpp",
+ "common/vsoc/lib/lock_common.cpp",
+ "common/vsoc/lib/managed_e2e_test_region_layout.cpp",
+ "common/vsoc/lib/region_view.cpp",
+ "common/vsoc/lib/screen_layout.cpp",
+ "common/vsoc/lib/screen_region_view.cpp",
+ "common/vsoc/lib/socket_forward_layout.cpp",
+ "common/vsoc/lib/socket_forward_region_view.cpp",
+ "common/vsoc/lib/vsoc_memory.cpp",
+ ],
+ header_libs: ["cuttlefish_glog"],
+ shared_libs: [
+ "libcuttlefish_fs",
+ "cuttlefish_auto_resources",
+ "libbase",
+ "liblog",
+ ],
+ target: {
+ host: {
+ srcs: [
+ "host/vsoc/lib/gralloc_buffer_region_view.cpp",
+ "host/vsoc/lib/host_lock.cpp",
+ "host/vsoc/lib/region_control.cpp",
+ "host/vsoc/lib/region_view.cpp",
+ ],
+ },
+ android: {
+ srcs: [
+ "guest/vsoc/lib/gralloc_region_view.cpp",
+ "guest/vsoc/lib/guest_lock.cpp",
+ "guest/vsoc/lib/region_control.cpp",
+ "guest/vsoc/lib/region_view.cpp",
+ ],
+ },
+ },
+ defaults: ["cuttlefish_host_and_guest"],
+}
+
+cc_test_host {
+ name: "circqueue_test",
+ srcs: [
+ "common/vsoc/lib/circqueue_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libbase",
+ ],
+ defaults: ["cuttlefish_host_only"],
+ test_suites: ["general-tests"],
+}
+
+cc_binary_host {
+ name: "vsoc_screen_region_view_test",
+ srcs: [
+ "common/vsoc/lib/screen_region_view_test.cpp",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ "libgflags",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libbase",
+ "libcuttlefish_utils",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+ name: "lock_test",
+ srcs: [
+ "common/vsoc/lib/lock_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libcuttlefish_fs",
+ "cuttlefish_auto_resources",
+ "libbase",
+ ],
+ static_libs: [
+ "libgtest_host",
+ ],
+ defaults: ["cuttlefish_host_only"],
+ test_suites: ["general-tests"],
+}
+
+cc_test_host {
+ name: "vsoc_graphics_test",
+ srcs: [
+ "common/vsoc/lib/graphics_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libbase",
+ ],
+ defaults: ["cuttlefish_host_only"],
+ test_suites: ["general-tests"],
+}
+
+cc_binary_host {
+ name: "host_region_e2e_test",
+ srcs: [
+ "host/vsoc/lib/host_region_e2e_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ "cuttlefish_auto_resources",
+ "libbase",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ "libgtest_host",
+ "libgflags",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/frontend/Android.bp b/common/frontend/Android.bp
index 39a4dbb..a7a4de5 100644
--- a/common/frontend/Android.bp
+++ b/common/frontend/Android.bp
@@ -14,5 +14,6 @@
// limitations under the License.
subdirs = [
+ "socket_forward_proxy",
"socket_vsock_proxy",
]
diff --git a/common/frontend/socket_forward_proxy/Android.bp b/common/frontend/socket_forward_proxy/Android.bp
new file mode 100644
index 0000000..74ef53a
--- /dev/null
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -0,0 +1,45 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary {
+ name: "socket_forward_proxy",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ "libcuttlefish_strings",
+ "cuttlefish_auto_resources",
+ "vsoc_lib",
+ "liblog",
+ ],
+ static_libs: [
+ "libgflags",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ target: {
+ host: {
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ ],
+ },
+ },
+ defaults: ["cuttlefish_host_and_guest"]
+}
diff --git a/common/frontend/socket_forward_proxy/main.cpp b/common/frontend/socket_forward_proxy/main.cpp
new file mode 100644
index 0000000..4c21459
--- /dev/null
+++ b/common/frontend/socket_forward_proxy/main.cpp
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2018 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 <array>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <string>
+#include <thread>
+#include <vector>
+#include <glog/logging.h>
+#include <gflags/gflags.h>
+
+#include <unistd.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/strings/str_split.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
+
+#ifdef CUTTLEFISH_HOST
+#include "host/libs/config/cuttlefish_config.h"
+#endif
+
+using vsoc::socket_forward::Packet;
+using vsoc::socket_forward::SocketForwardRegionView;
+
+#ifdef CUTTLEFISH_HOST
+DEFINE_string(guest_ports, "",
+ "Comma-separated list of ports on which to forward TCP "
+ "connections to the guest.");
+DEFINE_string(host_ports, "",
+ "Comma-separated list of ports on which to run TCP servers on "
+ "the host.");
+#endif
+
+namespace {
+// Sends packets, Shutdown(SHUT_WR) on destruction
+class SocketSender {
+ public:
+ explicit SocketSender(cvd::SharedFD socket) : socket_{std::move(socket)} {}
+
+ SocketSender(SocketSender&&) = default;
+ SocketSender& operator=(SocketSender&&) = default;
+
+ SocketSender(const SocketSender&&) = delete;
+ SocketSender& operator=(const SocketSender&) = delete;
+
+ ~SocketSender() {
+ if (socket_.operator->()) { // check that socket_ was not moved-from
+ socket_->Shutdown(SHUT_WR);
+ }
+ }
+
+ ssize_t SendAll(const Packet& packet) {
+ ssize_t written{};
+ while (written < static_cast<ssize_t>(packet.payload_length())) {
+ if (!socket_->IsOpen()) {
+ return -1;
+ }
+ auto just_written =
+ socket_->Send(packet.payload() + written,
+ packet.payload_length() - written, MSG_NOSIGNAL);
+ if (just_written <= 0) {
+ LOG(INFO) << "Couldn't write to client: "
+ << strerror(socket_->GetErrno());
+ return just_written;
+ }
+ written += just_written;
+ }
+ return written;
+ }
+
+ private:
+ cvd::SharedFD socket_;
+};
+
+class SocketReceiver {
+ public:
+ explicit SocketReceiver(cvd::SharedFD socket) : socket_{std::move(socket)} {}
+
+ SocketReceiver(SocketReceiver&&) = default;
+ SocketReceiver& operator=(SocketReceiver&&) = default;
+
+ SocketReceiver(const SocketReceiver&&) = delete;
+ SocketReceiver& operator=(const SocketReceiver&) = delete;
+
+ // *packet will be empty if Read returns 0 or error
+ void Recv(Packet* packet) {
+ auto size = socket_->Read(packet->payload(), sizeof packet->payload());
+ if (size < 0) {
+ size = 0;
+ }
+ packet->set_payload_length(size);
+ }
+
+ private:
+ cvd::SharedFD socket_;
+};
+
+void SocketToShm(SocketReceiver socket_receiver,
+ SocketForwardRegionView::ShmSender shm_sender) {
+ while (true) {
+ auto packet = Packet::MakeData();
+ socket_receiver.Recv(&packet);
+ if (packet.empty() || !shm_sender.Send(packet)) {
+ break;
+ }
+ }
+ LOG(INFO) << "Socket to shm exiting";
+}
+
+void ShmToSocket(SocketSender socket_sender,
+ SocketForwardRegionView::ShmReceiver shm_receiver) {
+ auto packet = Packet{};
+ while (true) {
+ shm_receiver.Recv(&packet);
+ CHECK(packet.IsData());
+ if (packet.empty()) {
+ break;
+ }
+ if (socket_sender.SendAll(packet) < 0) {
+ break;
+ }
+ }
+ LOG(INFO) << "Shm to socket exiting";
+}
+
+// One thread for reading from shm and writing into a socket.
+// One thread for reading from a socket and writing into shm.
+void HandleConnection(SocketForwardRegionView::ShmSenderReceiverPair shm_sender_and_receiver,
+ cvd::SharedFD socket) {
+ auto socket_to_shm =
+ std::thread(SocketToShm, SocketReceiver{socket}, std::move(shm_sender_and_receiver.first));
+ ShmToSocket(SocketSender{socket}, std::move(shm_sender_and_receiver.second));
+ socket_to_shm.join();
+}
+
+#ifdef CUTTLEFISH_HOST
+struct PortPair {
+ int guest_port;
+ int host_port;
+};
+
+enum class QueueState {
+ kFree,
+ kUsed,
+};
+
+struct SocketConnectionInfo {
+ std::mutex lock{};
+ std::condition_variable cv{};
+ cvd::SharedFD socket{};
+ int guest_port{};
+ QueueState state = QueueState::kFree;
+};
+
+static constexpr auto kNumHostThreads =
+ vsoc::layout::socket_forward::kNumQueues;
+
+using SocketConnectionInfoCollection =
+ std::array<SocketConnectionInfo, kNumHostThreads>;
+
+void MarkAsFree(SocketConnectionInfo* conn) {
+ std::lock_guard<std::mutex> guard{conn->lock};
+ conn->socket = cvd::SharedFD{};
+ conn->guest_port = 0;
+ conn->state = QueueState::kFree;
+}
+
+std::pair<int, cvd::SharedFD> WaitForConnection(SocketConnectionInfo* conn) {
+ std::unique_lock<std::mutex> guard{conn->lock};
+ while (conn->state != QueueState::kUsed) {
+ conn->cv.wait(guard);
+ }
+ return {conn->guest_port, conn->socket};
+}
+
+[[noreturn]] void host_thread(SocketForwardRegionView::ShmConnectionView view,
+ SocketConnectionInfo* conn) {
+ while (true) {
+ int guest_port{};
+ cvd::SharedFD socket{};
+ // TODO structured binding in C++17
+ std::tie(guest_port, socket) = WaitForConnection(conn);
+
+ LOG(INFO) << "Establishing connection to guest port " << guest_port
+ << " with connection_id: " << view.connection_id();
+ HandleConnection(view.EstablishConnection(guest_port), std::move(socket));
+ LOG(INFO) << "Connection to guest port " << guest_port
+ << " closed. Marking queue " << view.connection_id()
+ << " as free.";
+ MarkAsFree(conn);
+ }
+}
+
+bool TryAllocateConnection(SocketConnectionInfo* conn, int guest_port,
+ cvd::SharedFD socket) {
+ bool success = false;
+ {
+ std::lock_guard<std::mutex> guard{conn->lock};
+ if (conn->state == QueueState::kFree) {
+ conn->socket = std::move(socket);
+ conn->guest_port = guest_port;
+ conn->state = QueueState::kUsed;
+ success = true;
+ }
+ }
+ if (success) {
+ conn->cv.notify_one();
+ }
+ return success;
+}
+
+void AllocateWorkers(cvd::SharedFD socket,
+ SocketConnectionInfoCollection* socket_connection_info,
+ int guest_port) {
+ while (true) {
+ for (auto& conn : *socket_connection_info) {
+ if (TryAllocateConnection(&conn, guest_port, socket)) {
+ return;
+ }
+ }
+ LOG(INFO) << "no queues available. sleeping and retrying";
+ sleep(5);
+ }
+}
+
+[[noreturn]] void host_impl(
+ SocketForwardRegionView* shm,
+ SocketConnectionInfoCollection* socket_connection_info,
+ std::vector<PortPair> ports, std::size_t index) {
+ // launch a worker for the following port before handling the current port.
+ // recursion (instead of a loop) removes the need fore any join() or having
+ // the main thread do no work.
+ if (index + 1 < ports.size()) {
+ std::thread(host_impl, shm, socket_connection_info, ports, index + 1)
+ .detach();
+ }
+ auto guest_port = ports[index].guest_port;
+ auto host_port = ports[index].host_port;
+ LOG(INFO) << "starting server on " << host_port << " for guest port "
+ << guest_port;
+ auto server = cvd::SharedFD::SocketLocalServer(host_port, SOCK_STREAM);
+ CHECK(server->IsOpen()) << "Could not start server on port " << host_port;
+ while (true) {
+ LOG(INFO) << "waiting for client connection";
+ auto client_socket = cvd::SharedFD::Accept(*server);
+ CHECK(client_socket->IsOpen()) << "error creating client socket";
+ LOG(INFO) << "client socket accepted";
+ AllocateWorkers(std::move(client_socket), socket_connection_info,
+ guest_port);
+ }
+}
+
+[[noreturn]] void host(SocketForwardRegionView* shm,
+ std::vector<PortPair> ports) {
+ CHECK(!ports.empty());
+
+ SocketConnectionInfoCollection socket_connection_info{};
+
+ auto conn_info_iter = std::begin(socket_connection_info);
+ for (auto& shm_connection_view : shm->AllConnections()) {
+ CHECK_NE(conn_info_iter, std::end(socket_connection_info));
+ std::thread(host_thread, std::move(shm_connection_view), &*conn_info_iter)
+ .detach();
+ ++conn_info_iter;
+ }
+ CHECK_EQ(conn_info_iter, std::end(socket_connection_info));
+ host_impl(shm, &socket_connection_info, ports, 0);
+}
+
+std::vector<PortPair> ParsePortsList(const std::string& guest_ports_str,
+ const std::string& host_ports_str) {
+ std::vector<PortPair> ports{};
+ auto guest_ports = cvd::StrSplit(guest_ports_str, ',');
+ auto host_ports = cvd::StrSplit(host_ports_str, ',');
+ CHECK(guest_ports.size() == host_ports.size());
+ for (std::size_t i = 0; i < guest_ports.size(); ++i) {
+ ports.push_back({std::stoi(guest_ports[i]), std::stoi(host_ports[i])});
+ }
+ return ports;
+}
+
+#else
+cvd::SharedFD OpenSocketConnection(int port) {
+ while (true) {
+ auto sock = cvd::SharedFD::SocketLocalClient(port, SOCK_STREAM);
+ if (sock->IsOpen()) {
+ return sock;
+ }
+ LOG(WARNING) << "could not connect on port " << port
+ << ". sleeping for 1 second";
+ sleep(1);
+ }
+}
+
+[[noreturn]] void guest_thread(
+ SocketForwardRegionView::ShmConnectionView view) {
+ while (true) {
+ LOG(INFO) << "waiting for new connection";
+ auto shm_sender_and_receiver = view.WaitForNewConnection();
+ LOG(INFO) << "new connection for port " << view.port();
+ HandleConnection(std::move(shm_sender_and_receiver), OpenSocketConnection(view.port()));
+ LOG(INFO) << "connection closed on port " << view.port();
+ }
+}
+
+[[noreturn]] void guest(SocketForwardRegionView* shm) {
+ LOG(INFO) << "Starting guest mainloop";
+ auto connection_views = shm->AllConnections();
+ for (auto&& shm_connection_view : connection_views) {
+ std::thread(guest_thread, std::move(shm_connection_view)).detach();
+ }
+ while (true) {
+ sleep(std::numeric_limits<unsigned int>::max());
+ }
+}
+
+#endif
+
+SocketForwardRegionView* GetShm() {
+ auto shm = SocketForwardRegionView::GetInstance(
+#ifdef CUTTLEFISH_HOST
+ vsoc::GetDomain().c_str()
+#endif
+ );
+ if (!shm) {
+ LOG(FATAL) << "Could not open SHM. Aborting.";
+ }
+ shm->CleanUpPreviousConnections();
+ return shm;
+}
+
+// makes sure we're running as root on the guest, no-op on the host
+void assert_correct_user() {
+#ifndef CUTTLEFISH_HOST
+ CHECK_EQ(getuid(), 0u) << "must run as root!";
+#endif
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+ assert_correct_user();
+
+ auto shm = GetShm();
+ auto worker = shm->StartWorker();
+
+#ifdef CUTTLEFISH_HOST
+ CHECK(!FLAGS_guest_ports.empty()) << "Must specify --guest_ports flag";
+ CHECK(!FLAGS_host_ports.empty()) << "Must specify --host_ports flag";
+ host(shm, ParsePortsList(FLAGS_guest_ports, FLAGS_host_ports));
+#else
+ guest(shm);
+#endif
+}
diff --git a/common/frontend/socket_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
index 31cff0b..ac421a3 100644
--- a/common/frontend/socket_vsock_proxy/main.cpp
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -20,103 +20,13 @@
#include <gflags/gflags.h>
#include "common/libs/fs/shared_fd.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
#ifdef CUTTLEFISH_HOST
#include "host/libs/config/cuttlefish_config.h"
#endif
-struct Header {
- std::uint32_t payload_length;
- enum MessageType : std::uint32_t {
- DATA = 0,
- BEGIN,
- END,
- RECV_CLOSED, // indicate that this side's receive end is closed
- RESTART,
- };
- MessageType message_type;
-};
-
-constexpr std::size_t kMaxPacketSize = 8192;
-constexpr std::size_t kMaxPayloadSize = kMaxPacketSize - sizeof(Header);
-
-struct Packet {
- private:
- Header header_;
- using Payload = char[kMaxPayloadSize];
- Payload payload_data_;
-
- static constexpr Packet MakePacket(Header::MessageType type) {
- Packet packet{};
- packet.header_.message_type = type;
- return packet;
- }
-
- public:
- // port is only revelant on the host-side.
- static Packet MakeBegin(std::uint16_t port);
-
- static constexpr Packet MakeEnd() { return MakePacket(Header::END); }
-
- static constexpr Packet MakeRecvClosed() {
- return MakePacket(Header::RECV_CLOSED);
- }
-
- static constexpr Packet MakeRestart() { return MakePacket(Header::RESTART); }
-
- // NOTE payload and payload_length must still be set.
- static constexpr Packet MakeData() { return MakePacket(Header::DATA); }
-
- bool empty() const { return IsData() && header_.payload_length == 0; }
-
- void set_payload_length(std::uint32_t length) {
- CHECK_LE(length, sizeof payload_data_);
- header_.payload_length = length;
- }
-
- Payload& payload() { return payload_data_; }
-
- const Payload& payload() const { return payload_data_; }
-
- constexpr std::uint32_t payload_length() const {
- return header_.payload_length;
- }
-
- constexpr bool IsBegin() const {
- return header_.message_type == Header::BEGIN;
- }
-
- constexpr bool IsEnd() const { return header_.message_type == Header::END; }
-
- constexpr bool IsData() const { return header_.message_type == Header::DATA; }
-
- constexpr bool IsRecvClosed() const {
- return header_.message_type == Header::RECV_CLOSED;
- }
-
- constexpr bool IsRestart() const {
- return header_.message_type == Header::RESTART;
- }
-
- constexpr std::uint16_t port() const {
- CHECK(IsBegin());
- std::uint16_t port_number{};
- CHECK_EQ(payload_length(), sizeof port_number);
- std::memcpy(&port_number, payload(), sizeof port_number);
- return port_number;
- }
-
- char* raw_data() { return reinterpret_cast<char*>(this); }
-
- const char* raw_data() const { return reinterpret_cast<const char*>(this); }
-
- constexpr size_t raw_data_length() const {
- return payload_length() + sizeof header_;
- }
-};
-
-static_assert(sizeof(Packet) == kMaxPacketSize, "");
-static_assert(std::is_pod<Packet>{}, "");
+using vsoc::socket_forward::Packet;
DEFINE_uint32(tcp_port, 0, "TCP port (server on host, client on guest)");
DEFINE_uint32(vsock_port, 0, "vsock port (client on host, server on guest");
diff --git a/common/vsoc/lib/audio_data_layout.cpp b/common/vsoc/lib/audio_data_layout.cpp
new file mode 100644
index 0000000..4b5172c
--- /dev/null
+++ b/common/vsoc/lib/audio_data_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/audio_data_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace audio_data {
+
+// static
+const char *const AudioDataLayout::region_name = "audio_data";
+
+} // namespace audio_data
+} // namespace layout
+} // namespace vsoc
+
diff --git a/common/vsoc/lib/audio_data_region_view.h b/common/vsoc/lib/audio_data_region_view.h
new file mode 100644
index 0000000..42d05df
--- /dev/null
+++ b/common/vsoc/lib/audio_data_region_view.h
@@ -0,0 +1,39 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 <memory>
+
+#include <android-base/macros.h>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/audio_data_layout.h"
+
+namespace vsoc {
+namespace audio_data {
+
+class AudioDataRegionView
+ : public vsoc::TypedRegionView<
+ AudioDataRegionView,
+ vsoc::layout::audio_data::AudioDataLayout> {
+public:
+ AudioDataRegionView() = default;
+ AudioDataRegionView(const AudioDataRegionView &) = delete;
+ AudioDataRegionView &operator=(const AudioDataRegionView &) = delete;
+};
+
+} // namespace audio_data
+} // namespace vsoc
diff --git a/common/vsoc/lib/circqueue_impl.h b/common/vsoc/lib/circqueue_impl.h
new file mode 100644
index 0000000..8b0fa90
--- /dev/null
+++ b/common/vsoc/lib/circqueue_impl.h
@@ -0,0 +1,234 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <cerrno>
+#include <cstring>
+
+#include <sys/uio.h>
+
+#include "common/vsoc/lib/region_signaling_interface.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Increases the given index until it is naturally aligned for T.
+template <typename T>
+uintptr_t align(uintptr_t index) {
+ return (index + sizeof(T) - 1) & ~(sizeof(T) - 1);
+}
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyInRange(const char* buffer_in,
+ const Range& t) {
+ size_t bytes = t.end_idx - t.start_idx;
+ uint32_t index = t.start_idx & (BufferSize - 1);
+ if (index + bytes <= BufferSize) {
+ std::memcpy(buffer_ + index, buffer_in, bytes);
+ } else {
+ size_t part1_size = BufferSize - index;
+ size_t part2_size = bytes - part1_size;
+ std::memcpy(buffer_ + index, buffer_in, part1_size);
+ std::memcpy(buffer_, buffer_in + part1_size, part2_size);
+ }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::CopyOutRange(const Range& t,
+ char* buffer_out) {
+ uint32_t index = t.start_idx & (BufferSize - 1);
+ size_t total_size = t.end_idx - t.start_idx;
+ if (index + total_size <= BufferSize) {
+ std::memcpy(buffer_out, buffer_ + index, total_size);
+ } else {
+ uint32_t part1_size = BufferSize - index;
+ uint32_t part2_size = total_size - part1_size;
+ std::memcpy(buffer_out, buffer_ + index, part1_size);
+ std::memcpy(buffer_out + part1_size, buffer_, part2_size);
+ }
+}
+
+template <uint32_t SizeLog2>
+void CircularQueueBase<SizeLog2>::WaitForDataLocked(
+ RegionSignalingInterface* r) {
+ while (1) {
+ uint32_t o_w_pub = w_pub_;
+ // We don't have data. Wait until some appears and try again
+ if (r_released_ != o_w_pub) {
+ return;
+ }
+ lock_.Unlock();
+ r->WaitForSignal(&w_pub_, o_w_pub);
+ lock_.Lock();
+ }
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularQueueBase<SizeLog2>::WriteReserveLocked(
+ RegionSignalingInterface* r, size_t bytes, Range* t, bool non_blocking) {
+ // Can't write more than the buffer will hold
+ if (bytes > BufferSize) {
+ return -ENOSPC;
+ }
+ while (true) {
+ uint32_t o_w_pub = w_pub_;
+ uint32_t o_r_release = r_released_;
+ uint32_t bytes_in_use = o_w_pub - o_r_release;
+ size_t available = BufferSize - bytes_in_use;
+ if (available >= bytes) {
+ t->start_idx = o_w_pub;
+ t->end_idx = o_w_pub + bytes;
+ break;
+ }
+ if (non_blocking) {
+ return -EWOULDBLOCK;
+ }
+ // If we can't write at the moment wait for a reader to release
+ // some bytes.
+ lock_.Unlock();
+ r->WaitForSignal(&r_released_, o_r_release);
+ lock_.Lock();
+ }
+ return t->end_idx - t->start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Read(RegionSignalingInterface* r,
+ char* buffer_out, size_t max_size) {
+ this->lock_.Lock();
+ this->WaitForDataLocked(r);
+ Range t;
+ t.start_idx = this->r_released_;
+ t.end_idx = this->w_pub_;
+ // The lock is still held here...
+ // Trim the range if we got more than the reader wanted
+ if ((t.end_idx - t.start_idx) > max_size) {
+ t.end_idx = t.start_idx + max_size;
+ }
+ this->CopyOutRange(t, buffer_out);
+ this->r_released_ = t.end_idx;
+ this->lock_.Unlock();
+ r->SendSignal(layout::Sides::Both, &this->r_released_);
+ return t.end_idx - t.start_idx;
+}
+
+template <uint32_t SizeLog2>
+intptr_t CircularByteQueue<SizeLog2>::Write(RegionSignalingInterface* r,
+ const char* buffer_in, size_t bytes,
+ bool non_blocking) {
+ Range range;
+ this->lock_.Lock();
+ intptr_t rval = this->WriteReserveLocked(r, bytes, &range, non_blocking);
+ if (rval < 0) {
+ this->lock_.Unlock();
+ return rval;
+ }
+ this->CopyInRange(buffer_in, range);
+ // We can't publish until all of the previous write allocations where
+ // published.
+ this->w_pub_ = range.end_idx;
+ this->lock_.Unlock();
+ r->SendSignal(layout::Sides::Both, &this->w_pub_);
+ return bytes;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::CalculateBufferedSize(
+ size_t payload) {
+ return align<uint32_t>(sizeof(uint32_t) + payload);
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Read(
+ RegionSignalingInterface* r, char* buffer_out, size_t max_size) {
+ this->lock_.Lock();
+ this->WaitForDataLocked(r);
+ uint32_t packet_size = *reinterpret_cast<uint32_t*>(
+ this->buffer_ + (this->r_released_ & (this->BufferSize - 1)));
+ if (packet_size > max_size) {
+ this->lock_.Unlock();
+ return -ENOSPC;
+ }
+ Range t;
+ t.start_idx = this->r_released_ + sizeof(uint32_t);
+ t.end_idx = t.start_idx + packet_size;
+ this->CopyOutRange(t, buffer_out);
+ this->r_released_ += this->CalculateBufferedSize(packet_size);
+ this->lock_.Unlock();
+ r->SendSignal(layout::Sides::Both, &this->r_released_);
+ return packet_size;
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Write(
+ RegionSignalingInterface* r, const char* buffer_in, uint32_t bytes,
+ bool non_blocking) {
+ iovec iov;
+ iov.iov_base = const_cast<char *>(buffer_in);
+ iov.iov_len = bytes;
+ return Writev(r, &iov, 1 /* iov_count */, non_blocking);
+}
+
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+intptr_t CircularPacketQueue<SizeLog2, MaxPacketSize>::Writev(
+ RegionSignalingInterface *r,
+ const iovec *iov,
+ size_t iov_count,
+ bool non_blocking) {
+ size_t bytes = 0;
+ for (size_t i = 0; i < iov_count; ++i) {
+ bytes += iov[i].iov_len;
+ }
+
+ if (bytes > MaxPacketSize) {
+ return -ENOSPC;
+ }
+
+ Range range;
+ size_t buffered_size = this->CalculateBufferedSize(bytes);
+ this->lock_.Lock();
+ intptr_t rval =
+ this->WriteReserveLocked(r, buffered_size, &range, non_blocking);
+ if (rval < 0) {
+ this->lock_.Unlock();
+ return rval;
+ }
+ Range header = range;
+ header.end_idx = header.start_idx + sizeof(uint32_t);
+ Range payload{
+ static_cast<uint32_t>(range.start_idx + sizeof(uint32_t)),
+ static_cast<uint32_t>(range.start_idx + sizeof(uint32_t) + bytes)};
+ this->CopyInRange(reinterpret_cast<const char*>(&bytes), header);
+
+ Range subRange = payload;
+ for (size_t i = 0; i < iov_count; ++i) {
+ subRange.end_idx = subRange.start_idx + iov[i].iov_len;
+ this->CopyInRange(static_cast<const char *>(iov[i].iov_base), subRange);
+
+ subRange.start_idx = subRange.end_idx;
+ }
+
+ this->w_pub_ = range.end_idx;
+ this->lock_.Unlock();
+ r->SendSignal(layout::Sides::Both, &this->w_pub_);
+ return bytes;
+}
+
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/lib/circqueue_test.cpp b/common/vsoc/lib/circqueue_test.cpp
new file mode 100644
index 0000000..34aca33
--- /dev/null
+++ b/common/vsoc/lib/circqueue_test.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2017 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 <thread>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/mock_region_view.h"
+
+#define EXPECT_BLOCK(region, tid) \
+ EXPECT_TRUE(region.IsBlocking(tid))
+
+namespace {
+
+constexpr int kQueueSizeLog2 = 16;
+constexpr int kQueueCapacity = 1 << kQueueSizeLog2;
+constexpr int kMaxPacketSize = 1024;
+
+constexpr int kNumReadingThread = 5;
+constexpr int kNumWritingThread = 5;
+
+struct CircQueueTestRegionLayout : public vsoc::layout::RegionLayout {
+ vsoc::layout::CircularByteQueue<kQueueSizeLog2> byte_queue;
+ vsoc::layout::CircularPacketQueue<kQueueSizeLog2, kMaxPacketSize> packet_queue;
+};
+
+typedef vsoc::test::MockRegionView<CircQueueTestRegionLayout> CircQueueRegionView;
+
+class CircQueueTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ region_.Open();
+ }
+
+ CircQueueRegionView region_;
+};
+
+intptr_t ReadBytes(CircQueueRegionView* region, int bytes) {
+ char buffer_out[bytes];
+ CircQueueTestRegionLayout* layout = region->data();
+ return layout->byte_queue.Read(region, buffer_out, bytes);
+}
+
+intptr_t WriteBytes(CircQueueRegionView* region, int bytes) {
+ char buffer_in[bytes];
+ CircQueueTestRegionLayout* layout = region->data();
+ return layout->byte_queue.Write(region, buffer_in, bytes);
+}
+
+intptr_t ReadPacket(CircQueueRegionView* region, int max_size) {
+ char buffer_out[max_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ return layout->packet_queue.Read(region, buffer_out, max_size);
+}
+
+intptr_t WritePacket(CircQueueRegionView* region, int packet_size) {
+ char buffer_in[packet_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ return layout->packet_queue.Write(region, buffer_in, packet_size);
+}
+
+void ReadBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+ char buffer_out[chuck_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ int total_read = 0;
+ int remaining = total_size;
+ while (remaining >= chuck_size) {
+ int ret = layout->byte_queue.Read(region, buffer_out, chuck_size);
+ total_read += ret;
+ remaining -= ret;
+ }
+ if (remaining > 0) {
+ total_read += layout->byte_queue.Write(region, buffer_out, remaining);
+ }
+ EXPECT_EQ(total_read, total_size);
+}
+
+void WriteBytesInChunk(CircQueueRegionView* region, int total_size, int chuck_size) {
+ char buffer_in[chuck_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ int total_write = 0;
+ int remaining = total_size;
+ while (remaining >= chuck_size) {
+ int ret = layout->byte_queue.Write(region, buffer_in, chuck_size);
+ total_write += ret;
+ remaining -= ret;
+ }
+ if (remaining > 0) {
+ total_write += layout->byte_queue.Write(region, buffer_in, remaining);
+ }
+ EXPECT_EQ(total_write, total_size);
+}
+
+void ReadManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+ char buffer_out[packet_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ int total_read = 0;
+ int remaining = num_packets;
+ while (remaining > 0) {
+ int ret = layout->packet_queue.Read(region, buffer_out, packet_size);
+ total_read += ret;
+ remaining--;
+ }
+ EXPECT_EQ(total_read, num_packets * packet_size);
+}
+
+void WriteManyPackets(CircQueueRegionView* region, int num_packets, int packet_size) {
+ char buffer_in[packet_size];
+ CircQueueTestRegionLayout* layout = region->data();
+ int total_write = 0;
+ int remaining = num_packets;
+ while (remaining > 0) {
+ int ret = layout->packet_queue.Write(region, buffer_in, packet_size);
+ total_write += ret;
+ remaining--;
+ }
+ EXPECT_EQ(total_write, num_packets * packet_size);
+}
+
+// ByteQueue Tests
+
+// Test writing bytes
+TEST_F(CircQueueTest, ByteQueueSimpleWrite) {
+ const int num_bytes = 8;
+ EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test reading bytes
+TEST_F(CircQueueTest, ByteQueueSimpleRead) {
+ const int num_bytes = 8;
+ EXPECT_EQ(num_bytes, WriteBytes(&this->region_, num_bytes));
+ EXPECT_EQ(num_bytes, ReadBytes(&this->region_, num_bytes));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueReadOnEmpty) {
+ const int num_bytes = 8;
+
+ // Spawn a thread to read from queue. Expect it to block.
+ std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+ EXPECT_BLOCK(region_, reading_thread.get_id());
+
+ // Write expected bytes in so that we can clean up properly.
+ std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+ writing_thread.join();
+
+ reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, ByteQueueWriteOnFull) {
+ // Fill the queue.
+ const int capacity_bytes = kQueueCapacity;
+ EXPECT_EQ(capacity_bytes, WriteBytes(&this->region_, capacity_bytes));
+
+ // Now the queue is full, any further write would block.
+ const int num_bytes = 8;
+ std::thread writing_thread(WriteBytes, &this->region_, num_bytes);
+ EXPECT_BLOCK(region_, writing_thread.get_id());
+
+ // Read the extra bytes out so that we can clean up properly.
+ std::thread reading_thread(ReadBytes, &this->region_, num_bytes);
+ reading_thread.join();
+
+ writing_thread.join();
+}
+
+// Test if bytes being read out are the same as ones being written in.
+TEST_F(CircQueueTest, ByteQueueContentIntegrity) {
+ const int num_bytes = 8;
+ CircQueueTestRegionLayout* layout = this->region_.data();
+
+ char buffer_in[num_bytes] = {'a'};
+ layout->byte_queue.Write(&this->region_, buffer_in, num_bytes);
+
+ char buffer_out[num_bytes] = {'b'};
+ layout->byte_queue.Read(&this->region_, buffer_out, num_bytes);
+
+ for (int i=0; i<num_bytes; i++) {
+ EXPECT_EQ(buffer_in[i], buffer_out[i]);
+ }
+}
+
+// Test writing more bytes than capacity
+TEST_F(CircQueueTest, ByteQueueWriteTooManyBytes) {
+ const int extra_bytes = 8;
+ const int num_bytes = kQueueCapacity + extra_bytes;
+ EXPECT_EQ(-ENOSPC, WriteBytes(&this->region_, num_bytes));
+}
+
+// Test multiple bytes read/write
+TEST_F(CircQueueTest, ByteQueueMultipleReadWrite) {
+ const int chunk_size = 7;
+ const int total_size = 3.3 * kQueueCapacity;
+ std::vector<std::thread> reading_threads;
+ std::vector<std::thread> writing_threads;
+ for (int i=0; i<kNumReadingThread; i++) {
+ reading_threads.emplace_back(
+ std::thread(ReadBytesInChunk, &this->region_, total_size, chunk_size));
+ }
+ for (int i=0; i<kNumWritingThread; i++) {
+ writing_threads.emplace_back(
+ std::thread(WriteBytesInChunk, &this->region_, total_size, chunk_size));
+ }
+ std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+ std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+// PacketQueue Tests
+
+// Test writing packet
+TEST_F(CircQueueTest, PacketQueueSimpleWrite) {
+ const int packet_size = 8;
+ EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet
+TEST_F(CircQueueTest, PacketQueueSimpleRead) {
+ const int packet_size = 8;
+ EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+ EXPECT_EQ(packet_size, ReadPacket(&this->region_, packet_size));
+}
+
+// Test reading on an empty queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueReadOnEmpty) {
+ const int packet_size = 8;
+
+ // Spawn a thread to read from queue. Expect it to block.
+ std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+ EXPECT_BLOCK(region_, reading_thread.get_id());
+
+ // Write expected bytes in so that we can clean up properly.
+ std::thread writing_thread(WritePacket, &this->region_, packet_size);
+ writing_thread.join();
+
+ reading_thread.join();
+}
+
+// Test writing on a full queue. Expect blocking.
+TEST_F(CircQueueTest, PacketQueueWriteOnFull) {
+ // Fill the queue.
+ const int packet_size = kMaxPacketSize;
+ int capacity_bytes = kQueueCapacity;
+ while (capacity_bytes >= packet_size) {
+ EXPECT_EQ(packet_size, WritePacket(&this->region_, packet_size));
+ capacity_bytes -= (packet_size + sizeof(uint32_t));
+ }
+
+ // Now the queue is full, any further write would block.
+ std::thread writing_thread(WritePacket, &this->region_, packet_size);
+ EXPECT_BLOCK(region_, writing_thread.get_id());
+
+ // Read the extra bytes out so that we can clean up properly.
+ std::thread reading_thread(ReadPacket, &this->region_, packet_size);
+ reading_thread.join();
+
+ writing_thread.join();
+}
+
+// Test if packet being read out are the same as one being written in.
+TEST_F(CircQueueTest, PacketQueueContentIntegrity) {
+ const int packet_size = 8;
+ CircQueueTestRegionLayout* layout = this->region_.data();
+
+ char buffer_in[packet_size] = {'a'};
+ layout->packet_queue.Write(&this->region_, buffer_in, packet_size);
+
+ char buffer_out[packet_size] = {'b'};
+ layout->packet_queue.Read(&this->region_, buffer_out, packet_size);
+
+ for (int i=0; i<packet_size; i++) {
+ EXPECT_EQ(buffer_in[i], buffer_out[i]);
+ }
+}
+
+// Test writing packet larger than capacity
+TEST_F(CircQueueTest, PacketQueueWriteTooLargePacket) {
+ const int extra_bytes = 8;
+ const int packet_size = kQueueCapacity + extra_bytes;
+ EXPECT_EQ(-ENOSPC, WritePacket(&this->region_, packet_size));
+}
+
+// Test reading packet larger than can handle
+TEST_F(CircQueueTest, PacketQueueReadTooLargePacket) {
+ const int extra_bytes = 8;
+ const int small_packet = 8;
+ const int large_packet = small_packet + extra_bytes;
+
+ WritePacket(&this->region_, large_packet);
+ char buffer_out[small_packet];
+ CircQueueTestRegionLayout* layout = this->region_.data();
+ EXPECT_EQ(-ENOSPC, layout->packet_queue.Read(&this->region_, buffer_out, small_packet));
+}
+
+// Test multiple packets read/write
+TEST_F(CircQueueTest, PacketQueueMultipleReadWrite) {
+ const int packet_size = kMaxPacketSize;
+ const int num_packets = 1.5 * (kQueueCapacity / packet_size);
+ std::vector<std::thread> reading_threads;
+ std::vector<std::thread> writing_threads;
+ for (int i=0; i<kNumReadingThread; i++) {
+ reading_threads.emplace_back(
+ std::thread(ReadManyPackets, &this->region_, num_packets, packet_size));
+ }
+ for (int i=0; i<kNumWritingThread; i++) {
+ writing_threads.emplace_back(
+ std::thread(WriteManyPackets, &this->region_, num_packets, packet_size));
+ }
+ std::for_each(reading_threads.begin(), reading_threads.end(), [](std::thread& t) { t.join(); });
+ std::for_each(writing_threads.begin(), writing_threads.end(), [](std::thread& t) { t.join(); });
+}
+
+} // namespace
diff --git a/common/vsoc/lib/compat.cpp b/common/vsoc/lib/compat.cpp
new file mode 100644
index 0000000..3b8deb2
--- /dev/null
+++ b/common/vsoc/lib/compat.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/compat.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+namespace vsoc {
+
+#ifdef CUTTLEFISH_HOST
+uint32_t gettid() {
+ thread_local uint32_t tid = syscall(SYS_gettid);
+ return tid;
+}
+#endif
+}
diff --git a/common/vsoc/lib/compat.h b/common/vsoc/lib/compat.h
new file mode 100644
index 0000000..8f3355c
--- /dev/null
+++ b/common/vsoc/lib/compat.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Utilities to make the host and the guest a bit more compatible
+
+#include <stdint.h>
+
+namespace vsoc {
+#ifdef CUTTLEFISH_HOST
+// Things that are missing on the host
+uint32_t gettid();
+#endif
+}
diff --git a/common/vsoc/lib/e2e_test_region_layout.cpp b/common/vsoc/lib/e2e_test_region_layout.cpp
new file mode 100644
index 0000000..9a787bd
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_layout.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace e2e_test {
+
+const char* E2EPrimaryTestRegionLayout::region_name = "e2e_primary";
+const char
+ E2EPrimaryTestRegionLayout::guest_pattern[E2EMemoryFill::kOwnedFieldSize] =
+ "primary guest e2e string";
+const char
+ E2EPrimaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+ "primary host e2e string";
+
+const char* E2ESecondaryTestRegionLayout::region_name = "e2e_secondary";
+const char E2ESecondaryTestRegionLayout::guest_pattern
+ [E2EMemoryFill::kOwnedFieldSize] = "secondary guest e2e string";
+const char
+ E2ESecondaryTestRegionLayout::host_pattern[E2EMemoryFill::kOwnedFieldSize] =
+ "secondary host e2e string";
+
+const char* E2EUnfindableRegionLayout::region_name = "e2e_must_not_exist";
+
+} // namespace e2e_test
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/lib/e2e_test_region_view.h b/common/vsoc/lib/e2e_test_region_view.h
new file mode 100644
index 0000000..70a3db6
--- /dev/null
+++ b/common/vsoc/lib/e2e_test_region_view.h
@@ -0,0 +1,104 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <functional>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+
+namespace vsoc {
+template <typename Layout>
+class E2ERegionView : public vsoc::TypedRegionView<
+ E2ERegionView<Layout>,
+ Layout> {
+ public:
+
+ uint32_t read_guest_self_register() const {
+ return this->data().guest_self_register;
+ }
+
+ void write_guest_self_register(uint32_t val) {
+ this->data()->guest_self_register = val;
+ }
+
+ void signal_guest_self_register() {
+ this->SendSignal(layout::Sides::OurSide,
+ &this->data()->guest_self_register);
+ }
+
+ int wait_guest_self_register(uint32_t expected_value) {
+ return this->WaitForSignal(
+ &this->data()->guest_self_register, expected_value);
+ }
+
+ void signal_guest_to_host_register() {
+ this->SendSignal(layout::Sides::OurSide,
+ &this->data()->guest_to_host_signal);
+ }
+
+ uint32_t guest_to_host_signal_offset() const {
+ return this->pointer_to_region_offset(&this->data().guest_to_host_signal);
+ }
+
+ uint32_t host_to_guest_signal_offset() const {
+ return this->pointer_to_region_offset(&this->data().host_to_guest_signal);
+ }
+
+ const char* guest_string(size_t index) const {
+ return const_cast<const char*>(this->data().data[index].guest_writable);
+ }
+
+ const char* host_string(size_t index) const {
+ return const_cast<const char*>(this->data().data[index].host_writable);
+ }
+
+ bool set_guest_string(size_t index, const char* value) {
+ strcpy(const_cast<char*>(this->data()->data[index].guest_writable),
+ value);
+ return true;
+ }
+
+ bool set_host_string(size_t index, const char* value) {
+ strcpy(const_cast<char*>(this->data()->data[index].host_writable),
+ value);
+ return true;
+ }
+
+ size_t string_size() const {
+ return Layout::NumFillRecords(this->control_->region_data_size());
+ }
+
+ void guest_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+ this->data()->guest_status.set_value(stage);
+ }
+
+ void host_status(vsoc::layout::e2e_test::E2ETestStage stage) {
+ this->data()->host_status.set_value(stage);
+ }
+};
+
+using E2EPrimaryRegionView =
+ vsoc::E2ERegionView<layout::e2e_test::E2EPrimaryTestRegionLayout>;
+
+using E2ESecondaryRegionView =
+ vsoc::E2ERegionView<layout::e2e_test::E2ESecondaryTestRegionLayout>;
+
+using E2EUnfindableRegionView =
+ vsoc::E2ERegionView<layout::e2e_test::E2EUnfindableRegionLayout>;
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/gralloc_layout.cpp b/common/vsoc/lib/gralloc_layout.cpp
new file mode 100644
index 0000000..9be790a
--- /dev/null
+++ b/common/vsoc/lib/gralloc_layout.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/gralloc_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+const char* GrallocManagerLayout::region_name = "gralloc_manager";
+const char* GrallocBufferLayout::region_name = "gralloc_memory";
+
+} // gralloc
+} // layout
+} // vsoc
diff --git a/common/vsoc/lib/graphics_common.h b/common/vsoc/lib/graphics_common.h
new file mode 100644
index 0000000..d2db6c4
--- /dev/null
+++ b/common/vsoc/lib/graphics_common.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/graphics.h"
+
+namespace vsoc {
+
+// Returns BytesPerPixel given a PixelFormat. Code should rely on this rather
+// than accessing the mask directly.
+static inline std::size_t BytesPerPixel(PixelFormat in) {
+ return 1 + (in >> PixelFormatBase::SubformatSize) &
+ (PixelFormatBase::MaxBytesPerPixel - 1);
+}
+} // vsoc
diff --git a/common/vsoc/lib/graphics_test.cpp b/common/vsoc/lib/graphics_test.cpp
new file mode 100644
index 0000000..b43e56e
--- /dev/null
+++ b/common/vsoc/lib/graphics_test.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/graphics.h"
+
+#include <gtest/gtest.h>
+
+TEST(GraphicsTest, Basic) {
+ // This is a shared memory layout and is mostly tested via static asserts.
+ // If this can compile then the test has passed.
+}
diff --git a/common/vsoc/lib/input_events_layout.cpp b/common/vsoc/lib/input_events_layout.cpp
new file mode 100644
index 0000000..3f1da61
--- /dev/null
+++ b/common/vsoc/lib/input_events_layout.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Define some of the string constants associated with the region layout.
+#include "common/vsoc/shm/input_events_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace input_events {
+
+const char* InputEventsLayout::region_name = "input_events";
+
+} // namespace input_events
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/lib/input_events_region_view.cpp b/common/vsoc/lib/input_events_region_view.cpp
new file mode 100644
index 0000000..66bc697
--- /dev/null
+++ b/common/vsoc/lib/input_events_region_view.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/input_events_region_view.h"
+
+#include <linux/input.h>
+#include <linux/uinput.h>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+
+using vsoc::layout::input_events::InputEventsLayout;
+
+namespace vsoc {
+namespace input_events {
+
+namespace {
+void InitInputEvent(InputEvent* evt, uint16_t type, uint16_t code, uint32_t value) {
+ evt->type = type;
+ evt->code = code;
+ evt->value = value;
+}
+} // namespace
+
+const int InputEventsRegionView::kMaxEventsPerPacket = 4;
+
+bool InputEventsRegionView::HandleSingleTouchEvent(bool down, int x, int y) {
+ // TODO(jemoreira): Use multitouch when available
+ InputEvent events[4];
+ // Make sure to modify kMaxEventPerPacket if more events are sent.
+ InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+ InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+ InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, down);
+ InitInputEvent(&events[3], EV_SYN, 0, 0);
+ return 0 <
+ data()->touch_screen_queue.Write(
+ this, reinterpret_cast<char*>(&events[0]), sizeof(events), true);
+}
+
+bool InputEventsRegionView::HandlePowerButtonEvent(bool down) {
+ InputEvent events[2];
+ InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+ return 0 < data()->power_button_queue.Write(
+ this, reinterpret_cast<char*>(&events[0]),
+ sizeof(events), true);
+}
+
+bool InputEventsRegionView::HandleKeyboardEvent(bool down, uint16_t key_code) {
+ InputEvent events[2];
+ InitInputEvent(&events[0], EV_KEY, key_code, down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+ return 0 <
+ data()->keyboard_queue.Write(this, reinterpret_cast<char*>(&events[0]),
+ sizeof(events), true);
+}
+
+intptr_t InputEventsRegionView::GetScreenEventsOrWait(InputEvent* evt,
+ int max_event_count) {
+ intptr_t ret = this->data()->touch_screen_queue.Read(
+ this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+ if (ret < 0) {
+ return ret;
+ }
+ return ret / sizeof(InputEvent);
+}
+
+intptr_t InputEventsRegionView::GetKeyboardEventsOrWait(InputEvent* evt,
+ int max_event_count) {
+ intptr_t ret = this->data()->keyboard_queue.Read(
+ this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+ if (ret < 0) {
+ return ret;
+ }
+ return ret / sizeof(InputEvent);
+}
+
+intptr_t InputEventsRegionView::GetPowerButtonEventsOrWait(
+ InputEvent* evt, int max_event_count) {
+ intptr_t ret = this->data()->power_button_queue.Read(
+ this, reinterpret_cast<char*>(evt), sizeof(InputEvent) * max_event_count);
+ if (ret < 0) {
+ return ret;
+ }
+ return ret / sizeof(InputEvent);
+}
+
+} // namespace input_events
+} // namespace vsoc
diff --git a/common/vsoc/lib/input_events_region_view.h b/common/vsoc/lib/input_events_region_view.h
new file mode 100644
index 0000000..3355631
--- /dev/null
+++ b/common/vsoc/lib/input_events_region_view.h
@@ -0,0 +1,57 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/input_events_layout.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+namespace input_events {
+
+struct InputEvent {
+ uint16_t type;
+ uint16_t code;
+ uint32_t value;
+};
+
+class InputEventsRegionView
+ : public vsoc::TypedRegionView<
+ InputEventsRegionView,
+ vsoc::layout::input_events::InputEventsLayout> {
+ public:
+ static const int kMaxEventsPerPacket;
+ // Generates a touch event, may returns true if successful, false if there was
+ // an error, most likely that the queue is full.
+ bool HandleSingleTouchEvent(bool down, int x, int y);
+ bool HandlePowerButtonEvent(bool down);
+ bool HandleKeyboardEvent(bool down, uint16_t key_code);
+
+ // TODO(jemoreira): HandleMultiTouchEvent()...
+
+ // Read input events from the queue, waits if there are none available.
+ // Returns the number of events read or a negative value in case of an error
+ // (most likely the next packet in the queue is larger than the buffer
+ // provided).
+ intptr_t GetScreenEventsOrWait(InputEvent* buffer, int max_event_count);
+ intptr_t GetKeyboardEventsOrWait(InputEvent* buffer, int max_event_count);
+ intptr_t GetPowerButtonEventsOrWait(InputEvent* buffer, int max_event_count);
+
+};
+} // namespace input_events
+} // namespace vsoc
diff --git a/common/vsoc/lib/lock_common.cpp b/common/vsoc/lib/lock_common.cpp
new file mode 100644
index 0000000..c100574
--- /dev/null
+++ b/common/vsoc/lib/lock_common.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/region_view.h"
+
+#include <stdlib.h>
+
+using vsoc::layout::Sides;
+
+namespace {
+
+const uint32_t LockFree = 0U;
+const uint32_t GuestWaitingFlag = (Sides::Guest << 30);
+const uint32_t HostWaitingFlag = (Sides::Host << 30);
+const uint32_t OurWaitingFlag = (Sides::OurSide << 30);
+static_assert(GuestWaitingFlag, "GuestWaitingFlag is 0");
+static_assert(HostWaitingFlag, "HostWaitingFlag is 0");
+static_assert((GuestWaitingFlag & HostWaitingFlag) == 0,
+ "Waiting flags should not share bits");
+
+// Set if the current owner is the host
+const uint32_t HostIsOwner = 0x20000000U;
+
+// PID_MAX_LIMIT appears to be 0x00400000U, so we're probably ok here
+const uint32_t OwnerMask = 0x3FFFFFFFU;
+
+uint32_t MakeOwnerTid(uint32_t raw_tid) {
+ if (Sides::OurSide == Sides::Host) {
+ return (raw_tid | HostIsOwner) & OwnerMask;
+ } else {
+ return raw_tid & (OwnerMask & ~HostIsOwner);
+ }
+}
+
+}; // namespace
+
+namespace vsoc {
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+bool vsoc::layout::WaitingLockBase::TryLock(uint32_t tid,
+ uint32_t* expected_out) {
+ uint32_t masked_tid = MakeOwnerTid(tid);
+ uint32_t expected = LockFree;
+ while (1) {
+ // First try to lock assuming that the mutex is free
+ if (lock_uint32_.compare_exchange_strong(expected, masked_tid)) {
+ // We got the lock.
+ return true;
+ }
+ // We didn't get the lock and our wait flag is already set. It's safe to
+ // try to sleep
+ if (expected & OurWaitingFlag) {
+ *expected_out = expected;
+ return false;
+ }
+ // Attempt to set the wait flag. This will fail if the lock owner changes.
+ while (1) {
+ uint32_t add_wait_flag = expected | OurWaitingFlag;
+ if (lock_uint32_.compare_exchange_strong(expected, add_wait_flag)) {
+ // We set the waiting flag. Try to sleep.
+ *expected_out = expected;
+ return false;
+ }
+ // The owner changed, but we at least we got the wait flag.
+ // Try sleeping
+ if (expected & OurWaitingFlag) {
+ *expected_out = expected;
+ return false;
+ }
+ // Special case: the lock was just freed. Stop trying to set the
+ // waiting flag and try to grab the lock.
+ if (expected == LockFree) {
+ break;
+ }
+ // The owner changed and we have no wait flag
+ // Try to set the wait flag again
+ }
+ // This only happens if the lock was freed while we attempt the set the
+ // wait flag. Try to grab the lock again
+ }
+ // Never reached.
+}
+
+layout::Sides vsoc::layout::WaitingLockBase::UnlockCommon(uint32_t tid) {
+ uint32_t expected_state = lock_uint32_;
+
+ // We didn't hold the lock. This process is insane and must die before it
+ // does damage.
+ uint32_t marked_tid = MakeOwnerTid(tid);
+ if ((marked_tid ^ expected_state) & OwnerMask) {
+ LOG(FATAL) << tid << " unlocking " << this << " owned by "
+ << expected_state;
+ }
+ // If contention is just starting this may fail twice (once for each bit)
+ // expected_state updates on each failure. When this finishes we have
+ // one bit for each waiter
+ while (1) {
+ if (lock_uint32_.compare_exchange_strong(expected_state, LockFree)) {
+ break;
+ }
+ }
+ if ((expected_state ^ marked_tid) & OwnerMask) {
+ LOG(FATAL) << "Lock owner of " << this << " changed from " << tid << " to "
+ << expected_state << " during unlock";
+ }
+ switch (expected_state & (GuestWaitingFlag | HostWaitingFlag)) {
+ case 0:
+ return Sides::NoSides;
+ case GuestWaitingFlag:
+ return Sides::Guest;
+ case HostWaitingFlag:
+ return Sides::Host;
+ default:
+ return Sides::Both;
+ }
+}
+
+bool vsoc::layout::WaitingLockBase::RecoverSingleSided() {
+ // No need to signal because the caller ensured that there were no other
+ // threads...
+ return lock_uint32_.exchange(LockFree) != LockFree;
+}
+
+void layout::GuestAndHostLock::Lock(RegionView* region) {
+ uint32_t expected;
+ uint32_t tid = gettid();
+ while (1) {
+ if (TryLock(tid, &expected)) {
+ return;
+ }
+ region->WaitForSignal(&lock_uint32_, expected);
+ }
+}
+
+void layout::GuestAndHostLock::Unlock(RegionView* region) {
+ region->SendSignal(UnlockCommon(gettid()), &lock_uint32_);
+}
+
+bool layout::GuestAndHostLock::Recover(RegionView* region) {
+ uint32_t expected_state = lock_uint32_;
+ uint32_t expected_owner_bit = (Sides::OurSide == Sides::Host) ? HostIsOwner : 0;
+ // This avoids check then act by reading exactly once and then
+ // eliminating the states where Recover should be a noop.
+ if (expected_state == LockFree) {
+ return false;
+ }
+ // Owned by the other side, do nothing.
+ if ((expected_state & HostIsOwner) != expected_owner_bit) {
+ return false;
+ }
+ // At this point we know two things:
+ // * The lock was held by our side
+ // * There are no other threads running on our side (precondition
+ // for calling Recover())
+ // Therefore, we know that the current expected value should still
+ // be in the lock structure. Use the normal unlock logic, providing
+ // the tid that we observed in the lock.
+ region->SendSignal(UnlockCommon(expected_state), &lock_uint32_);
+ return true;
+}
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/lock_guard.h b/common/vsoc/lib/lock_guard.h
new file mode 100644
index 0000000..8fbcc7d
--- /dev/null
+++ b/common/vsoc/lib/lock_guard.h
@@ -0,0 +1,84 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+namespace vsoc {
+
+class RegionView;
+
+/*
+ * Implements std::lock_guard like functionality for the vsoc locks.
+ */
+
+template <typename Lock>
+class LockGuard {
+ public:
+ explicit LockGuard(Lock* lock) : lock_(lock) {
+ lock_->Lock();
+ }
+
+ LockGuard(LockGuard&& o) noexcept {
+ lock_ = o.lock_;
+ o.lock_ = nullptr;
+ }
+
+ LockGuard(const LockGuard&) = delete;
+ LockGuard& operator=(const LockGuard&) = delete;
+
+ ~LockGuard() {
+ if (lock_) {
+ lock_->Unlock();
+ }
+ }
+
+ private:
+ Lock* lock_;
+};
+
+template <>
+class LockGuard<::vsoc::layout::GuestAndHostLock> {
+ using Lock = ::vsoc::layout::GuestAndHostLock;
+
+ public:
+ LockGuard(Lock* lock, RegionView* region) : lock_(lock), region_(region) {
+ lock_->Lock(region_);
+ }
+
+ LockGuard(LockGuard&& o) noexcept {
+ lock_ = o.lock_;
+ o.lock_ = nullptr;
+ region_ = o.region_;
+ o.region_ = nullptr;
+ }
+
+ LockGuard(const LockGuard&) = delete;
+ LockGuard& operator=(const LockGuard&) = delete;
+
+ ~LockGuard() {
+ if (lock_) {
+ lock_->Unlock(region_);
+ }
+ }
+
+ private:
+ Lock* lock_;
+ RegionView* region_;
+};
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/lock_test.cpp b/common/vsoc/lib/lock_test.cpp
new file mode 100644
index 0000000..d09b5a4
--- /dev/null
+++ b/common/vsoc/lib/lock_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common/vsoc/lib/region_view.h"
+
+#ifdef CUTTLEFISH_HOST
+using MyLock = vsoc::layout::HostLock;
+#else
+using MyLock = vsoc::layout::GuestLock;
+#endif
+
+class SimpleLocker {
+ public:
+ enum State {
+ BEFORE_EXECUTION,
+ BEFORE_LOCK,
+ IN_CRITICAL_SECTION,
+ DONE,
+ JOINED
+ };
+
+ explicit SimpleLocker(MyLock* lock)
+ : lock_(lock), thread_(&SimpleLocker::Work, this) {}
+
+ void Work() {
+ state_ = BEFORE_LOCK;
+ lock_->Lock();
+ state_ = IN_CRITICAL_SECTION;
+ InCriticalSection();
+ lock_->Unlock();
+ state_ = DONE;
+ }
+
+ void InCriticalSection() {}
+
+ void Join() {
+ thread_.join();
+ state_ = JOINED;
+ }
+
+ protected:
+ MyLock* lock_;
+ volatile State state_{BEFORE_EXECUTION};
+ std::thread thread_;
+};
+
+TEST(LockTest, Basic) {
+ // In production regions are always 0 filled on allocation. That's not
+ // true on the stack, so initialize the lock when we declare it.
+ MyLock lock{};
+ SimpleLocker a(&lock);
+ SimpleLocker b(&lock);
+ a.Join();
+ b.Join();
+}
diff --git a/common/vsoc/lib/managed_e2e_test_region_layout.cpp b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
new file mode 100644
index 0000000..1cc794b
--- /dev/null
+++ b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/managed_e2e_test_region_layout.h"
+
+namespace vsoc {
+namespace layout {
+namespace e2e_test {
+
+const char* E2EManagedTestRegionLayout::region_name = "e2e_managed";
+
+const char* E2EManagerTestRegionLayout::region_name = "e2e_manager";
+
+} // namespace e2e_test
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/lib/mock_region_view.h b/common/vsoc/lib/mock_region_view.h
new file mode 100644
index 0000000..f9c3006
--- /dev/null
+++ b/common/vsoc/lib/mock_region_view.h
@@ -0,0 +1,119 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <linux/futex.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+#include <unordered_map>
+
+#include "common/vsoc/lib/region_signaling_interface.h"
+
+namespace vsoc {
+namespace test {
+
+/**
+ * MockRegionView mocks a region in the shared memory window by calloc and
+ * futex. It supports only one-sided signal as in it doesn't do anything on
+ * sending or waiting interrupt. This is to test if a particular layout
+ * behaves correctly when there are multiple threads accessing it.
+ */
+template <typename Layout>
+class MockRegionView : public vsoc::RegionSignalingInterface {
+ public:
+ explicit MockRegionView(){};
+ virtual ~MockRegionView() {
+ if (region_base_) {
+ free(region_base_);
+ region_base_ = nullptr;
+ }
+ };
+
+ bool Open() {
+ region_base_ = calloc(sizeof(Layout), 1);
+ return !region_base_;
+ };
+
+ virtual void SendSignal(vsoc::layout::Sides /* sides_to_signal */,
+ std::atomic<uint32_t>* uaddr) {
+ syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1,
+ nullptr, nullptr, 0);
+ }
+
+ virtual int WaitForSignal(std::atomic<uint32_t>* uaddr,
+ uint32_t expected_value) {
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ if (tid_to_addr_.find(std::this_thread::get_id()) != tid_to_addr_.end()) {
+ // Same thread tries to wait
+ return 0;
+ }
+ tid_to_addr_.emplace(std::this_thread::get_id(), uaddr);
+ map_changed_.notify_one();
+ }
+
+ syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+
+ {
+ std::lock_guard<std::mutex> guard(mutex_);
+ tid_to_addr_.erase(std::this_thread::get_id());
+ }
+ return 0;
+ }
+
+ // Mock methods from TypedRegionView
+ Layout* data() { return reinterpret_cast<Layout*>(region_base_); };
+
+ // Check wait status on a specificy thread
+ bool IsBlocking(std::thread::id tid) {
+ while (1) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (tid_to_addr_.find(tid) != tid_to_addr_.end()) {
+ return true;
+ }
+ // Allow some time as tid map might not be updated yet
+ while (tid_to_addr_.find(tid) == tid_to_addr_.end()) {
+ if (map_changed_.wait_for(lock,
+ std::chrono::seconds(kWaitTimeoutInSec)) ==
+ std::cv_status::timeout) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private:
+ // Timeout to avoid a race on checking if a thread is blocked
+ static constexpr int kWaitTimeoutInSec = 5;
+
+ void* region_base_{};
+ std::mutex mutex_;
+ std::condition_variable map_changed_;
+ std::unordered_map<std::thread::id, std::atomic<uint32_t>*> tid_to_addr_;
+};
+
+template <typename Layout>
+constexpr int MockRegionView<Layout>::kWaitTimeoutInSec;
+
+} // namespace test
+} // namespace vsoc
diff --git a/common/vsoc/lib/region_control.h b/common/vsoc/lib/region_control.h
new file mode 100644
index 0000000..7762603
--- /dev/null
+++ b/common/vsoc/lib/region_control.h
@@ -0,0 +1,111 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <sys/mman.h>
+#include <stdint.h>
+#include <memory>
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+/**
+ * Base class for side-specific utility functions that work on regions.
+ * The methods in this class do not assume that the region is mapped in memory.
+ * This makes is appropriate for ManagedRegions and certain low-level tests
+ * of VSoC shared memory. Most other users will want to use TypedRegions with
+ * a defined RegionLayout.
+ *
+ * This class is not directly instantiable because it must be specialized with
+ * additional fields for the host and guest.
+ */
+class RegionControl {
+ public:
+ virtual ~RegionControl() {
+ if (region_base_) {
+ munmap(region_base_, region_size());
+ }
+ region_base_ = nullptr;
+ }
+
+#if defined(CUTTLEFISH_HOST)
+ static std::shared_ptr<RegionControl> Open(const char* region_name,
+ const char* domain);
+#else
+ static std::shared_ptr<RegionControl> Open(const char* region_name);
+#endif
+
+ const vsoc_device_region& region_desc() const { return region_desc_; }
+
+ // Returns the size of the entire region, including the signal tables.
+ uint32_t region_size() const {
+ return region_desc_.region_end_offset - region_desc_.region_begin_offset;
+ }
+
+ // Returns the size of the region that is usable for region-specific data.
+ uint32_t region_data_size() const {
+ return region_size() - region_desc_.offset_of_region_data;
+ }
+
+ // Creates a FdScopedPermission. Returns the file descriptor or -1 on
+ // failure. FdScopedPermission is not supported on the host, so -1 is
+ // always returned there.
+ virtual int CreateFdScopedPermission(const char* managed_region_name,
+ uint32_t owner_offset,
+ uint32_t owned_value,
+ uint32_t begin_offset,
+ uint32_t end_offset) = 0;
+
+ // Interrupt our peer, causing it to scan the outgoing_signal_table
+ virtual bool InterruptPeer() = 0;
+
+ // Wake the local signal table scanner. Primarily used during shutdown
+ virtual void InterruptSelf() = 0;
+
+ // Maps the entire region at an address, returning a pointer to the mapping
+ virtual void* Map() = 0;
+
+ // Wait for an interrupt from our peer
+ virtual void WaitForInterrupt() = 0;
+
+ // Signals local waiters at the given region offset.
+ // Defined only on the guest.
+ // Return value is negative on error.
+ virtual int SignalSelf(uint32_t offset) = 0;
+
+ // Waits for a signal at the given region offset.
+ // Defined only on the guest.
+ // Return value is negative on error. The number of false wakes is returned
+ // on success.
+ virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) = 0;
+
+ template <typename T>
+ T* region_offset_to_pointer(uint32_t offset) {
+ if (offset > region_size()) {
+ LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+ << region_base_;
+ }
+ return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(region_base_) +
+ offset);
+ }
+
+ protected:
+ RegionControl() {}
+ void* region_base_{};
+ vsoc_device_region region_desc_{};
+};
+} // namespace vsoc
diff --git a/common/vsoc/lib/region_signaling_interface.h b/common/vsoc/lib/region_signaling_interface.h
new file mode 100644
index 0000000..c1399e1
--- /dev/null
+++ b/common/vsoc/lib/region_signaling_interface.h
@@ -0,0 +1,64 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <atomic>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+
+/**
+ * Interface that defines signaling and waiting for signal.
+ */
+class RegionSignalingInterface {
+ public:
+ virtual ~RegionSignalingInterface(){};
+
+ // Post a signal to the guest, the host, or both.
+ // See futex(2) FUTEX_WAKE for details.
+ //
+ // sides_to_signal: controls where the signal is sent
+ //
+ // signal_addr: the memory location to signal. Must be within the region.
+ virtual void SendSignal(layout::Sides sides_to_signal,
+ std::atomic<uint32_t>* signal_addr) = 0;
+
+ // This implements the following:
+ // if (*signal_addr == last_observed_value)
+ // wait_for_signal_at(signal_addr);
+ //
+ // Note: the caller still needs to check the value at signal_addr because
+ // this function may return early for reasons that are implementation-defined.
+ // See futex(2) FUTEX_WAIT for details.
+ //
+ // signal_addr: the memory that will be signaled. Must be within the region.
+ //
+ // last_observed_value: the value that motivated the calling code to wait.
+ //
+ // The return values are:
+ // -1 on failure
+ // 0 indicates success with no tuning information
+ // >0 indicates success. The number indicates how many times the thread
+ // woke before it could return.
+ // Large values indicate that the regions should be tuned.
+ virtual int WaitForSignal(std::atomic<uint32_t>* signal_addr,
+ uint32_t last_observed_value) = 0;
+};
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/region_view.cpp b/common/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..dc1088c
--- /dev/null
+++ b/common/vsoc/lib/region_view.cpp
@@ -0,0 +1,201 @@
+#include "common/vsoc/lib/region_view.h"
+
+#include <sys/mman.h>
+
+#include "common/libs/glog/logging.h"
+
+namespace {
+const uint32_t UADDR_OFFSET_MASK = 0xFFFFFFFC;
+const uint32_t UADDR_OFFSET_ROUND_TRIP_FLAG = 1;
+} // namespace
+
+using vsoc::layout::Sides;
+
+vsoc::RegionWorker::RegionWorker(RegionView* region,
+ std::shared_ptr<RegionControl> control)
+ : control_(control),
+ region_(region),
+ stopping_(false) {}
+
+void vsoc::RegionWorker::start() {
+ CHECK(!thread_.joinable());
+ thread_ = std::thread(&vsoc::RegionWorker::Work, this);
+}
+
+void vsoc::RegionWorker::Work() {
+ while (!stopping_) {
+ region_->WaitForInterrupt();
+ if (stopping_) {
+ return;
+ }
+ region_->ProcessSignalsFromPeer([this](uint32_t offset) {
+ control_->SignalSelf(offset);
+ });
+ }
+}
+
+vsoc::RegionWorker::~RegionWorker() {
+ stopping_ = true;
+
+ if (thread_.joinable()) {
+ region_->InterruptSelf();
+ thread_.join();
+ }
+}
+
+vsoc::RegionView::~RegionView() {
+ // region_base_ is borrowed here. It's owned by control_, which is
+ // responsible for unmapping the memory
+ region_base_ = nullptr;
+}
+
+#if defined(CUTTLEFISH_HOST)
+bool vsoc::RegionView::Open(const char* name, const char* domain) {
+ control_ = vsoc::RegionControl::Open(name, domain);
+ if (!control_) {
+ return false;
+ }
+ region_base_ = control_->Map();
+ return region_base_ != nullptr;
+}
+#else
+bool vsoc::RegionView::Open(const char* name) {
+ control_ = vsoc::RegionControl::Open(name);
+ if (!control_) {
+ return false;
+ }
+ region_base_ = control_->Map();
+ return region_base_ != nullptr;
+}
+#endif
+
+// Interrupt our peer, causing it to scan the outgoing_signal_table
+bool vsoc::RegionView::MaybeInterruptPeer() {
+ if (region_offset_to_pointer<std::atomic<uint32_t>>(
+ outgoing_signal_table().interrupt_signalled_offset)
+ ->exchange(1)) {
+ return false;
+ }
+ return control_->InterruptPeer();
+}
+
+// Wait for an interrupt from our peer
+void vsoc::RegionView::WaitForInterrupt() {
+ while (1) {
+ if (region_offset_to_pointer<std::atomic<uint32_t>>(
+ incoming_signal_table().interrupt_signalled_offset)
+ ->exchange(0)) {
+ return;
+ }
+ control_->WaitForInterrupt();
+ }
+}
+
+void vsoc::RegionView::ProcessSignalsFromPeer(
+ std::function<void(uint32_t)> signal_handler) {
+ const vsoc_signal_table_layout& table = incoming_signal_table();
+ const size_t num_offsets = (1 << table.num_nodes_lg2);
+ std::atomic<uint32_t>* offsets =
+ region_offset_to_pointer<std::atomic<uint32_t>>(
+ table.futex_uaddr_table_offset);
+ for (size_t i = 0; i < num_offsets; ++i) {
+ uint32_t raw_offset = offsets[i].exchange(0);
+ if (raw_offset) {
+ bool round_trip = raw_offset & UADDR_OFFSET_ROUND_TRIP_FLAG;
+ uint32_t offset = raw_offset & UADDR_OFFSET_MASK;
+ signal_handler(offset);
+ if (round_trip) {
+ SendSignalToPeer(
+ region_offset_to_pointer<std::atomic<uint32_t>>(offset), false);
+ }
+ }
+ }
+}
+
+void vsoc::RegionView::SendSignal(Sides sides_to_signal,
+ std::atomic<uint32_t>* uaddr) {
+ if (sides_to_signal & Sides::Peer) {
+ // If we should also be signalling our side set the round trip flag on
+ // the futex signal.
+ bool round_trip = sides_to_signal & Sides::OurSide;
+ SendSignalToPeer(uaddr, round_trip);
+ // Return without signaling our waiters to give the other side a chance
+ // to run.
+ return;
+ }
+ if (sides_to_signal & Sides::OurSide) {
+ control_->SignalSelf(pointer_to_region_offset(uaddr));
+ }
+}
+
+void vsoc::RegionView::SendSignalToPeer(std::atomic<uint32_t>* uaddr,
+ bool round_trip) {
+ const vsoc_signal_table_layout& table = outgoing_signal_table();
+ std::atomic<uint32_t>* offsets =
+ region_offset_to_pointer<std::atomic<uint32_t>>(
+ table.futex_uaddr_table_offset);
+ // maximum index in the node that can hold an offset;
+ const size_t max_index = (1 << table.num_nodes_lg2) - 1;
+ uint32_t offset = pointer_to_region_offset(uaddr);
+ if (offset & ~UADDR_OFFSET_MASK) {
+ LOG(FATAL) << "uaddr offset is not naturally aligned " << uaddr;
+ }
+ // Guess at where this offset should go in the table.
+ // Do this before we set the round-trip flag.
+ size_t hash = (offset >> 2) & max_index;
+ if (round_trip) {
+ offset |= UADDR_OFFSET_ROUND_TRIP_FLAG;
+ }
+ while (1) {
+ uint32_t expected = 0;
+ if (offsets[hash].compare_exchange_strong(expected, offset)) {
+ // We stored the offset. Send the interrupt.
+ this->MaybeInterruptPeer();
+ break;
+ }
+ // We didn't store, but the value was already in the table with our flag.
+ // Return without interrupting.
+ if (expected == offset) {
+ return;
+ }
+ // Hash collision. Try again in a different node
+ if ((expected & UADDR_OFFSET_MASK) != (offset & UADDR_OFFSET_MASK)) {
+ hash = (hash + 1) & max_index;
+ continue;
+ }
+ // Our offset was in the bucket, but the flags didn't match.
+ // We're done if the value in the node had the round trip flag set.
+ if (expected & UADDR_OFFSET_ROUND_TRIP_FLAG) {
+ return;
+ }
+ // We wanted the round trip flag, but the value in the bucket didn't set it.
+ // Do a second swap to try to set it.
+ if (offsets[hash].compare_exchange_strong(expected, offset)) {
+ // It worked. We're done.
+ return;
+ }
+ if (expected == offset) {
+ // expected was the offset without the flag. After the swap it has the
+ // the flag. This means that some other thread set the flag, so
+ // we're done.
+ return;
+ }
+ // Something about the offset changed. We need to go around again, because:
+ // our peer processed the old entry
+ // another thread may have stolen the node while we were distracted
+ }
+}
+
+std::unique_ptr<vsoc::RegionWorker> vsoc::RegionView::StartWorker() {
+ std::unique_ptr<vsoc::RegionWorker> worker(
+ new vsoc::RegionWorker(this /* region */, control_));
+
+ worker->start();
+ return worker;
+}
+
+int vsoc::RegionView::WaitForSignal(std::atomic<uint32_t>* uaddr,
+ uint32_t expected_value) {
+ return control_->WaitForSignal(pointer_to_region_offset(uaddr),
+ expected_value);
+}
diff --git a/common/vsoc/lib/region_view.h b/common/vsoc/lib/region_view.h
new file mode 100644
index 0000000..055f721
--- /dev/null
+++ b/common/vsoc/lib/region_view.h
@@ -0,0 +1,214 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Object that represents a region on the Host
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <atomic>
+#include <cstdint>
+
+#include <functional>
+#include <map>
+#include <thread>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+#include "common/vsoc/lib/region_control.h"
+#include "common/vsoc/lib/region_signaling_interface.h"
+#include "common/vsoc/shm/base.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+class RegionControl;
+class RegionView;
+
+/**
+ * Represents a task that is tied to a RegionView.
+ *
+ * This is currently used for the task that forwards futexes across the
+ * shared memory window.
+ */
+class RegionWorker {
+ public:
+ RegionWorker(RegionView* region, std::shared_ptr<RegionControl> control);
+ ~RegionWorker();
+
+ void start();
+
+ void Work();
+
+ protected:
+ std::shared_ptr<RegionControl> control_;
+ RegionView* region_;
+ std::thread thread_;
+ volatile bool stopping_;
+};
+
+/**
+ * Base class to access a mapped region in VSoC shared memory.
+ * This class holds the methods that depends on the region's memory having an
+ * address. The RegionControl class holds the methods that can be invoked
+ * without mapping the region.
+ */
+class RegionView : public RegionSignalingInterface {
+ public:
+ virtual ~RegionView();
+
+#if defined(CUTTLEFISH_HOST)
+ bool Open(const char* region_name, const char* domain);
+#else
+ bool Open(const char* region_name);
+#endif
+
+ // Returns a pointer to the table that will be scanned for signals
+ const vsoc_signal_table_layout& incoming_signal_table();
+
+ // Returns a pointer to the table that will be used to post signals
+ const vsoc_signal_table_layout& outgoing_signal_table();
+
+ // Returns true iff an interrupt is queued in the signal table
+ bool HasIncomingInterrupt() {
+ return *region_offset_to_pointer<std::atomic<uint32_t>>(
+ incoming_signal_table().interrupt_signalled_offset);
+ }
+
+ // Wake any threads waiting for an interrupt. This is generally used during
+ // shutdown.
+ void InterruptSelf() { control_->InterruptSelf(); }
+
+ // Interrupt our peer if an interrupt is not already on the way.
+ // Returns true if the interrupt was sent, false if an interrupt was already
+ // pending.
+ bool MaybeInterruptPeer();
+
+ // Scan in the incoming signal table, issuing futex calls for any posted
+ // signals and then reposting them to the peer if they were round-trip
+ // signals.
+ //
+ // signal_handler: An action to perform on every offset signalled by our
+ // peer, usually a FUTEX_WAKE call, but can be customized for other
+ // purposes.
+ void ProcessSignalsFromPeer(
+ std::function<void(uint32_t)> signal_handler);
+
+ // Post a signal to the guest, the host, or both.
+ // See futex(2) FUTEX_WAKE for details.
+ //
+ // sides_to_signal: controls where the signal is sent
+ //
+ // signal_addr: the memory location to signal. Must be within the region.
+ void SendSignal(layout::Sides sides_to_signal,
+ std::atomic<uint32_t>* signal_addr);
+
+ // Post a signal to our peer for a specific memeory location.
+ // See futex(2) FUTEX_WAKE for details.
+ //
+ // signal_addr: the memory location to signal. Must be within the region.
+ //
+ // round_trip: true if there may be waiters on both sides of the shared
+ // memory.
+ void SendSignalToPeer(std::atomic<uint32_t>* signal_addr, bool round_trip);
+
+ // Waits until an interrupt appears on this region, then clears the
+ // interrupted flag and returns.
+ void WaitForInterrupt();
+
+ // This implements the following:
+ // if (*signal_addr == last_observed_value)
+ // wait_for_signal_at(signal_addr);
+ //
+ // Note: the caller still needs to check the value at signal_addr because
+ // this function may return early for reasons that are implementation-defined.
+ // See futex(2) FUTEX_WAIT for details.
+ //
+ // signal_addr: the memory that will be signaled. Must be within the region.
+ //
+ // last_observed_value: the value that motivated the calling code to wait.
+ //
+ // The return value is -1 on error. On the guest positive values give the
+ // number of false wakes.
+ int WaitForSignal(std::atomic<uint32_t>* signal_addr,
+ uint32_t last_observed_value) override;
+
+ // Starts the signal table scanner. This must be invoked by subclasses, which
+ // MUST store the returned unique_ptr as a class member.
+ __attribute__((warn_unused_result))
+ std::unique_ptr<RegionWorker> StartWorker();
+
+ // Returns a pointer to the start of region data that is cast to the given
+ // type. Initializers that run in the launcher use this to get a typed view
+ // of the region. Most other cases should be handled via TypedRegionView.
+ template <typename LayoutType>
+ LayoutType* GetLayoutPointer() {
+ return this->region_offset_to_pointer<LayoutType>(
+ control_->region_desc().offset_of_region_data);
+ }
+
+ // Helper functions for lock guards. This approach has two advantages:
+ //
+ // o Callers don't have to be refactored when lock types change
+ // o The region pointer can be provided automatically
+ template <typename T>
+ LockGuard<T> make_lock_guard(T* lock) {
+ return LockGuard<T>(lock);
+ }
+
+ LockGuard<::vsoc::layout::GuestAndHostLock> make_lock_guard(
+ ::vsoc::layout::GuestAndHostLock* l) {
+ return LockGuard<::vsoc::layout::GuestAndHostLock>(l, this);
+ }
+
+ protected:
+ template <typename T>
+ T* region_offset_to_pointer(uint32_t offset) {
+ return control_->region_offset_to_pointer<T>(offset);
+ }
+
+ template <typename T>
+ const T& region_offset_to_reference(uint32_t offset) const {
+ if (offset > control_->region_size()) {
+ LOG(FATAL) << __FUNCTION__ << ": " << offset << " not in region @"
+ << region_base_;
+ }
+ return *reinterpret_cast<const T*>(
+ reinterpret_cast<uintptr_t>(region_base_) + offset);
+ }
+
+ // Calculates an offset based on a pointer in the region. Crashes if the
+ // pointer isn't in the region.
+ // This is mostly for the RegionView's internal plumbing. Use TypedRegionView
+ // and RegionLayout to avoid this in most cases.
+ template <typename T>
+ uint32_t pointer_to_region_offset(T* ptr) const {
+ uint32_t rval = reinterpret_cast<uintptr_t>(ptr) -
+ reinterpret_cast<uintptr_t>(region_base_);
+ if (rval > control_->region_size()) {
+ LOG(FATAL) << __FUNCTION__ << ": " << ptr << " not in region @"
+ << region_base_;
+ }
+ return rval;
+ }
+
+ std::shared_ptr<RegionControl> control_;
+ void* region_base_{};
+};
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/screen_layout.cpp b/common/vsoc/lib/screen_layout.cpp
new file mode 100644
index 0000000..d302030
--- /dev/null
+++ b/common/vsoc/lib/screen_layout.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/screen_layout.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace screen {
+
+const char* ScreenLayout::region_name = "screen";
+
+} // namespace screen
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/lib/screen_region_view.cpp b/common/vsoc/lib/screen_region_view.cpp
new file mode 100644
index 0000000..2a22335
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/screen_region_view.h"
+
+#include <memory>
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/lib/lock_guard.h"
+
+using vsoc::layout::screen::CompositionStats;
+using vsoc::screen::ScreenRegionView;
+
+const uint8_t* ScreenRegionView::first_buffer() const {
+ // TODO(jemoreira): Add alignments?
+ return &(this->data().buffer[0]);
+}
+
+int ScreenRegionView::number_of_buffers() const {
+ auto offset_of_first_buffer =
+ const_cast<ScreenRegionView*>(this)->pointer_to_region_offset(
+ this->first_buffer());
+ size_t total_buffer_size = control_->region_size() - offset_of_first_buffer;
+ return total_buffer_size / buffer_size();
+}
+
+void* ScreenRegionView::GetBuffer(int buffer_idx) {
+ uint8_t* buffer = const_cast<uint8_t*>(this->first_buffer());
+ return buffer + buffer_idx * this->buffer_size();
+}
+
+// We can use a locking protocol because we decided that the streamer should
+// have more priority than the hwcomposer, so it's OK to block the hwcomposer
+// waiting for the streamer to complete, while the streamer will only block on
+// the hwcomposer when it has ran out of work to do and needs to get more from
+// the hwcomposer.
+void ScreenRegionView::BroadcastNewFrame(int buffer_idx,
+ const CompositionStats* stats) {
+ {
+ if (buffer_idx < 0 || buffer_idx >= number_of_buffers()) {
+ LOG(ERROR) << "Attempting to broadcast an invalid buffer index: "
+ << buffer_idx;
+ return;
+ }
+ auto lock_guard(make_lock_guard(&data()->bcast_lock));
+ data()->seq_num++;
+ data()->buffer_index = static_cast<int32_t>(buffer_idx);
+ if (stats) {
+ data()->stats = *stats;
+ }
+ }
+ // Signaling after releasing the lock may cause spurious wake ups.
+ // Signaling while holding the lock may cause the just-awaken listener to
+ // block immediately trying to acquire the lock.
+ // The former is less costly and slightly less likely to happen.
+ SendSignal(layout::Sides::Both, &data()->seq_num);
+}
+
+int ScreenRegionView::WaitForNewFrameSince(uint32_t* last_seq_num,
+ CompositionStats* stats) {
+ static std::unique_ptr<RegionWorker> worker = StartWorker();
+ // It's ok to read seq_num here without holding the lock because the lock will
+ // be acquired immediately after so we'll block if necessary to wait for the
+ // critical section in BroadcastNewFrame to complete.
+ // Also, the call to WaitForSignal receives a pointer to seq_num (so the
+ // compiler should not optimize it out) and includes a memory barrier
+ // (FUTEX_WAIT).
+ while (data()->seq_num == *last_seq_num) {
+ // Don't hold the lock when waiting for a signal, will deadlock.
+ WaitForSignal(&data()->seq_num, *last_seq_num);
+ }
+
+ {
+ auto lock_guard(make_lock_guard(&data()->bcast_lock));
+ *last_seq_num = data()->seq_num;
+ if (stats) {
+ *stats = data()->stats;
+ }
+ return static_cast<int>(data()->buffer_index);
+ }
+}
diff --git a/common/vsoc/lib/screen_region_view.h b/common/vsoc/lib/screen_region_view.h
new file mode 100644
index 0000000..aef4359
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view.h
@@ -0,0 +1,104 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+namespace screen {
+
+// Provides information related to the device's screen. Allows to query screen
+// properties such as resolution and dpi, as well as subscribe/notify to/of
+// changes on the screen contents. It also holds the contents of the display.
+class ScreenRegionView
+ : public vsoc::TypedRegionView<ScreenRegionView,
+ vsoc::layout::screen::ScreenLayout> {
+ public:
+ static int align(int input) {
+ auto constexpr alignment = 16;
+ return (input + alignment - 1) & -alignment;
+ }
+
+ // Screen width in pixels
+ int x_res() const { return data().x_res; }
+
+ // Screen height in pixels
+ int y_res() const { return data().y_res; }
+
+ // Dots per inch
+ int dpi() const { return data().dpi; }
+
+ // Refresh rate in Hertz
+ int refresh_rate_hz() const { return data().refresh_rate_hz; }
+
+ uint32_t pixel_format() const { return kFbPixelFormat; }
+
+ uint32_t bytes_per_pixel() const {
+ return vsoc::PixelFormatProperties<kFbPixelFormat>::bytes_per_pixel;
+ }
+
+ int line_length() const { return align(x_res() * bytes_per_pixel()); }
+
+ size_t buffer_size() const {
+ return (align(x_res() * bytes_per_pixel()) * y_res()) + kSwiftShaderPadding;
+ }
+
+ int number_of_buffers() const;
+
+ // Gets a pointer to the beginning of a buffer. Does not perform any bound
+ // checks on the index.
+ void* GetBuffer(int buffer_idx);
+
+ // Broadcasts a new frame.
+ // buffer_idx is the index of the buffer containing the composed screen, it's
+ // a number in the range [0, number_of_buffers() - 1].
+ // Stats holds performance information of the last composition, can be null.
+ void BroadcastNewFrame(
+ int buffer_idx,
+ const vsoc::layout::screen::CompositionStats* stats = nullptr);
+
+ // Waits for a new frame (one with a different seq_num than last one we saw).
+ // Returns the index of the buffer containing the new frame or a negative
+ // number if there was an error, stores the new sequential number in
+ // *last_seq_num. The frame sequential numbers will be provided by the
+ // hwcomposer and expected to increase monotonically over time (though it's
+ // not a hard requirement), this numbers are guaranteed to be non-zero when a
+ // valid frame is available. Performance statistics are returned through the
+ // stats parameter when it's not null.
+ int WaitForNewFrameSince(
+ uint32_t* last_seq_num,
+ vsoc::layout::screen::CompositionStats* stats = nullptr);
+
+ using Pixel = uint32_t;
+ static constexpr int kSwiftShaderPadding = 4;
+ static constexpr int kRedShift = 0;
+ static constexpr int kGreenShift = 8;
+ static constexpr int kBlueShift = 16;
+ static constexpr int kRedBits = 8;
+ static constexpr int kGreenBits = 8;
+ static constexpr int kBlueBits = 8;
+ static constexpr uint32_t kFbPixelFormat = vsoc::VSOC_PIXEL_FORMAT_RGBA_8888;
+
+ protected:
+ const uint8_t* first_buffer() const;
+};
+} // namespace screen
+} // namespace vsoc
diff --git a/common/vsoc/lib/screen_region_view_test.cpp b/common/vsoc/lib/screen_region_view_test.cpp
new file mode 100644
index 0000000..cc02baa
--- /dev/null
+++ b/common/vsoc/lib/screen_region_view_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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 "screen_region_view.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include <stdio.h>
+
+using vsoc::screen::ScreenRegionView;
+
+int main() {
+ uint32_t frame_num = 0;
+ int buffer_id = 0;
+
+#if defined(CUTTLEFISH_HOST)
+ auto region = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+#else
+ auto region = ScreenRegionView::GetInstance();
+#endif
+ if (!region) {
+ fprintf(stderr, "Error opening region\n");
+ return 1;
+ }
+
+ while (1) {
+ buffer_id = region->WaitForNewFrameSince(&frame_num);
+ printf("Signaled frame_num = %d, buffer_id = %d\n", frame_num, buffer_id);
+ }
+
+ return 0;
+}
diff --git a/common/vsoc/lib/single_sided_signal.h b/common/vsoc/lib/single_sided_signal.h
new file mode 100644
index 0000000..a40c792
--- /dev/null
+++ b/common/vsoc/lib/single_sided_signal.h
@@ -0,0 +1,50 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <unistd.h>
+#include <linux/futex.h>
+#include <sys/syscall.h>
+
+
+// Signaling mechanism that allows threads to signal changes to shared
+// memory and to wait for signals.
+
+namespace vsoc {
+/**
+ * Defines the strategy for signaling among threads on a single kernel.
+ */
+namespace SingleSidedSignal {
+/**
+ * Waits for a signal, assuming the the word at addr matches expected_state.
+ * Will return immediately if the value does not match.
+ * Callers must be equipped to cope with spurious returns.
+ */
+static void AwaitSignal(uint32_t expected_state, uint32_t* uaddr) {
+ syscall(SYS_futex, uaddr, FUTEX_WAIT, expected_state, nullptr, nullptr, 0);
+}
+
+/**
+ * Sends a signal to every thread in AwaitSignal() using the address in
+ * uaddr.
+ */
+static void Signal(std::atomic<uint32_t>* uaddr) {
+ syscall(SYS_futex, reinterpret_cast<int32_t*>(uaddr), FUTEX_WAKE, -1, nullptr,
+ nullptr, 0);
+}
+} // namespace SingleSidedSignal
+} // namespace vsoc
diff --git a/common/vsoc/lib/socket_forward_layout.cpp b/common/vsoc/lib/socket_forward_layout.cpp
new file mode 100644
index 0000000..299ef70
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_layout.cpp
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/socket_forward_layout.h"
+
+const char* vsoc::layout::socket_forward::SocketForwardLayout::region_name =
+ "socket_forward";
diff --git a/common/vsoc/lib/socket_forward_region_view.cpp b/common/vsoc/lib/socket_forward_region_view.cpp
new file mode 100644
index 0000000..2dbd2e8
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_region_view.cpp
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2018 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 <cassert>
+
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/lock_guard.h"
+#include "common/vsoc/lib/socket_forward_region_view.h"
+#include "common/vsoc/shm/lock.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+using vsoc::layout::socket_forward::Queue;
+using vsoc::layout::socket_forward::QueuePair;
+// store the read and write direction as variables to keep the ifdefs and macros
+// in later code to a minimum
+constexpr auto ReadDirection = &QueuePair::
+#ifdef CUTTLEFISH_HOST
+ guest_to_host;
+#else
+ host_to_guest;
+#endif
+
+constexpr auto WriteDirection = &QueuePair::
+#ifdef CUTTLEFISH_HOST
+ host_to_guest;
+#else
+ guest_to_host;
+#endif
+
+using vsoc::socket_forward::SocketForwardRegionView;
+
+vsoc::socket_forward::Packet vsoc::socket_forward::Packet::MakeBegin(
+ std::uint16_t port) {
+ auto packet = MakePacket(Header::BEGIN);
+ std::memcpy(packet.payload(), &port, sizeof port);
+ packet.set_payload_length(sizeof port);
+ return packet;
+}
+
+void SocketForwardRegionView::Recv(int connection_id, Packet* packet) {
+ CHECK(packet != nullptr);
+ do {
+ (data()->queues_[connection_id].*ReadDirection)
+ .queue.Read(this, reinterpret_cast<char*>(packet), sizeof *packet);
+ } while (packet->IsBegin());
+ CHECK(!packet->empty()) << "zero-size data message received";
+ CHECK_LE(packet->payload_length(), kMaxPayloadSize) << "invalid size";
+}
+
+bool SocketForwardRegionView::Send(int connection_id, const Packet& packet) {
+ CHECK(!packet.empty());
+ CHECK_LE(packet.payload_length(), kMaxPayloadSize);
+
+ (data()->queues_[connection_id].*WriteDirection)
+ .queue.Write(this, packet.raw_data(), packet.raw_data_length());
+ return true;
+}
+
+int SocketForwardRegionView::IgnoreUntilBegin(int connection_id) {
+ Packet packet{};
+ do {
+ (data()->queues_[connection_id].*ReadDirection)
+ .queue.Read(this, reinterpret_cast<char*>(&packet), sizeof packet);
+ } while (!packet.IsBegin());
+ return packet.port();
+}
+
+constexpr int kNumQueues =
+ static_cast<int>(vsoc::layout::socket_forward::kNumQueues);
+
+void SocketForwardRegionView::CleanUpPreviousConnections() {
+ data()->Recover();
+
+ static constexpr auto kRestartPacket = Packet::MakeRestart();
+ for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) {
+ Send(connection_id, kRestartPacket);
+ }
+}
+
+SocketForwardRegionView::ConnectionViewCollection
+SocketForwardRegionView::AllConnections() {
+ SocketForwardRegionView::ConnectionViewCollection all_queues;
+ for (int connection_id = 0; connection_id < kNumQueues; ++connection_id) {
+ all_queues.emplace_back(this, connection_id);
+ }
+ return all_queues;
+}
+
+// --- Connection ---- //
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::Recv(Packet* packet) {
+ std::unique_lock<std::mutex> guard(receive_thread_data_lock_);
+ while (received_packet_free_) {
+ receive_thread_data_cv_.wait(guard);
+ }
+ CHECK(received_packet_.IsData());
+ *packet = received_packet_;
+ received_packet_free_ = true;
+ receive_thread_data_cv_.notify_one();
+}
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::GotRecvClosed() const {
+ return received_packet_.IsRecvClosed() || (received_packet_.IsRestart()
+#ifdef CUTTLEFISH_HOST
+ && saw_data_
+#endif
+ );
+}
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::ShouldReceiveAnotherPacket() const {
+ return (received_packet_.IsRecvClosed() && !saw_end_) ||
+ (saw_end_ && received_packet_.IsEnd())
+#ifdef CUTTLEFISH_HOST
+ || (received_packet_.IsRestart() && !saw_data_) ||
+ (received_packet_.IsBegin())
+#endif
+ ;
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::ReceivePacket() {
+ view_->region_view()->Recv(view_->connection_id(), &received_packet_);
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForRecvClosed() {
+ if (GotRecvClosed()) {
+ saw_recv_closed_ = true;
+ view_->MarkOtherSideRecvClosed();
+ }
+#ifdef CUTTLEFISH_HOST
+ if (received_packet_.IsData()) {
+ saw_data_ = true;
+ }
+#endif
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::CheckPacketForEnd() {
+ if (received_packet_.IsEnd() || received_packet_.IsRestart()) {
+ CHECK(!saw_end_ || received_packet_.IsRestart());
+ saw_end_ = true;
+ }
+}
+
+
+bool SocketForwardRegionView::ShmConnectionView::Receiver::ExpectMorePackets() const {
+ return !saw_recv_closed_ || !saw_end_;
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::UpdatePacketAndSignalAvailable() {
+ if (!received_packet_.IsData()) {
+ static constexpr auto kEmptyPacket = Packet::MakeData();
+ received_packet_ = kEmptyPacket;
+ }
+ received_packet_free_ = false;
+ receive_thread_data_cv_.notify_one();
+}
+
+void SocketForwardRegionView::ShmConnectionView::Receiver::Start() {
+ while (ExpectMorePackets()) {
+ std::unique_lock<std::mutex> guard(receive_thread_data_lock_);
+ while (!received_packet_free_) {
+ receive_thread_data_cv_.wait(guard);
+ }
+
+ do {
+ ReceivePacket();
+ CheckPacketForRecvClosed();
+ } while (ShouldReceiveAnotherPacket());
+
+ if (received_packet_.empty()) {
+ LOG(ERROR) << "Received empty packet.";
+ }
+
+ CheckPacketForEnd();
+
+ UpdatePacketAndSignalAvailable();
+ }
+}
+
+auto SocketForwardRegionView::ShmConnectionView::ResetAndConnect()
+ -> ShmSenderReceiverPair {
+ if (receiver_) {
+ receiver_->Join();
+ }
+
+ {
+ std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+ other_side_receive_closed_ = false;
+ }
+
+#ifdef CUTTLEFISH_HOST
+ region_view()->IgnoreUntilBegin(connection_id());
+ region_view()->Send(connection_id(), Packet::MakeBegin(port_));
+#else
+ region_view()->Send(connection_id(), Packet::MakeBegin(port_));
+ port_ =
+ region_view()->IgnoreUntilBegin(connection_id());
+#endif
+
+ receiver_.reset(new Receiver{this});
+ return {ShmSender{this}, ShmReceiver{this}};
+}
+
+#ifdef CUTTLEFISH_HOST
+auto SocketForwardRegionView::ShmConnectionView::EstablishConnection(int port)
+ -> ShmSenderReceiverPair {
+ port_ = port;
+ return ResetAndConnect();
+}
+#else
+auto SocketForwardRegionView::ShmConnectionView::WaitForNewConnection()
+ -> ShmSenderReceiverPair {
+ port_ = 0;
+ return ResetAndConnect();
+}
+#endif
+
+bool SocketForwardRegionView::ShmConnectionView::Send(const Packet& packet) {
+ if (packet.empty()) {
+ LOG(ERROR) << "Sending empty packet";
+ }
+ if (packet.IsData() && IsOtherSideRecvClosed()) {
+ return false;
+ }
+ return region_view()->Send(connection_id(), packet);
+}
+
+void SocketForwardRegionView::ShmConnectionView::Recv(Packet* packet) {
+ receiver_->Recv(packet);
+}
+
+void SocketForwardRegionView::ShmReceiver::Recv(Packet* packet) {
+ view_->Recv(packet);
+}
+
+bool SocketForwardRegionView::ShmSender::Send(const Packet& packet) {
+ return view_->Send(packet);
+}
diff --git a/common/vsoc/lib/socket_forward_region_view.h b/common/vsoc/lib/socket_forward_region_view.h
new file mode 100644
index 0000000..c41517b
--- /dev/null
+++ b/common/vsoc/lib/socket_forward_region_view.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2018 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 <cstdlib>
+#include <utility>
+#include <vector>
+#include <memory>
+
+#include "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+namespace vsoc {
+namespace socket_forward {
+
+struct Header {
+ std::uint32_t payload_length;
+ enum MessageType : std::uint32_t {
+ DATA = 0,
+ BEGIN,
+ END,
+ RECV_CLOSED, // indicate that this side's receive end is closed
+ RESTART,
+ };
+ MessageType message_type;
+};
+
+constexpr std::size_t kMaxPayloadSize =
+ layout::socket_forward::kMaxPacketSize - sizeof(Header);
+
+struct Packet {
+ private:
+ Header header_;
+ using Payload = char[kMaxPayloadSize];
+ Payload payload_data_;
+
+ static constexpr Packet MakePacket(Header::MessageType type) {
+ Packet packet{};
+ packet.header_.message_type = type;
+ return packet;
+ }
+
+ public:
+ // port is only revelant on the host-side.
+ static Packet MakeBegin(std::uint16_t port);
+
+ static constexpr Packet MakeEnd() { return MakePacket(Header::END); }
+
+ static constexpr Packet MakeRecvClosed() {
+ return MakePacket(Header::RECV_CLOSED);
+ }
+
+ static constexpr Packet MakeRestart() { return MakePacket(Header::RESTART); }
+
+ // NOTE payload and payload_length must still be set.
+ static constexpr Packet MakeData() { return MakePacket(Header::DATA); }
+
+ bool empty() const { return IsData() && header_.payload_length == 0; }
+
+ void set_payload_length(std::uint32_t length) {
+ CHECK_LE(length, sizeof payload_data_);
+ header_.payload_length = length;
+ }
+
+ Payload& payload() { return payload_data_; }
+
+ const Payload& payload() const { return payload_data_; }
+
+ constexpr std::uint32_t payload_length() const {
+ return header_.payload_length;
+ }
+
+ constexpr bool IsBegin() const {
+ return header_.message_type == Header::BEGIN;
+ }
+
+ constexpr bool IsEnd() const { return header_.message_type == Header::END; }
+
+ constexpr bool IsData() const { return header_.message_type == Header::DATA; }
+
+ constexpr bool IsRecvClosed() const {
+ return header_.message_type == Header::RECV_CLOSED;
+ }
+
+ constexpr bool IsRestart() const {
+ return header_.message_type == Header::RESTART;
+ }
+
+ constexpr std::uint16_t port() const {
+ CHECK(IsBegin());
+ std::uint16_t port_number{};
+ CHECK_EQ(payload_length(), sizeof port_number);
+ std::memcpy(&port_number, payload(), sizeof port_number);
+ return port_number;
+ }
+
+ char* raw_data() { return reinterpret_cast<char*>(this); }
+
+ const char* raw_data() const { return reinterpret_cast<const char*>(this); }
+
+ constexpr size_t raw_data_length() const {
+ return payload_length() + sizeof header_;
+ }
+};
+
+static_assert(sizeof(Packet) == layout::socket_forward::kMaxPacketSize, "");
+static_assert(std::is_pod<Packet>{}, "");
+
+// Data sent will start with a uint32_t indicating the number of bytes being
+// sent, followed be the data itself
+class SocketForwardRegionView
+ : public TypedRegionView<SocketForwardRegionView,
+ layout::socket_forward::SocketForwardLayout> {
+ private:
+ // Returns an empty data packet if the other side is closed.
+ void Recv(int connection_id, Packet* packet);
+ // Returns true on success
+ bool Send(int connection_id, const Packet& packet);
+
+ // skip everything in the connection queue until seeing a BEGIN packet.
+ // returns port from begin packet.
+ int IgnoreUntilBegin(int connection_id);
+
+ public:
+ class ShmSender;
+ class ShmReceiver;
+
+ using ShmSenderReceiverPair = std::pair<ShmSender, ShmReceiver>;
+
+ class ShmConnectionView {
+ public:
+ ShmConnectionView(SocketForwardRegionView* region_view, int connection_id)
+ : region_view_{region_view}, connection_id_{connection_id} {}
+
+#ifdef CUTTLEFISH_HOST
+ ShmSenderReceiverPair EstablishConnection(int port);
+#else
+ // Should not be called while there is an active ShmSender or ShmReceiver
+ // for this connection.
+ ShmSenderReceiverPair WaitForNewConnection();
+#endif
+
+ int port() const { return port_; }
+
+ bool Send(const Packet& packet);
+ void Recv(Packet* packet);
+
+ ShmConnectionView(const ShmConnectionView&) = delete;
+ ShmConnectionView& operator=(const ShmConnectionView&) = delete;
+
+ // Moving invalidates all existing ShmSenders and ShmReceiver
+ ShmConnectionView(ShmConnectionView&&) = default;
+ ShmConnectionView& operator=(ShmConnectionView&&) = default;
+ ~ShmConnectionView() = default;
+
+ // NOTE should only be used for debugging/logging purposes.
+ // connection_ids are an implementation detail that are currently useful for
+ // debugging, but may go away in the future.
+ int connection_id() const { return connection_id_; }
+
+ private:
+ SocketForwardRegionView* region_view() const { return region_view_; }
+
+ bool IsOtherSideRecvClosed() {
+ std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+ return other_side_receive_closed_;
+ }
+
+ void MarkOtherSideRecvClosed() {
+ std::lock_guard<std::mutex> guard(*other_side_receive_closed_lock_);
+ other_side_receive_closed_ = true;
+ }
+
+ void ReceiverThread();
+ ShmSenderReceiverPair ResetAndConnect();
+
+ class Receiver {
+ public:
+ Receiver(ShmConnectionView* view)
+ : view_{view}
+ {
+ receiver_thread_ = std::thread([this] { Start(); });
+ }
+
+ void Recv(Packet* packet);
+
+ void Join() { receiver_thread_.join(); }
+
+ Receiver(const Receiver&) = delete;
+ Receiver& operator=(const Receiver&) = delete;
+
+ ~Receiver() = default;
+ private:
+ void Start();
+ bool GotRecvClosed() const;
+ void ReceivePacket();
+ void CheckPacketForRecvClosed();
+ void CheckPacketForEnd();
+ void UpdatePacketAndSignalAvailable();
+ bool ShouldReceiveAnotherPacket() const;
+ bool ExpectMorePackets() const;
+
+ std::mutex receive_thread_data_lock_;
+ std::condition_variable receive_thread_data_cv_;
+ bool received_packet_free_ = true;
+ Packet received_packet_{};
+
+ ShmConnectionView* view_{};
+ bool saw_recv_closed_ = false;
+ bool saw_end_ = false;
+#ifdef CUTTLEFISH_HOST
+ bool saw_data_ = false;
+#endif
+
+ std::thread receiver_thread_;
+ };
+
+ SocketForwardRegionView* region_view_{};
+ int connection_id_ = -1;
+ int port_ = -1;
+
+ std::unique_ptr<std::mutex> other_side_receive_closed_lock_ =
+ std::unique_ptr<std::mutex>{new std::mutex{}};
+ bool other_side_receive_closed_ = false;
+
+ std::unique_ptr<Receiver> receiver_;
+ };
+
+ class ShmSender {
+ public:
+ explicit ShmSender(ShmConnectionView* view) : view_{view} {}
+
+ ShmSender(const ShmSender&) = delete;
+ ShmSender& operator=(const ShmSender&) = delete;
+
+ ShmSender(ShmSender&&) = default;
+ ShmSender& operator=(ShmSender&&) = default;
+ ~ShmSender() = default;
+
+ // Returns true on success
+ bool Send(const Packet& packet);
+
+ private:
+ struct EndSender {
+ void operator()(ShmConnectionView* view) const {
+ if (view) {
+ view->Send(Packet::MakeEnd());
+ }
+ }
+ };
+
+ // Doesn't actually own the View, responsible for sending the End
+ // indicator and marking the sending side as disconnected.
+ std::unique_ptr<ShmConnectionView, EndSender> view_;
+ };
+
+ class ShmReceiver {
+ public:
+ explicit ShmReceiver(ShmConnectionView* view) : view_{view} {}
+ ShmReceiver(const ShmReceiver&) = delete;
+ ShmReceiver& operator=(const ShmReceiver&) = delete;
+
+ ShmReceiver(ShmReceiver&&) = default;
+ ShmReceiver& operator=(ShmReceiver&&) = default;
+ ~ShmReceiver() = default;
+
+ void Recv(Packet* packet);
+
+ private:
+ struct RecvClosedSender {
+ void operator()(ShmConnectionView* view) const {
+ if (view) {
+ view->Send(Packet::MakeRecvClosed());
+ }
+ }
+ };
+
+ // Doesn't actually own the view, responsible for sending the RecvClosed
+ // indicator
+ std::unique_ptr<ShmConnectionView, RecvClosedSender> view_{};
+ };
+
+ friend ShmConnectionView;
+
+ SocketForwardRegionView() = default;
+ ~SocketForwardRegionView() = default;
+ SocketForwardRegionView(const SocketForwardRegionView&) = delete;
+ SocketForwardRegionView& operator=(const SocketForwardRegionView&) = delete;
+
+ using ConnectionViewCollection = std::vector<ShmConnectionView>;
+ ConnectionViewCollection AllConnections();
+
+ int port(int connection_id);
+ void CleanUpPreviousConnections();
+
+ private:
+#ifndef CUTTLEFISH_HOST
+ std::uint32_t last_seq_number_{};
+#endif
+};
+
+} // namespace socket_forward
+} // namespace vsoc
diff --git a/common/vsoc/lib/typed_region_view.h b/common/vsoc/lib/typed_region_view.h
new file mode 100644
index 0000000..ead7412
--- /dev/null
+++ b/common/vsoc/lib/typed_region_view.h
@@ -0,0 +1,104 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Object that represents a region on the Host
+
+#include "common/vsoc/lib/region_view.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+
+namespace vsoc {
+
+/**
+ * This class adds methods that depend on the Region's type.
+ * This may be directly constructed. However, it may be more effective to
+ * subclass it, adding region-specific methods.
+ *
+ * Layout should be VSoC shared memory compatible, defined in common/vsoc/shm,
+ * and should have a constant string region name.
+ */
+template <typename ViewType, typename LayoutType>
+class TypedRegionView : public RegionView {
+ public:
+ using Layout = LayoutType;
+
+ /* Returns a pointer to the region with a type that matches the layout */
+ LayoutType* data() {
+ return this->GetLayoutPointer<LayoutType>();
+ }
+
+ const LayoutType& data() const {
+ return this->region_offset_to_reference<LayoutType>(
+ control_->region_desc().offset_of_region_data);
+ }
+
+ protected:
+#if defined(CUTTLEFISH_HOST)
+ bool Open(const char* domain) {
+ return RegionView::Open(LayoutType::region_name, domain);
+ }
+#else
+ bool Open() {
+ return RegionView::Open(LayoutType::region_name);
+ }
+#endif
+
+ public:
+ // Implementation of the region singletons.
+#if defined(CUTTLEFISH_HOST)
+ static ViewType* GetInstance(const char* domain) {
+ static std::mutex mtx;
+ static std::map<std::string, std::unique_ptr<ViewType>> instances;
+ if (!domain) {
+ return nullptr;
+ }
+ std::lock_guard<std::mutex> lock(mtx);
+ // Get a reference to the actual unique_ptr that's stored in the map, if
+ // there wasn't one it will be default constructed pointing to nullptr.
+ auto& instance = instances[domain];
+ if (!instance) {
+ // Update the referenced pointer with the address of the newly created
+ // region view.
+ instance.reset(new ViewType{});
+ if (!instance->Open(domain)) {
+ instance.reset();
+ }
+ }
+ return instance.get();
+ }
+#else
+ static ViewType* GetInstance() {
+ static std::mutex mtx;
+ static std::unique_ptr<ViewType> instance;
+ std::lock_guard<std::mutex> lock(mtx);
+ if (!instance) {
+ instance.reset(new ViewType{});
+ if (!instance->Open()) {
+ instance.reset();
+ }
+ }
+ return instance.get();
+ }
+#endif
+
+
+};
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_audio_message.h b/common/vsoc/lib/vsoc_audio_message.h
new file mode 100644
index 0000000..230c4de
--- /dev/null
+++ b/common/vsoc/lib/vsoc_audio_message.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#include <system/audio.h>
+
+typedef uint32_t size32_t;
+
+struct timespec32 {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+
+ timespec32() = default;
+
+ timespec32(const timespec &from)
+ : tv_sec(from.tv_sec),
+ tv_nsec(from.tv_nsec) {
+ }
+};
+
+struct gce_audio_message {
+ static const size32_t kMaxAudioFrameLen = 65536;
+ enum message_t {
+ UNKNOWN = 0,
+ DATA_SAMPLES = 1,
+ OPEN_INPUT_STREAM = 2,
+ OPEN_OUTPUT_STREAM = 3,
+ CLOSE_INPUT_STREAM = 4,
+ CLOSE_OUTPUT_STREAM = 5,
+ CONTROL_PAUSE = 100
+ };
+ // Size of the header + data. Used to frame when we're on TCP.
+ size32_t total_size;
+ // Size of the audio header
+ size32_t header_size;
+ message_t message_type;
+ // Identifier for the stream.
+ uint32_t stream_number;
+ // HAL assigned frame number, starts from 0.
+ int64_t frame_num;
+ // MONOTONIC_TIME when these frames were presented to the HAL.
+ timespec32 time_presented;
+ // Sample rate from the audio configuration.
+ uint32_t frame_rate;
+ // Channel mask from the audio configuration.
+ audio_channel_mask_t channel_mask;
+ // Format from the audio configuration.
+ audio_format_t format;
+ // Size of each frame in bytes.
+ size32_t frame_size;
+ // Number of frames that were presented to the HAL.
+ size32_t num_frames_presented;
+ // Number of frames that the HAL accepted.
+ // For blocking audio this will be the same as num_frames.
+ // For non-blocking audio this may be less.
+ size32_t num_frames_accepted;
+ // Count of the number of packets that were dropped because they would
+ // have blocked the HAL or exceeded the maximum message size.
+ size32_t num_packets_dropped;
+ // Count of the number of packets that were shortened to fit within
+ // kMaxAudioFrameLen.
+ size32_t num_packets_shortened;
+ // num_frames_presented (not num_frames_accepted) will follow here.
+
+ gce_audio_message() :
+ total_size(sizeof(gce_audio_message)),
+ header_size(sizeof(gce_audio_message)),
+ message_type(UNKNOWN),
+ stream_number(0),
+ frame_num(0),
+ frame_rate(0),
+ channel_mask(0),
+ format(AUDIO_FORMAT_DEFAULT),
+ frame_size(0),
+ num_frames_presented(0),
+ num_frames_accepted(0),
+ num_packets_dropped(0),
+ num_packets_shortened(0) {
+ time_presented.tv_sec = 0;
+ time_presented.tv_nsec = 0;
+ }
+};
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
new file mode 100644
index 0000000..017ab13
--- /dev/null
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/lib/vsoc_memory.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+#include <type_traits>
+
+#include "common/libs/glog/logging.h"
+#include "common/vsoc/shm/audio_data_layout.h"
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/e2e_test_region_layout.h"
+#include "common/vsoc/shm/gralloc_layout.h"
+#include "common/vsoc/shm/input_events_layout.h"
+#include "common/vsoc/shm/managed_e2e_test_region_layout.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "common/vsoc/shm/socket_forward_layout.h"
+
+#include "uapi/vsoc_shm.h"
+
+namespace {
+
+// Takes a vector of objects and returns a vector of pointers to those objects.
+template <typename T, typename R>
+std::vector<R*> GetConstPointers(const std::vector<T>& v) {
+ std::vector<R*> result;
+ result.reserve(v.size());
+ for (auto& element : v) {
+ result.push_back(&element);
+ }
+ return result;
+}
+} // namespace
+
+namespace vsoc {
+
+namespace {
+
+class VSoCRegionLayoutImpl : public VSoCRegionLayout {
+ public:
+ VSoCRegionLayoutImpl(const char* region_name, size_t layout_size,
+ int guest_to_host_signal_table_log_size,
+ int host_to_guest_signal_table_log_size,
+ const char* managed_by)
+ : region_name_(region_name),
+ layout_size_(layout_size),
+ guest_to_host_signal_table_log_size_(
+ guest_to_host_signal_table_log_size),
+ host_to_guest_signal_table_log_size_(
+ host_to_guest_signal_table_log_size),
+ managed_by_(managed_by) {
+ }
+ VSoCRegionLayoutImpl(const VSoCRegionLayoutImpl&) = default;
+
+ const char* region_name() const override { return region_name_; }
+ const char* managed_by() const override { return managed_by_; }
+
+ size_t layout_size() const override { return layout_size_; }
+ int guest_to_host_signal_table_log_size() const override {
+ return guest_to_host_signal_table_log_size_;
+ }
+ int host_to_guest_signal_table_log_size() const override {
+ return host_to_guest_signal_table_log_size_;
+ }
+
+ private:
+ const char* region_name_{};
+ const size_t layout_size_{};
+ const int guest_to_host_signal_table_log_size_{};
+ const int host_to_guest_signal_table_log_size_{};
+ const char* managed_by_{};
+};
+
+class VSoCMemoryLayoutImpl : public VSoCMemoryLayout {
+ public:
+ explicit VSoCMemoryLayoutImpl(std::vector<VSoCRegionLayoutImpl>&& regions)
+ : regions_(regions), region_idx_by_name_(GetNameToIndexMap(regions)) {
+ for (size_t i = 0; i < regions_.size(); ++i) {
+ // This link could be resolved later, but doing it here disables
+ // managed_by cycles among the regions.
+ if (regions[i].managed_by() &&
+ !region_idx_by_name_.count(regions[i].managed_by())) {
+ LOG(FATAL) << regions[i].region_name()
+ << " managed by unknown region: " << regions[i].managed_by()
+ << ". Manager Regions must be declared before the regions "
+ "they manage";
+ }
+ }
+ }
+
+ ~VSoCMemoryLayoutImpl() = default;
+
+ std::vector<const VSoCRegionLayout*> GetRegions() const {
+ static std::vector<const VSoCRegionLayout*> ret =
+ GetConstPointers<VSoCRegionLayoutImpl, const VSoCRegionLayout>(
+ regions_);
+ return ret;
+ }
+
+ const VSoCRegionLayout* GetRegionByName(
+ const char* region_name) const override {
+ if (!region_idx_by_name_.count(region_name)) {
+ return nullptr;
+ }
+ return ®ions_[region_idx_by_name_.at(region_name)];
+ }
+
+ protected:
+ VSoCMemoryLayoutImpl() = delete;
+ VSoCMemoryLayoutImpl(const VSoCMemoryLayoutImpl&) = delete;
+
+ // Helper function to allow the creation of the name to index map in the
+ // constructor and allow the field to be const
+ static std::map<const char*, size_t> GetNameToIndexMap(
+ const std::vector<VSoCRegionLayoutImpl>& regions) {
+ std::map<const char*, size_t> result;
+ for (size_t index = 0; index < regions.size(); ++index) {
+ auto region_name = regions[index].region_name();
+ if (result.count(region_name)) {
+ LOG(FATAL) << region_name << " used for more than one region";
+ }
+ result[region_name] = index;
+ }
+ return result;
+ }
+
+ std::vector<VSoCRegionLayoutImpl> regions_;
+ const std::map<const char*, size_t> region_idx_by_name_;
+};
+
+template <class R>
+VSoCRegionLayoutImpl ValidateAndBuildLayout(int g_to_h_signal_table_log_size,
+ int h_to_g_signal_table_log_size,
+ const char* managed_by = nullptr) {
+ // Double check that the Layout is a valid shm type.
+ ASSERT_SHM_COMPATIBLE(R);
+ return VSoCRegionLayoutImpl(R::region_name, sizeof(R),
+ g_to_h_signal_table_log_size,
+ h_to_g_signal_table_log_size, managed_by);
+}
+
+} // namespace
+
+VSoCMemoryLayout* VSoCMemoryLayout::Get() {
+ /*******************************************************************
+ * Make sure the first region is not the manager of other regions. *
+ * This error will only be caught on runtime!!!!! *
+ *******************************************************************/
+ static VSoCMemoryLayoutImpl layout(
+ {ValidateAndBuildLayout<layout::input_events::InputEventsLayout>(2, 2),
+ ValidateAndBuildLayout<layout::screen::ScreenLayout>(2, 2),
+ ValidateAndBuildLayout<layout::gralloc::GrallocManagerLayout>(2, 2),
+ ValidateAndBuildLayout<layout::gralloc::GrallocBufferLayout>(
+ 0, 0,
+ /* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
+ ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(7,
+ 7),
+ ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(1,
+ 1),
+ ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
+ 1, 1),
+ ValidateAndBuildLayout<layout::e2e_test::E2EManagerTestRegionLayout>(1,
+ 1),
+ ValidateAndBuildLayout<layout::e2e_test::E2EManagedTestRegionLayout>(1,
+ 1),
+ ValidateAndBuildLayout<layout::audio_data::AudioDataLayout>(2, 2)});
+
+ // We need this code to compile on both sides to enforce the static checks,
+ // but should only be used host side.
+#if !defined(CUTTLEFISH_HOST)
+ LOG(FATAL) << "Memory layout should not be used guest side, use region "
+ "classes or the vsoc driver directly instead.";
+#endif
+ return &layout;
+}
+
+} // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_memory.h b/common/vsoc/lib/vsoc_memory.h
new file mode 100644
index 0000000..49853f9
--- /dev/null
+++ b/common/vsoc/lib/vsoc_memory.h
@@ -0,0 +1,53 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <vector>
+
+namespace vsoc {
+
+class VSoCRegionLayout {
+ public:
+ virtual const char* region_name() const = 0;
+ virtual const char* managed_by() const = 0;
+
+ virtual size_t layout_size() const = 0;
+ virtual int guest_to_host_signal_table_log_size() const = 0;
+ virtual int host_to_guest_signal_table_log_size() const = 0;
+ protected:
+ VSoCRegionLayout() = default;
+ virtual ~VSoCRegionLayout() = default;
+};
+
+class VSoCMemoryLayout {
+ public:
+ // Returns a pointer to the memory layout singleton.
+ static VSoCMemoryLayout* Get();
+
+ VSoCMemoryLayout(const VSoCMemoryLayout&) = delete;
+ VSoCMemoryLayout(VSoCMemoryLayout&&) = delete;
+
+ virtual std::vector<const VSoCRegionLayout *> GetRegions() const = 0;
+ virtual const VSoCRegionLayout* GetRegionByName(
+ const char* region_name) const = 0;
+ protected:
+ VSoCMemoryLayout() = default;
+ virtual ~VSoCMemoryLayout() = default;
+};
+
+} // namespace vsoc
diff --git a/common/vsoc/shm/audio_data_layout.h b/common/vsoc/shm/audio_data_layout.h
new file mode 100644
index 0000000..3991ea0
--- /dev/null
+++ b/common/vsoc/shm/audio_data_layout.h
@@ -0,0 +1,38 @@
+#pragma once
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Memory layout for region carrying audio data from audio HAL to client.
+
+namespace vsoc {
+namespace layout {
+namespace audio_data {
+
+struct AudioDataLayout : public RegionLayout {
+ static const char *const region_name;
+ static constexpr size_t layout_size = 16396;
+
+ // size = 2^14 = 16384, packets are up to 4KB bytes each.
+ CircularPacketQueue<14, 4096> audio_queue;
+};
+ASSERT_SHM_COMPATIBLE(AudioDataLayout);
+
+} // namespace audio_data
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/base.h b/common/vsoc/shm/base.h
new file mode 100644
index 0000000..f5abec8
--- /dev/null
+++ b/common/vsoc/shm/base.h
@@ -0,0 +1,82 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <type_traits>
+
+// Base macros for all layout structures.
+// ShmTypeValidator provides meaningful information about the type size
+// mismatch in compilation error messages, eg.
+//
+// error:
+// static_assert failed "Class size changed, update the layout_size field"
+// static_assert(Current == Expected,
+// note: in instantiation of template class
+// 'ShmTypeValidator<vsoc::layout::myclass::ClassName>'
+// requested here ASSERT_SHM_COMPATIBLE(ClassName);
+//
+template <typename Type, size_t expected_size = Type::layout_size>
+struct ShmTypeValidator {
+ static_assert(sizeof(Type) == expected_size,
+ "Class size changed, update the layout_size field");
+ static_assert(std::is_trivial<Type>(), "Class uses features that are unsafe");
+ static constexpr bool valid =
+ sizeof(Type) == expected_size && std::is_trivial<Type>();
+};
+
+#define ASSERT_SHM_COMPATIBLE(T) \
+ static_assert(ShmTypeValidator<T>::valid, \
+ "Compilation error. Please fix above errors and retry.")
+
+namespace vsoc {
+namespace layout {
+
+/**
+ * Memory is shared between Guest and Host kernels. In some cases we need
+ * flag to indicate which side we're on. In those cases we'll use this
+ * simple structure.
+ *
+ * These are carefully formatted to make Guest and Host a bitfield.
+ */
+enum Sides : uint32_t {
+ NoSides = 0,
+ Guest = 1,
+ Host = 2,
+ Both = 3,
+#ifdef CUTTLEFISH_HOST
+ OurSide = Host,
+ Peer = Guest
+#else
+ OurSide = Guest,
+ Peer = Host
+#endif
+};
+// Enums can't have static members, so can't use the macro here.
+ static_assert(ShmTypeValidator<Sides, 4>::valid,
+ "Compilation error. Please fix above errors and retry.");
+
+/**
+ * Base class for all region layout structures.
+ */
+class RegionLayout {
+public:
+ static constexpr size_t layout_size = 1;
+};
+ASSERT_SHM_COMPATIBLE(RegionLayout);
+
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/circqueue.h b/common/vsoc/shm/circqueue.h
new file mode 100644
index 0000000..05893bf
--- /dev/null
+++ b/common/vsoc/shm/circqueue.h
@@ -0,0 +1,203 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for byte-oriented circular queues
+
+#include <atomic>
+#include <cstdint>
+#include "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+
+struct iovec;
+
+namespace vsoc {
+class RegionSignalingInterface;
+namespace layout {
+
+/**
+ * Base classes for all spinlock protected circular queues.
+ * This class should be embedded in the per-region data structure that is used
+ * as the parameter to TypedRegion.
+ */
+template <uint32_t SizeLog2>
+class CircularQueueBase {
+ public:
+ static constexpr size_t layout_size = (1 << SizeLog2) + 12;
+
+ private:
+ CircularQueueBase() = delete;
+ CircularQueueBase(const CircularQueueBase&) = delete;
+ CircularQueueBase& operator=(const CircularQueueBase&) = delete;
+
+ protected:
+ /**
+ * Specifies a part of the queue. Note, the given indexes must be masked
+ * before they can be used against buffer_
+ */
+ struct Range {
+ // Points to the first bytes that is part of the range
+ uint32_t start_idx;
+ // Points to the first byte that is not in the range. This is similar to
+ // the STL end iterator.
+ uint32_t end_idx;
+ };
+ static const uintptr_t BufferSize = (1 << SizeLog2);
+
+ /**
+ * Copy bytes from buffer_in into the part of the queue specified by Range.
+ */
+ void CopyInRange(const char* buffer_in, const Range& t);
+
+ /**
+ * Copy the bytes specified by range to the given buffer. They caller must
+ * ensure that the buffer is large enough to hold the content of the range.
+ */
+ void CopyOutRange(const Range& t, char* buffer_out);
+
+ /**
+ * Wait until data becomes available in the queue. The caller must have
+ * called Lock() before invoking this. The caller must call Unlock()
+ * after this returns.
+ */
+ void WaitForDataLocked(RegionSignalingInterface* r);
+
+ /**
+ * Reserve space in the queue for writing. The caller must have called Lock()
+ * before invoking this. The caller must call Unlock() after this returns.
+ * Indexes pointing to the reserved space will be placed in range.
+ * On success this returns bytes.
+ * On failure a negative errno indicates the problem. -ENOSPC indicates that
+ * bytes > the queue size, -EWOULDBLOCK indicates that the call would block
+ * waiting for space but was requested non bloking.
+ */
+ intptr_t WriteReserveLocked(RegionSignalingInterface* r, size_t bytes,
+ Range* t, bool non_blocking);
+
+ bool RecoverBase() {
+ return lock_.Recover();
+ }
+
+ // Note: Both of these fields may hold values larger than the buffer size,
+ // they should be interpreted modulo the buffer size. This fact along with the
+ // buffer size being a power of two greatly simplyfies the index calculations.
+ // Advances when a reader has finished with buffer space
+ std::atomic<uint32_t> r_released_;
+ // Advances when buffer space is filled and ready for a reader
+ std::atomic<uint32_t> w_pub_;
+ // Spinlock that protects the region. 0 means unlocked
+ SpinLock lock_;
+ // The actual memory in the buffer
+ char buffer_[BufferSize];
+};
+using CircularQueueBase64k = CircularQueueBase<16>;
+ASSERT_SHM_COMPATIBLE(CircularQueueBase64k);
+
+/**
+ * Byte oriented circular queue. Reads will always return some data, but
+ * may return less data than requested. Writes will always write all of the
+ * data or return an error.
+ */
+template <uint32_t SizeLog2>
+class CircularByteQueue : public CircularQueueBase<SizeLog2> {
+ public:
+ static constexpr size_t layout_size =
+ CircularQueueBase<SizeLog2>::layout_size;
+ /**
+ * Read at most max_size bytes from the qeueue, placing them in buffer_out
+ */
+ intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
+ std::size_t max_size);
+ /**
+ * Write all of the given bytes into the queue. If non_blocking isn't set the
+ * call may block until there is enough available space in the queue. On
+ * success the return value will match bytes. On failure a negative errno is
+ * returned. -ENOSPC: If the queue size is smaller than the number of bytes to
+ * write. -EWOULDBLOCK: If non_blocking is true and there is not enough free
+ * space.
+ */
+ intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
+ std::size_t bytes, bool non_blocking = false);
+
+ bool Recover() {
+ return this->RecoverBase();
+ }
+
+ protected:
+ using Range = typename CircularQueueBase<SizeLog2>::Range;
+};
+using CircularByteQueue64k = CircularByteQueue<16>;
+ASSERT_SHM_COMPATIBLE(CircularByteQueue64k);
+
+/**
+ * Packet oriented circular queue. Reads will either return data or an error.
+ * Each return from read corresponds to a call to write and returns all of the
+ * data from that corresponding Write().
+ */
+template <uint32_t SizeLog2, uint32_t MaxPacketSize>
+class CircularPacketQueue : public CircularQueueBase<SizeLog2> {
+ public:
+ static constexpr size_t layout_size =
+ CircularQueueBase<SizeLog2>::layout_size;
+
+ /**
+ * Read a single packet from the queue, placing its data into buffer_out.
+ * If max_size indicates that buffer_out cannot hold the entire packet
+ * this function will return -ENOSPC.
+ */
+ intptr_t Read(RegionSignalingInterface* r, char* buffer_out,
+ std::size_t max_size);
+
+ /**
+ * Writes [buffer_in, buffer_in + bytes) to the queue.
+ * If the number of bytes to be written exceeds the size of the queue
+ * -ENOSPC will be returned.
+ * If non_blocking is true and there is not enough free space on the queue to
+ * write all the data -EWOULDBLOCK will be returned.
+ */
+ intptr_t Write(RegionSignalingInterface* r, const char* buffer_in,
+ uint32_t bytes, bool non_blocking = false);
+
+ /**
+ * Writes the data referenced by the given iov scatter/gather array to the
+ * queue.
+ * If the number of bytes to be written exceeds the size of the queue
+ * -ENOSPC will be returned.
+ * If non_blocking is true and there is not enough free space on the queue to
+ * write all the data -EWOULDBLOCK will be returned.
+ */
+ intptr_t Writev(
+ RegionSignalingInterface *r,
+ const iovec *iov,
+ size_t iov_count,
+ bool non_blocking = false);
+
+ bool Recover() {
+ return this->RecoverBase();
+ }
+
+ protected:
+ static_assert(CircularQueueBase<SizeLog2>::BufferSize >= MaxPacketSize,
+ "Buffer is too small to hold the maximum sized packet");
+ using Range = typename CircularQueueBase<SizeLog2>::Range;
+ intptr_t CalculateBufferedSize(size_t payload);
+};
+using CircularPacketQueue64k = CircularPacketQueue<16, 1024>;
+ASSERT_SHM_COMPATIBLE(CircularPacketQueue64k);
+
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/e2e_test_region_layout.h b/common/vsoc/shm/e2e_test_region_layout.h
new file mode 100644
index 0000000..f3ae615
--- /dev/null
+++ b/common/vsoc/shm/e2e_test_region_layout.h
@@ -0,0 +1,168 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <cstdint>
+#include "common/vsoc/shm/base.h"
+
+// Memory layout for a region that supports end-to-end (E2E) testing of
+// shared memory regions. This verifies that all sorts of things work along the
+// the path:
+//
+// host libraries <-> ivshmem server <-> kernel <-> guest libraries
+//
+// This is intentionally not a unit test. The primary source of errors along
+// this path is a misunderstanding and/or inconsistency in one of the
+// interfaces. Introducing mocks would allow these errors to go undetected.
+// Another way of looking at it is that the mocks would end up being a
+// a copy-and-paste job, making a series of change-detector tests.
+//
+// These tests are actually run on every device boot to verify that things are
+// ok.
+
+namespace vsoc {
+namespace layout {
+
+namespace e2e_test {
+
+/**
+ * Flags that are used to indicate test status. Some of the latter testing
+ * stages rely on initializion that must be done on the peer.
+ */
+enum E2ETestStage : uint32_t {
+ // No tests have passed
+ E2E_STAGE_NONE = 0,
+ // This side has finished writing its pattern to the region
+ E2E_MEMORY_FILLED = 1,
+ // This side has confirmed that it can see its peer's writes to the region
+ E2E_PEER_MEMORY_READ = 2,
+};
+static_assert(ShmTypeValidator<E2ETestStage, 4>::valid,
+ "Compilation error. Please fix above errors and retry.");
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+struct E2EMemoryFill {
+ static constexpr size_t layout_size = 64;
+
+ static const std::size_t kOwnedFieldSize = 32;
+
+ // The compiler must not attempt to optimize away reads and writes to the
+ // shared memory window. This is pretty typical when dealing with devices
+ // doing memory mapped I/O.
+ char host_writable[kOwnedFieldSize];
+ char guest_writable[kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EMemoryFill);
+
+/**
+ * Structure that grants permission to write in the region to either the guest
+ * or the host. This size of these fields is arbitrary.
+ */
+class E2ETestStageRegister {
+ public:
+ static constexpr size_t layout_size = 4;
+
+ E2ETestStage value() const {
+ return value_;
+ }
+
+ void set_value(E2ETestStage new_value) { value_ = new_value; }
+
+ protected:
+ // The compiler must not attempt to optimize away reads and writes to the
+ // shared memory window. This is pretty typical when dealing with devices
+ // doing memory mapped I/O.
+ E2ETestStage value_;
+};
+ASSERT_SHM_COMPATIBLE(E2ETestStageRegister);
+
+/**
+ * Describes the layout of the regions used for the end-to-end test. There
+ * are multiple regions: primary and secondary, so some details like the region
+ * name must wait until later.
+ */
+class E2ETestRegionLayout : public ::vsoc::layout::RegionLayout {
+ public:
+ static constexpr size_t layout_size = 2 * E2ETestStageRegister::layout_size +
+ 3 * 4 + E2EMemoryFill::layout_size;
+
+ /**
+ * Computes how many E2EMemoryFill records we need to cover the region.
+ * Covering the entire region during the test ensures that everything is
+ * mapped and coherent between guest and host.
+ */
+ static std::size_t NumFillRecords(std::size_t region_size) {
+ if (region_size < sizeof(E2ETestRegionLayout)) {
+ return 0;
+ }
+ // 1 + ... An array of size 1 is allocated in the E2ETestRegion.
+ // TODO(ghartman): AddressSanitizer may find this sort of thing to be
+ // alarming.
+ return 1 +
+ (region_size - sizeof(E2ETestRegionLayout)) / sizeof(E2EMemoryFill);
+ }
+ // The number of test stages that have completed on the guest
+ // Later host tests will wait on this
+ E2ETestStageRegister guest_status;
+ // The number of test stages that have completed on the host
+ // Later guest tests will wait on this
+ E2ETestStageRegister host_status;
+ // These fields are used to test the signaling mechanism.
+ std::atomic<uint32_t> host_to_guest_signal;
+ std::atomic<uint32_t> guest_to_host_signal;
+ std::atomic<uint32_t> guest_self_register;
+ // There rest of the region will be filled by guest_host_strings.
+ // We actually use more than one of these, but we can't know how many
+ // until we examine the region.
+ E2EMemoryFill data[1];
+};
+ASSERT_SHM_COMPATIBLE(E2ETestRegionLayout);
+
+struct E2EPrimaryTestRegionLayout : public E2ETestRegionLayout {
+ static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+ static const char* region_name;
+ static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+ static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2EPrimaryTestRegionLayout);
+
+struct E2ESecondaryTestRegionLayout : public E2ETestRegionLayout {
+ static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+ static const char* region_name;
+ static const char guest_pattern[E2EMemoryFill::kOwnedFieldSize];
+ static const char host_pattern[E2EMemoryFill::kOwnedFieldSize];
+};
+ASSERT_SHM_COMPATIBLE(E2ESecondaryTestRegionLayout);
+
+/**
+ * Defines an end-to-end region with a name that should never be configured.
+ */
+struct E2EUnfindableRegionLayout : public E2ETestRegionLayout {
+ static constexpr size_t layout_size = E2ETestRegionLayout::layout_size;
+
+ static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout);
+
+} // namespace e2e_test
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/gralloc_layout.h b/common/vsoc/shm/gralloc_layout.h
new file mode 100644
index 0000000..4da740f
--- /dev/null
+++ b/common/vsoc/shm/gralloc_layout.h
@@ -0,0 +1,75 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/graphics.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for the gralloc manager region.
+
+namespace vsoc {
+namespace layout {
+
+namespace gralloc {
+
+struct BufferEntry {
+ static constexpr size_t layout_size =
+ 7 * 4 + PixelFormatRegister::layout_size;
+
+ uint32_t owned_by;
+ uint32_t buffer_begin;
+ uint32_t buffer_end;
+
+ PixelFormatRegister pixel_format;
+ uint32_t stride;
+ uint32_t width;
+ uint32_t height;
+
+ // A size of 28 is causing different layouts when GrallocManagerLayout is
+ // compiled in host and guest sides
+ uint32_t padding;
+
+ uint32_t buffer_size() {
+ return buffer_end - buffer_begin;
+ }
+};
+ASSERT_SHM_COMPATIBLE(BufferEntry);
+
+struct GrallocBufferLayout : public RegionLayout {
+ static constexpr size_t layout_size = 1;
+ static const char* region_name;
+};
+ASSERT_SHM_COMPATIBLE(GrallocBufferLayout);
+
+struct GrallocManagerLayout : public RegionLayout {
+ static constexpr size_t layout_size =
+ 8 + GuestLock::layout_size + BufferEntry::layout_size;
+ static const char* region_name;
+ typedef GrallocBufferLayout ManagedRegion;
+
+ uint32_t allocated_buffer_memory;
+ uint32_t buffer_count;
+ // Make sure this isn't the first field
+ GuestLock new_buffer_lock;
+ // Needs to be last field
+ BufferEntry buffers_table[1];
+};
+ASSERT_SHM_COMPATIBLE(GrallocManagerLayout);
+
+} // namespace gralloc
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/graphics.h b/common/vsoc/shm/graphics.h
new file mode 100644
index 0000000..0aa7875
--- /dev/null
+++ b/common/vsoc/shm/graphics.h
@@ -0,0 +1,169 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for primitive graphics types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+
+// The enumerations for VSoC pixel formats are laid out so that hardware to
+// parse bytes per pixel without relying an a exhaustive list of pixel formats.
+// These constants define the fields involved.
+namespace PixelFormatConst {
+ static const uint32_t BytesPerPixelSize = 3;
+ static const uint32_t SubformatSize = 3;
+ static const uint32_t MaxBytesPerPixel = (1 << BytesPerPixelSize);
+ static const uint32_t MaxSubformat = (1 << SubformatSize) - 1;
+};
+
+
+// Builds (statically) a new pixel format enumeration value given constant
+// bytes per pixel.
+template <uint32_t BYTES, uint32_t SUB_FORMAT>
+struct PixelFormatBuilder {
+ static_assert(BYTES > 0, "Too few bytes");
+ static_assert(BYTES <= PixelFormatConst::MaxBytesPerPixel, "Too many bytes");
+ static_assert(SUB_FORMAT <= PixelFormatConst::MaxSubformat,
+ "Too many subformats");
+ static const uint32_t value = ((BYTES - 1) << PixelFormatConst::SubformatSize) | SUB_FORMAT;
+};
+
+template <uint32_t FORMAT>
+struct PixelFormatProperties {
+ // No static asserts since all int32_t values are (technically) valid pixel formats?
+ static const uint32_t bytes_per_pixel = (FORMAT >> PixelFormatConst::SubformatSize) + 1;
+};
+
+// Contains all of the pixel formats currently supported by this VSoC. The
+// enumeration serves multiple purposes:
+//
+// * The compile will warn (or error) if we switch on PixelFormat and don't
+// handly all of the cases.
+//
+// * Code can use PixelFormat to describe paramaters, making APIs a bit more
+// self-documenting.
+//
+// * Observant reviewers can verify that the same pixel value is not assigned
+// to multiple formats. Keep the enums in numerical order below to
+// make this easier.
+enum PixelFormat : uint32_t {
+ VSOC_PIXEL_FORMAT_UNINITIALIZED = PixelFormatBuilder<1,0>::value,
+ VSOC_PIXEL_FORMAT_BLOB = PixelFormatBuilder<1,1>::value,
+
+ VSOC_PIXEL_FORMAT_RGB_565 = PixelFormatBuilder<2,0>::value,
+ VSOC_PIXEL_FORMAT_YV12 = PixelFormatBuilder<2,1>::value,
+ VSOC_PIXEL_FORMAT_YCbCr_420_888 = PixelFormatBuilder<2,2>::value,
+
+ VSOC_PIXEL_FORMAT_RGB_888 = PixelFormatBuilder<3,0>::value,
+
+ VSOC_PIXEL_FORMAT_RGBA_8888 = PixelFormatBuilder<4,0>::value,
+ VSOC_PIXEL_FORMAT_RGBX_8888 = PixelFormatBuilder<4,1>::value,
+ VSOC_PIXEL_FORMAT_BGRA_8888 = PixelFormatBuilder<4,2>::value,
+
+ VSOC_PIXEL_FORMAT_RGBA_FP16 = PixelFormatBuilder<8,0>::value,
+
+ // VSOC_PIXEL_FORMAT_IMPLEMENTATION_DEFINED intentionally left out. The HALs
+ // should choose one of the defined contrete types.
+ //
+ // The following formats are defined in various platform versions, but don't
+ // seem to be used. If we encounter them it's ok to add them to the table.
+ // This does not necessitate a version change.
+ //
+ // The following have been in the framework for a long time:
+ //
+ // VSOC_PIXEL_FORMAT_YCrCb_420_SP
+ // VSOC_PIXEL_FORMAT_YCbCr_422_SP
+ //
+ // The following were added in JB_MR2:
+ //
+ // VSOC_PIXEL_FORMAT_YCbCr_420_888
+ // VSOC_PIXEL_FORMAT_Y8
+ // VSOC_PIXEL_FORMAT_Y16
+ //
+ // The following were added in L:
+ //
+ // VSOC_PIXEL_FORMAT_RAW_OPAQUE
+ // VSOC_PIXEL_FORMAT_RAW16 (also known as RAW_SENSOR. Define only RAW16)
+ // VSOC_PIXEL_FORMAT_RAW10
+ //
+ // The following were added in L MR1:
+ //
+ // VSOC_PIXEL_FORMAT_YCbCr_444_888
+ // VSOC_PIXEL_FORMAT_YCbCr_422_888
+ // VSOC_PIXEL_FORMAT_RAW12
+ // VSOC_PIXEL_FORMAT_FLEX_RGBA_8888
+ // VSOC_PIXEL_FORMAT_FLEX_RGB_888
+ //
+ // These pixel formats were removed in later framework versions. Implement
+ // only if absolutely necessary.
+ //
+ // Support was dropped in K for:
+ //
+ // VSOC_PIXEL_FORMAT_RGBA_5551
+ // VSOC_PIXEL_FORMAT_RGBA_4444
+ //
+ // Supported only in K, L, and LMR1:
+ //
+ // VSOC_PIXEL_FORMAT_sRGB_X_8888
+ // VSOC_PIXEL_FORMAT_sRGB_A_8888
+};
+// Enums can't have static members, so can't use the macro here.
+static_assert(ShmTypeValidator<PixelFormat, 4>::valid,
+ "Compilation error. Please fix above errors and retry.");
+
+namespace layout {
+
+// VSoC memory layout for a register that accepts a single pixel format.
+// The value is volatile to ensure that the compiler does not eliminate stores.
+struct PixelFormatRegister {
+ static constexpr size_t layout_size = 4;
+
+ volatile PixelFormat value_;
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatRegister);
+
+// Register layout for a mask giving different PixelFormats. Reserve enough
+// space to allow for future expansion. For example, we may well end with
+// a 12 bit per channel format in the future.
+struct PixelFormatMaskRegister {
+ static constexpr size_t layout_size = 8;
+
+ volatile uint64_t value_;
+
+ bool HasValue(PixelFormat in) {
+ return !!(value_ & (uint64_t(1) << in));
+ }
+};
+ASSERT_SHM_COMPATIBLE(PixelFormatMaskRegister);
+
+// Ensure that the mask is large enough to hold the highest encodable
+// pixel format.
+static_assert(PixelFormatBuilder<
+ PixelFormatConst::MaxBytesPerPixel,
+ PixelFormatConst::MaxSubformat>::value <
+ 8 * sizeof(PixelFormatMaskRegister),
+ "Largest pixel format does not fit in mask");
+} // layout
+} // vsoc
diff --git a/common/vsoc/shm/input_events_layout.h b/common/vsoc/shm/input_events_layout.h
new file mode 100644
index 0000000..737c903
--- /dev/null
+++ b/common/vsoc/shm/input_events_layout.h
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+
+// Memory layout for region carrying input events from host to guest
+
+namespace vsoc {
+namespace layout {
+
+namespace input_events {
+
+struct InputEventsLayout : public RegionLayout {
+ static constexpr size_t layout_size =
+ CircularPacketQueue<10, 256>::layout_size +
+ 2 * CircularPacketQueue<10, 16>::layout_size;
+
+ static const char* region_name;
+ // Event queues for the different input devices supported. Both the power
+ // button and the keyboard need only generate 2 input events for every
+ // 'hardware' event, so 16 bytes are enough, however when the touchscreen has
+ // multitouch enabled the number of generated events is significantly higher.
+ CircularPacketQueue<10, 256> touch_screen_queue;
+ CircularPacketQueue<10, 16> keyboard_queue;
+ CircularPacketQueue<10, 16> power_button_queue;
+};
+ASSERT_SHM_COMPATIBLE(InputEventsLayout);
+
+} // namespace input_events
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/lock.h b/common/vsoc/shm/lock.h
new file mode 100644
index 0000000..2049ba8
--- /dev/null
+++ b/common/vsoc/shm/lock.h
@@ -0,0 +1,252 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Memory layout for locks of all types.
+
+// The vsoc::layout namespace indicates that these are shared memory structure
+// definitions. The #include's given above are strictly limited, as are the
+// types that can be referenced below.
+
+// For _mm_pause()
+#if defined(__SSE2__)
+#include <x86intrin.h>
+#define _pause() _mm_pause()
+#elif defined(__arm__) || defined(__aarch64__)
+#include <arm_acle.h>
+#define _pause() __yield()
+#endif
+
+#include <atomic>
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+// Host userspace, guest userspace, and the guest kernel must all agree on
+// the relationship between std::atomic and atomic_t. That's hard to do without
+// examining assembly, and we can't really examing atomic_t outside of the
+// kernel tree, but we can at least assert that the host and the guest
+// agree on a size.
+static_assert(sizeof(std::atomic<uint32_t>) == 4, "std::atomic size mismatch");
+
+namespace vsoc {
+
+class RegionView;
+
+namespace layout {
+
+/**
+ * Lock that causes threads to busy loop rather than sleeping.
+ * This lock should never be used when the amount of work in the critical
+ * section cannot be bounded.
+ */
+class SpinLock {
+ public:
+ static constexpr size_t layout_size = 4;
+
+ /**
+ * Acquire the spinlock on the queue. This will effectively block all
+ * readers and writers.
+ */
+ void Lock() {
+ while (1) {
+ uint32_t expected = 0;
+ if (lock_.compare_exchange_strong(expected, Sides::OurSide)) {
+ return;
+ }
+ _pause();
+ }
+ }
+
+ /**
+ * Drop the lock iff it is currently held by this side. Used by
+ * recovery code that cleans up regions in the event of a reboot
+ * (guest side) or a service restart (host side).
+ *
+ * The caller must ensure that there are no other threads on its
+ * side (e.g. guest/host) are using the window.
+ */
+ bool Recover() {
+ uint32_t expected = Sides::OurSide;
+ return lock_.compare_exchange_strong(expected, 0);
+ }
+
+ /**
+ * Release the spinlock.
+ */
+ void Unlock() {
+ lock_ = 0;
+ }
+
+ protected:
+ std::atomic<uint32_t> lock_;
+};
+ASSERT_SHM_COMPATIBLE(SpinLock);
+
+/**
+ * This is a generic synchronization primitive that provides space for the
+ * owner of the lock to write platform-specific information.
+ */
+class WaitingLockBase {
+ public:
+ static constexpr size_t layout_size = 40;
+
+ protected:
+ // Common code to handle locking
+ // Must be called with the kernel's thread id
+ // Returns true if the lock was acquired. In this case the value in
+ // expected_vlaue is undefined.
+ // Returns false if locking failed. The value discovered in the lock word
+ // is returned in expected_value, and should probably be used in a conditional
+ // sleep.
+ bool TryLock(uint32_t tid, uint32_t* expected_value);
+
+ // Common code to handle unlocking.
+ // Must be called with the kernel's thread id
+ // Returns sides that should be signalled or 0
+ Sides UnlockCommon(uint32_t tid);
+
+ // Common code to recover single-sided locks.
+ bool RecoverSingleSided();
+
+ // Non-zero values in this word indicate that the lock is in use.
+ // This is 32 bits for compatibility with futex()
+ std::atomic<uint32_t> lock_uint32_;
+
+ // Pad so we line up with glib's pthread_mutex_t and can share the same queue.
+ // These fields may be redefined at any point in the future. They should not
+ // be used.
+ private:
+// These fields are known to be unused and are provided for compatibility
+// with glibc's locks.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-private-field"
+ uint32_t reserved_1_;
+ char reserved_2_[16];
+ // Provide scratch space for the owner of the lock. The content of this space
+ // is undefined when the lock is acquired. The owner may write to and read
+ // from it while it holds the lock, but must relinquish control before
+ // releasing the lock.
+ //
+ // This is intended to support Linux robust futexes. See the documentation
+ // in the kernel tree:
+ // Documentation/robust-futex-ABI.txt
+ public:
+ int64_t owner_scratch_[2];
+#pragma clang diagnostic pop
+};
+ASSERT_SHM_COMPATIBLE(WaitingLockBase);
+
+/**
+ * GuestLocks can be acquired and released only on the guest. They reside
+ * in the shared memory window because mutiple guest processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestLock : public WaitingLockBase {
+ public:
+ static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+#ifndef CUTTLEFISH_HOST
+ void Lock();
+ void Unlock();
+ /**
+ * Drop the lock iff it is currently held. Used by
+ * recovery code that cleans up regions in the event of a reboot.
+ *
+ * The caller must ensure that there are no other threads on its
+ * side (e.g. guest/host) are using the window.
+ */
+ bool Recover();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(GuestLock);
+
+/**
+ * HostLocks can be acquired and released only on the host. They reside
+ * in the shared memory window because mutiple host processes may need
+ * to coordinate activities in certain shared memory regions.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class HostLock : public WaitingLockBase {
+ public:
+ static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+#ifdef CUTTLEFISH_HOST
+ void Lock();
+ void Unlock();
+ /**
+ * Drop the lock iff it is currently held. Used by
+ * recovery code that cleans up regions in the event of a daemon
+ * restart.
+ *
+ * The caller must ensure that there are no other threads on its
+ * side (e.g. guest/host) are using the window.
+ */
+ bool Recover();
+#endif
+};
+ASSERT_SHM_COMPATIBLE(HostLock);
+
+/**
+ * GuestAndHostLocks can be acquired and released on either side of the
+ * shared memory window. The locks attempt to enforce fairness by using
+ * a round-trip signal:
+ *
+ * When a guest releases a lock this code sends a signal to wake the host,
+ * but not other guest waiters.
+ *
+ * The wake handler on the host wakes up and local waiters and then reposts
+ * the signal to the guest.
+ *
+ * When the guest receives the signal from the host it then wakes ups
+ * any waiters.
+ *
+ * A similar scenario applies when the host releases a lock with guest waiters.
+ *
+ * Signalling across the shared memory window twice has non-trivial cost.
+ * There are some optimizations in the code to prevent the full round-trip
+ * if the process releasing the lock can confirm that there are no waiters on
+ * the other side.
+ *
+ * Representing this as a concrete type allows for some optimizations when
+ * signalling on the lock.
+ */
+class GuestAndHostLock : public WaitingLockBase {
+ public:
+ static constexpr size_t layout_size = WaitingLockBase::layout_size;
+
+ void Lock(RegionView*);
+ void Unlock(RegionView*);
+ /**
+ * Drop the lock iff it is currently held by this side. Used by
+ * recovery code that cleans up regions in the event of a reboot
+ * (guest side) or a service restart (host side).
+ *
+ * The caller must ensure that there are no other threads on its
+ * side (e.g. guest/host) are using the window.
+ */
+ bool Recover(RegionView*);
+};
+ASSERT_SHM_COMPATIBLE(GuestAndHostLock);
+
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/managed_e2e_test_region_layout.h b/common/vsoc/shm/managed_e2e_test_region_layout.h
new file mode 100644
index 0000000..dc23f66
--- /dev/null
+++ b/common/vsoc/shm/managed_e2e_test_region_layout.h
@@ -0,0 +1,46 @@
+#pragma once
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+
+#include "common/vsoc/shm/base.h"
+
+namespace vsoc {
+namespace layout {
+
+namespace e2e_test {
+
+struct E2EManagedTestRegionLayout : public RegionLayout {
+ static constexpr size_t layout_size = 4;
+
+ static const char* region_name;
+ uint32_t val; // Not needed, here only to avoid an empty struct.
+};
+ASSERT_SHM_COMPATIBLE(E2EManagedTestRegionLayout);
+
+struct E2EManagerTestRegionLayout : public RegionLayout {
+ static constexpr size_t layout_size = 4 * 4;
+
+ static const char* region_name;
+ typedef E2EManagedTestRegionLayout ManagedRegion;
+ uint32_t data[4]; // We don't need more than 4 for the tests
+};
+ASSERT_SHM_COMPATIBLE(E2EManagerTestRegionLayout);
+
+} // namespace e2e_test
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/screen_layout.h b/common/vsoc/shm/screen_layout.h
new file mode 100644
index 0000000..7ad0ca3
--- /dev/null
+++ b/common/vsoc/shm/screen_layout.h
@@ -0,0 +1,76 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for screen region
+
+namespace vsoc {
+namespace layout {
+
+namespace screen {
+struct TimeSpec {
+ static constexpr size_t layout_size = 16;
+
+ int64_t ts_sec;
+ uint32_t ts_nsec;
+ // Host and guest compilers are giving the structure different sizes without
+ // this field.
+ uint32_t reserved;
+};
+ASSERT_SHM_COMPATIBLE(TimeSpec);
+
+struct CompositionStats {
+ static constexpr size_t layout_size = 4 + 2 * 2 + 5 * TimeSpec::layout_size;
+
+ uint32_t num_prepare_calls;
+ uint16_t num_layers;
+ uint16_t num_hwcomposited_layers;
+ TimeSpec last_vsync;
+ TimeSpec prepare_start;
+ TimeSpec prepare_end;
+ TimeSpec set_start;
+ TimeSpec set_end;
+};
+ASSERT_SHM_COMPATIBLE(CompositionStats);
+
+struct ScreenLayout : public RegionLayout {
+ static constexpr size_t layout_size = 24 + CompositionStats::layout_size;
+ static const char* region_name;
+ // Display properties
+ uint32_t x_res;
+ uint32_t y_res;
+ uint16_t dpi;
+ uint16_t refresh_rate_hz;
+
+ // Protects access to the frame offset, sequential number and stats.
+ // See the region implementation for more details.
+ SpinLock bcast_lock;
+ // The frame sequential number
+ std::atomic<uint32_t> seq_num;
+ // The index of the buffer containing the current frame.
+ int32_t buffer_index;
+ CompositionStats stats;
+ uint8_t buffer[0];
+};
+ASSERT_SHM_COMPATIBLE(ScreenLayout);
+
+} // namespace screen
+
+} // namespace layout
+} // namespace vsoc
diff --git a/common/vsoc/shm/socket_forward_layout.h b/common/vsoc/shm/socket_forward_layout.h
new file mode 100644
index 0000000..4a9beda
--- /dev/null
+++ b/common/vsoc/shm/socket_forward_layout.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/shm/base.h"
+#include "common/vsoc/shm/circqueue.h"
+#include "common/vsoc/shm/lock.h"
+
+// Memory layout for wifi packet exchange region.
+namespace vsoc {
+namespace layout {
+namespace socket_forward {
+
+constexpr std::size_t kMaxPacketSize = 8192;
+constexpr std::size_t kNumQueues = 16;
+
+struct Queue {
+ static constexpr size_t layout_size =
+ CircularPacketQueue<16, kMaxPacketSize>::layout_size;
+
+ CircularPacketQueue<16, kMaxPacketSize> queue;
+
+ bool Recover() { return queue.Recover(); }
+};
+ASSERT_SHM_COMPATIBLE(Queue);
+
+struct QueuePair {
+ static constexpr size_t layout_size = 2 * Queue::layout_size;
+
+ // Traffic originating from host that proceeds towards guest.
+ Queue host_to_guest;
+ // Traffic originating from guest that proceeds towards host.
+ Queue guest_to_host;
+
+ bool Recover() {
+ bool recovered = false;
+ recovered = recovered || host_to_guest.Recover();
+ recovered = recovered || guest_to_host.Recover();
+ return recovered;
+ }
+};
+ASSERT_SHM_COMPATIBLE(QueuePair);
+
+struct SocketForwardLayout : public RegionLayout {
+ static constexpr size_t layout_size = QueuePair::layout_size * kNumQueues;
+
+ bool Recover() {
+ bool recovered = false;
+ for (auto& i : queues_) {
+ bool rval = i.Recover();
+ recovered = recovered || rval;
+ }
+ return recovered;
+ }
+
+ QueuePair queues_[kNumQueues];
+ static const char* region_name;
+};
+
+ASSERT_SHM_COMPATIBLE(SocketForwardLayout);
+
+} // namespace socket_forward
+} // namespace layout
+} // namespace vsoc
diff --git a/guest/commands/vsoc_input_service/Android.bp b/guest/commands/vsoc_input_service/Android.bp
index f2e197f..73ca067 100644
--- a/guest/commands/vsoc_input_service/Android.bp
+++ b/guest/commands/vsoc_input_service/Android.bp
@@ -33,6 +33,7 @@
"libcuttlefish_fs",
"libbase",
"liblog",
+ "vsoc_lib",
],
header_libs: [
"cuttlefish_glog",
diff --git a/guest/commands/vsoc_input_service/vsoc_input_service.cpp b/guest/commands/vsoc_input_service/vsoc_input_service.cpp
index 8b75b45..8753735 100644
--- a/guest/commands/vsoc_input_service/vsoc_input_service.cpp
+++ b/guest/commands/vsoc_input_service/vsoc_input_service.cpp
@@ -28,6 +28,7 @@
#include "common/libs/fs/shared_fd.h"
#include "common/libs/device_config/device_config.h"
+#include "common/vsoc/lib/input_events_region_view.h"
using vsoc::input_events::InputEvent;
using vsoc_input_service::VirtualDeviceBase;
diff --git a/guest/commands/vsoc_input_service/vsoc_input_service.h b/guest/commands/vsoc_input_service/vsoc_input_service.h
index edf196d..2282b40 100644
--- a/guest/commands/vsoc_input_service/vsoc_input_service.h
+++ b/guest/commands/vsoc_input_service/vsoc_input_service.h
@@ -21,18 +21,6 @@
#include "virtual_power_button.h"
#include "virtual_touchscreen.h"
-namespace vsoc {
-namespace input_events {
-
-struct InputEvent {
- uint16_t type;
- uint16_t code;
- uint32_t value;
-};
-
-} // namespace input_events
-} // namespace vsoc
-
namespace vsoc_input_service {
class VSoCInputService {
diff --git a/guest/hals/audio/legacy/Android.mk b/guest/hals/audio/legacy/Android.mk
new file mode 100644
index 0000000..8d4ef0a
--- /dev/null
+++ b/guest/hals/audio/legacy/Android.mk
@@ -0,0 +1,65 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+ libcutils \
+ cuttlefish_auto_resources \
+ libcuttlefish_fs \
+ cuttlefish_time \
+ vsoc_lib
+
+LOCAL_HEADER_LIBRARIES := \
+ libhardware_headers
+
+LOCAL_SRC_FILES := \
+ audio_hal.cpp \
+ vsoc_audio.cpp \
+ vsoc_audio_input_stream.cpp \
+ vsoc_audio_output_stream.cpp
+
+LOCAL_C_INCLUDES := \
+ device/google/cuttlefish_common \
+ device/google/cuttlefish_kernel \
+ $(VSOC_STLPORT_INCLUDES) \
+ frameworks/native/include/media/hardware \
+ $(call include-path-for, audio)
+
+LOCAL_STATIC_LIBRARIES := \
+ libcutils \
+ libcuttlefish_remoter_framework \
+ $(VSOC_STLPORT_STATIC_LIBS)
+
+LOCAL_CFLAGS := \
+ -Wall -Werror -std=c++17 \
+ $(VSOC_VERSION_CFLAGS)
+
+
+LOCAL_MODULE := audio.primary.cutf_ivsh
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/legacy/audio_hal.cpp b/guest/hals/audio/legacy/audio_hal.cpp
new file mode 100644
index 0000000..a865d46
--- /dev/null
+++ b/guest/hals/audio/legacy/audio_hal.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+static hw_module_methods_t hal_module_methods = {
+ .open = cvd::GceAudio::Open,
+};
+
+
+audio_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = AUDIO_HARDWARE_MODULE_ID,
+ .name = "GCE Audio HW HAL",
+ .author = "The Android Open Source Project",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/guest/hals/audio/legacy/audio_hal.h b/guest/hals/audio/legacy/audio_hal.h
new file mode 100644
index 0000000..db547ff
--- /dev/null
+++ b/guest/hals/audio/legacy/audio_hal.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#define AUDIO_DEBUG 0
+
+#if AUDIO_DEBUG
+# define D(...) ALOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <log/log.h>
+
+#include <hardware/hardware.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wparentheses"
+#pragma GCC diagnostic ignored "-Wgnu-designator"
+#include <system/audio.h>
+#pragma GCC diagnostic pop
+
+#include <hardware/audio.h>
diff --git a/guest/hals/audio/legacy/policy/Android.mk b/guest/hals/audio/legacy/policy/Android.mk
new file mode 100644
index 0000000..0b2ef8c
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+LOCAL_MODULE_TAGS := optional
+LOCAL_MULTILIB := first
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils
+
+LOCAL_SRC_FILES := \
+ vsoc_audio_policy_hal.cpp
+
+LOCAL_C_INCLUDES := \
+ device/google/cuttlefish_common \
+ $(VSOC_STLPORT_INCLUDES) \
+ frameworks/native/include/media/hardware \
+ $(call include-path-for, audio)
+
+LOCAL_CFLAGS := -Wall -Werror
+
+LOCAL_MODULE := audio_policy.vsoc
+LOCAL_VENDOR_MODULE := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
new file mode 100644
index 0000000..53c3127
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <system/audio_policy.h>
+#include <hardware/audio_policy.h>
+
+#include "guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h"
+
+namespace cvd {
+
+int GceAudioPolicy::Create(
+ const audio_policy_device* device,
+ audio_policy_service_ops* aps_ops,
+ void* service, audio_policy** ap) {
+ D("%s", __FUNCTION__);
+ audio_policy_device* dev;
+ gce_audio_policy* dap;
+ int ret;
+
+ *ap = NULL;
+
+ if (!service || !aps_ops) {
+ return -EINVAL;
+ }
+
+ dap = (gce_audio_policy*) calloc(1, sizeof(*dap));
+ if (!dap) {
+ return -ENOMEM;
+ }
+
+ dap->policy.set_device_connection_state =
+ &GceAudioPolicy::SetDeviceConnectionState;
+ dap->policy.get_device_connection_state =
+ &GceAudioPolicy::GetDeviceConnectionState;
+ dap->policy.set_phone_state = &GceAudioPolicy::SetPhoneState;
+ dap->policy.set_ringer_mode = &GceAudioPolicy::SetRingerMode;
+ dap->policy.set_force_use = &GceAudioPolicy::SetForceUse;
+ dap->policy.get_force_use = &GceAudioPolicy::GetForceUse;
+ dap->policy.set_can_mute_enforced_audible =
+ &GceAudioPolicy::SetCanMuteEnforcedAudible;
+ dap->policy.init_check = &GceAudioPolicy::InitCheck;
+ dap->policy.get_output = &GceAudioPolicy::GetOutput;
+ dap->policy.start_output = &GceAudioPolicy::StartOutput;
+ dap->policy.stop_output = &GceAudioPolicy::StopOutput;
+ dap->policy.release_output = &GceAudioPolicy::ReleaseOutput;
+ dap->policy.get_input = &GceAudioPolicy::GetInput;
+ dap->policy.start_input = &GceAudioPolicy::StartInput;
+ dap->policy.stop_input = &GceAudioPolicy::StopInput;
+ dap->policy.release_input = &GceAudioPolicy::ReleaseInput;
+ dap->policy.init_stream_volume = &GceAudioPolicy::InitStreamVolume;
+ dap->policy.set_stream_volume_index = &GceAudioPolicy::SetStreamVolumeIndex;
+ dap->policy.get_stream_volume_index = &GceAudioPolicy::GetStreamVolumeIndex;
+ dap->policy.set_stream_volume_index_for_device =
+ &GceAudioPolicy::SetStreamVolumeIndexForDevice;
+ dap->policy.get_stream_volume_index_for_device =
+ &GceAudioPolicy::GetStreamVolumeIndexForDevice;
+ dap->policy.get_strategy_for_stream = &GceAudioPolicy::GetStrategyForStream;
+ dap->policy.get_devices_for_stream = &GceAudioPolicy::GetDevicesForStream;
+ dap->policy.get_output_for_effect = &GceAudioPolicy::GetOutputForEffect;
+ dap->policy.register_effect = &GceAudioPolicy::RegisterEffect;
+ dap->policy.unregister_effect = &GceAudioPolicy::UnregisterEffect;
+ dap->policy.set_effect_enabled = &GceAudioPolicy::SetEffectEnabled;
+ dap->policy.is_stream_active = &GceAudioPolicy::IsStreamActive;
+ dap->policy.dump = &GceAudioPolicy::Dump;
+#ifdef ENABLE_OFFLOAD
+ dap->policy.is_offload_supported = &GceAudioPolicy::IsOffloadSupported;
+#endif
+
+ dap->service = service;
+ dap->aps_ops = aps_ops;
+
+ *ap = &dap->policy;
+ return 0;
+}
+
+
+int GceAudioPolicy::Destroy(const audio_policy_device* ap_dev,
+ audio_policy* ap) {
+ D("%s", __FUNCTION__);
+ free(ap);
+ return 0;
+}
+
+
+int GceAudioPolicy::Close(hw_device_t* device) {
+ D("%s", __FUNCTION__);
+ free(device);
+ return 0;
+}
+
+
+int GceAudioPolicy::Open(
+ const hw_module_t* module, const char* name, hw_device_t** device) {
+ D("%s", __FUNCTION__);
+ audio_policy_device* dev;
+
+ *device = NULL;
+
+ if (strcmp(name, AUDIO_POLICY_INTERFACE) != 0) {
+ return -EINVAL;
+ }
+
+ dev = (audio_policy_device*) calloc(1, sizeof(*dev));
+ if (!dev) {
+ return -ENOMEM;
+ }
+
+ dev->common.tag = HARDWARE_DEVICE_TAG;
+ dev->common.version = 0;
+ dev->common.module = (hw_module_t*) module;
+ dev->common.close = &GceAudioPolicy::Close;
+ dev->create_audio_policy = &GceAudioPolicy::Create;
+ dev->destroy_audio_policy = &GceAudioPolicy::Destroy;
+
+ *device = &dev->common;
+
+ return 0;
+}
+
+}
+
+static hw_module_methods_t gce_audio_policy_module_methods = {
+ .open = &cvd::GceAudioPolicy::Open,
+};
+
+
+audio_policy_module HAL_MODULE_INFO_SYM = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = 1,
+ .version_minor = 0,
+ .id = AUDIO_POLICY_HARDWARE_MODULE_ID,
+ .name = "GCE Audio Policy HAL",
+ .author = "The Android Open Source Project",
+ .methods = &gce_audio_policy_module_methods,
+ },
+};
diff --git a/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
new file mode 100644
index 0000000..00ea3d1
--- /dev/null
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <errno.h>
+#include <string.h>
+#include <log/log.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#define AUDIO_DEBUG 1
+
+#if AUDIO_DEBUG
+# define D(...) ALOGD(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+#define LOG_TAG "GceAudioPolicy"
+
+namespace avd {
+
+struct gce_audio_policy {
+ audio_policy policy;
+
+ audio_policy_service_ops* aps_ops;
+ void* service;
+};
+
+
+class GceAudioPolicy {
+ public:
+ GceAudioPolicy() {}
+ ~GceAudioPolicy() {}
+
+ static int Open(
+ const hw_module_t* module, const char* name, hw_device_t** device);
+ static int Create(const audio_policy_device* device,
+ audio_policy_service_ops* aps_ops, void* service,
+ audio_policy** ap);
+ static int Destroy(const audio_policy_device* ap_dev,
+ audio_policy* ap);
+ static int Close(hw_device_t* device);
+
+ static int SetDeviceConnectionState(
+ audio_policy* pol, audio_devices_t device,
+ audio_policy_dev_state_t state, const char* device_address) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static audio_policy_dev_state_t GetDeviceConnectionState(
+ const audio_policy* pol, audio_devices_t device,
+ const char* device_address) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
+ }
+
+ static void SetPhoneState(audio_policy* pol, audio_mode_t state) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static void SetRingerMode(audio_policy* pol, uint32_t mode,
+ uint32_t mask) {
+ ALOGW("%s: deprecated", __FUNCTION__);
+ }
+
+ static void SetForceUse(
+ audio_policy* pol, audio_policy_force_use_t usage,
+ audio_policy_forced_cfg_t config) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static audio_policy_forced_cfg_t GetForceUse(
+ const audio_policy* pol, audio_policy_force_use_t usage) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return AUDIO_POLICY_FORCE_NONE;
+ }
+
+ static void SetCanMuteEnforcedAudible(
+ audio_policy* pol, bool can_mute) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static int InitCheck(const audio_policy* pol) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static audio_io_handle_t GetOutput(
+ audio_policy* pol, audio_stream_type_t stream,
+ uint32_t sampling_rate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_output_flags_t flags
+#ifdef ENABLE_OFFLOAD
+ , const audio_offload_info_t* info
+#endif
+ ) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static int StartOutput(audio_policy* pol, audio_io_handle_t output,
+ audio_stream_type_t stream, int session) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int StopOutput(audio_policy* pol, audio_io_handle_t output,
+ audio_stream_type_t stream, int session) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static void ReleaseOutput(
+ audio_policy* pol, audio_io_handle_t output) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static audio_io_handle_t GetInput(
+ audio_policy* pol, audio_source_t inputSource,
+ uint32_t sampling_rate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_in_acoustics_t acoustics) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static int StartInput(audio_policy* pol, audio_io_handle_t input) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int StopInput(audio_policy* pol, audio_io_handle_t input) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static void ReleaseInput(
+ audio_policy* pol, audio_io_handle_t input) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static void InitStreamVolume(audio_policy* pol,
+ audio_stream_type_t stream, int index_min,
+ int index_max) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ }
+
+ static int SetStreamVolumeIndex(audio_policy* pol,
+ audio_stream_type_t stream, int index) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int GetStreamVolumeIndex(const audio_policy* pol,
+ audio_stream_type_t stream, int* index) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int SetStreamVolumeIndexForDevice(
+ audio_policy* pol, audio_stream_type_t stream,
+ int index, audio_devices_t device) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int GetStreamVolumeIndexForDevice(
+ const audio_policy* pol, audio_stream_type_t stream,
+ int* index, audio_devices_t device) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static uint32_t GetStrategyForStream(const audio_policy* pol,
+ audio_stream_type_t stream) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static audio_devices_t GetDevicesForStream(const audio_policy* pol,
+ audio_stream_type_t stream) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static audio_io_handle_t GetOutputForEffect(
+ audio_policy* pol, const effect_descriptor_s* desc) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return 0;
+ }
+
+ static int RegisterEffect(
+ audio_policy* pol, const effect_descriptor_s* desc,
+ audio_io_handle_t output, uint32_t strategy, int session, int id) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int UnregisterEffect(audio_policy* pol, int id) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static int SetEffectEnabled(audio_policy* pol, int id, bool enabled) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+ static bool IsStreamActive(
+ const audio_policy* pol, audio_stream_type_t stream,
+ uint32_t in_past_ms) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return false;
+ }
+
+ static int Dump(const audio_policy* pol, int fd) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return -ENOSYS;
+ }
+
+#ifdef ENABLE_OFFLOAD
+ static bool IsOffloadSupported(const audio_policy* pol,
+ const audio_offload_info_t* info) {
+ ALOGE("%s: not supported", __FUNCTION__);
+ return false;
+ }
+#endif
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio.cpp b/guest/hals/audio/legacy/vsoc_audio.cpp
new file mode 100644
index 0000000..040e1e7
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <fcntl.h>
+
+extern "C" {
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/fs/shared_select.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/libs/threads/thunkers.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+#include "guest/hals/audio/legacy/vsoc_audio_output_stream.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+using cvd::LockGuard;
+using cvd::Mutex;
+
+namespace cvd {
+
+GceAudio::~GceAudio() { }
+
+int GceAudio::Close() {
+ D("GceAudio::%s", __FUNCTION__);
+ {
+ LockGuard<Mutex> guard(lock_);
+ for (std::list<GceAudioOutputStream*>::iterator it = output_list_.begin();
+ it != output_list_.end(); ++it) {
+ delete *it;
+ }
+ for (input_map_t::iterator it = input_map_.begin();
+ it != input_map_.end(); ++it) {
+ delete it->second;
+ }
+ }
+ delete this;
+ return 0;
+}
+
+size_t GceAudio::GetInputBufferSize(const audio_config*) const {
+ return IN_BUFFER_BYTES;
+}
+
+uint32_t GceAudio::GetSupportedDevices() const {
+ return AUDIO_DEVICE_OUT_EARPIECE |
+ AUDIO_DEVICE_OUT_SPEAKER |
+ AUDIO_DEVICE_OUT_DEFAULT |
+ AUDIO_DEVICE_IN_COMMUNICATION |
+ AUDIO_DEVICE_IN_BUILTIN_MIC |
+ AUDIO_DEVICE_IN_WIRED_HEADSET |
+ AUDIO_DEVICE_IN_VOICE_CALL |
+ AUDIO_DEVICE_IN_DEFAULT;
+}
+
+int GceAudio::InitCheck() const {
+ D("GceAudio::%s", __FUNCTION__);
+ return 0;
+}
+
+int GceAudio::SetMicMute(bool state) {
+ D("GceAudio::%s", __FUNCTION__);
+ LockGuard<Mutex> guard(lock_);
+ mic_muted_ = state;
+ return 0;
+}
+
+int GceAudio::GetMicMute(bool *state) const {
+ D("GceAudio::%s", __FUNCTION__);
+ LockGuard<Mutex> guard(lock_);
+ *state = mic_muted_;
+ return 0;
+}
+
+int GceAudio::OpenInputStream(audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_config *config,
+ audio_stream_in **stream_in,
+ audio_input_flags_t /*flags*/,
+ const char * /*address*/,
+ audio_source_t /*source*/) {
+ GceAudioInputStream* new_stream;
+ int rval = GceAudioInputStream::Open(
+ this, handle, devices, *config, &new_stream);
+ uint32_t stream_number;
+ if (new_stream) {
+ LockGuard<Mutex> guard(lock_);
+ stream_number = next_stream_number_++;
+ input_map_[stream_number] = new_stream;
+ }
+ // This should happen after the lock is released, hence the double check
+ if (new_stream) {
+ SendStreamUpdate(new_stream->GetStreamDescriptor(
+ stream_number, gce_audio_message::OPEN_INPUT_STREAM), MSG_DONTWAIT);
+ }
+ *stream_in = new_stream;
+ return rval;
+}
+
+
+void GceAudio::CloseInputStream(audio_stream_in *stream) {
+ GceAudioInputStream* astream = static_cast<GceAudioInputStream*>(stream);
+ gce_audio_message descriptor;
+ {
+ LockGuard<Mutex> guard(lock_);
+ // TODO(ghartman): This could be optimized if stream knew it's number.
+ for (input_map_t::iterator it = input_map_.begin();
+ it != input_map_.end(); ++it) {
+ if (it->second == stream) {
+ descriptor = it->second->GetStreamDescriptor(
+ it->first, gce_audio_message::CLOSE_INPUT_STREAM);
+ input_map_.erase(it);
+ break;
+ }
+ }
+ }
+ SendStreamUpdate(descriptor, MSG_DONTWAIT);
+ delete astream;
+}
+
+
+int GceAudio::OpenOutputStream(audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ audio_config *config,
+ audio_stream_out **stream_out,
+ const char * /*address*/) {
+ GceAudioOutputStream* new_stream;
+ int rval;
+ {
+ LockGuard<Mutex> guard(lock_);
+ rval = GceAudioOutputStream::Open(
+ this, handle, devices, flags, config, next_stream_number_++,
+ &new_stream);
+ if (new_stream) {
+ output_list_.push_back(new_stream);
+ }
+ }
+ if (new_stream) {
+ SendStreamUpdate(new_stream->GetStreamDescriptor(
+ gce_audio_message::OPEN_OUTPUT_STREAM), MSG_DONTWAIT);
+ }
+ *stream_out = new_stream;
+ return rval;
+}
+
+void GceAudio::CloseOutputStream(audio_stream_out *stream) {
+ GceAudioOutputStream* astream = static_cast<GceAudioOutputStream*>(stream);
+ gce_audio_message close;
+ {
+ LockGuard<Mutex> guard(lock_);
+ output_list_.remove(astream);
+ close = astream->GetStreamDescriptor(
+ gce_audio_message::CLOSE_OUTPUT_STREAM);
+ }
+ SendStreamUpdate(close, MSG_DONTWAIT);
+ delete astream;
+}
+
+int GceAudio::Dump(int fd) const {
+ LockGuard<Mutex> guard(lock_);
+ dprintf(
+ fd,
+ "\nadev_dump:\n"
+ "\tmic_mute: %s\n"
+ "\tnum_outputs: %zu\n"
+ "\tnum_inputs: %zu\n\n",
+ mic_muted_ ? "true": "false",
+ output_list_.size(), input_map_.size());
+
+ for (std::list<GceAudioOutputStream*>::const_iterator it =
+ output_list_.begin();
+ it != output_list_.end(); ++it) {
+ (*it)->common.dump(&(*it)->common, fd);
+ }
+
+ for (input_map_t::const_iterator it = input_map_.begin();
+ it != input_map_.end(); ++it) {
+ (*it).second->common.dump(&(*it).second->common, fd);
+ }
+
+ return 0;
+}
+
+ssize_t GceAudio::SendMsg(const msghdr& msg, int /* flags */) {
+ intptr_t res = audio_data_rv_->data()->audio_queue.Writev(
+ audio_data_rv_,
+ msg.msg_iov,
+ msg.msg_iovlen,
+ true /* non_blocking */);
+
+ if (res < 0) {
+ ALOGV("GceAudio::%s: CircularPacketQueue::Write returned %" PRIiPTR,
+ __FUNCTION__,
+ res);
+ }
+
+ return static_cast<ssize_t>(res);
+}
+
+ssize_t GceAudio::SendStreamUpdate(
+ const gce_audio_message& stream_info, int flags) {
+ msghdr msg;
+ iovec msg_iov[1];
+ msg_iov[0].iov_base = const_cast<gce_audio_message*>(&stream_info);
+ msg_iov[0].iov_len = sizeof(gce_audio_message);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = msg_iov;
+ msg.msg_iovlen = arraysize(msg_iov);
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ return SendMsg(msg, flags);
+}
+
+int GceAudio::SetVoiceVolume(float volume) {
+ D("GceAudio::%s: set voice volume %f", __FUNCTION__, volume);
+ voice_volume_ = volume;
+ return 0;
+}
+
+int GceAudio::SetMasterVolume(float volume) {
+ D("GceAudio::%s: set master volume %f", __FUNCTION__, volume);
+ master_volume_ = volume;
+ return 0;
+}
+
+int GceAudio::GetMasterVolume(float* volume) {
+ D("GceAudio::%s: get master volume %f", __FUNCTION__, master_volume_);
+ *volume = master_volume_;
+ return 0;
+}
+
+int GceAudio::SetMasterMute(bool muted) {
+ D("GceAudio::%s: set master muted %d", __FUNCTION__, muted);
+ master_muted_ = muted;
+ return 0;
+}
+
+int GceAudio::GetMasterMute(bool* muted) {
+ D("GceAudio::%s: get master muted %d", __FUNCTION__, master_muted_);
+ *muted = master_muted_;
+ return 0;
+}
+
+int GceAudio::SetMode(audio_mode_t mode) {
+ D("GceAudio::%s: new mode %d", __FUNCTION__, mode);
+ mode_ = mode;
+ return 0;
+}
+
+int GceAudio::Open(const hw_module_t* module, const char* name,
+ hw_device_t** device) {
+ D("GceAudio::%s", __FUNCTION__);
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
+ ALOGE("GceAudio::%s: invalid module name %s (expected %s)",
+ __FUNCTION__, name, AUDIO_HARDWARE_INTERFACE);
+ return -EINVAL;
+ }
+
+ GceAudio* rval = new GceAudio;
+
+ rval->audio_data_rv_ = AudioDataRegionView::GetInstance();
+ rval->audio_worker_ = rval->audio_data_rv_->StartWorker();
+
+ rval->common.tag = HARDWARE_DEVICE_TAG;
+ rval->common.version = version_;
+ rval->common.module = const_cast<hw_module_t *>(module);
+ rval->common.close = cvd::thunk<hw_device_t, &GceAudio::Close>;
+
+#if !defined(AUDIO_DEVICE_API_VERSION_2_0)
+ // This HAL entry is supported only on AUDIO_DEVICE_API_VERSION_1_0.
+ // In fact, with version 2.0 the device numbers were orgainized in a
+ // way that makes the return value nonsense.
+ // Skipping the assignment is ok: the memset in the constructor already
+ // put a NULL here.
+ rval->get_supported_devices =
+ cvd::thunk<audio_hw_device, &GceAudio::GetSupportedDevices>;
+#endif
+ rval->init_check = cvd::thunk<audio_hw_device, &GceAudio::InitCheck>;
+
+ rval->set_voice_volume =
+ cvd::thunk<audio_hw_device, &GceAudio::SetVoiceVolume>;
+ rval->set_master_volume =
+ cvd::thunk<audio_hw_device, &GceAudio::SetMasterVolume>;
+ rval->get_master_volume =
+ cvd::thunk<audio_hw_device, &GceAudio::GetMasterVolume>;
+
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+ rval->set_master_mute =
+ cvd::thunk<audio_hw_device, &GceAudio::SetMasterMute>;
+ rval->get_master_mute =
+ cvd::thunk<audio_hw_device, &GceAudio::GetMasterMute>;
+#endif
+
+ rval->set_mode = cvd::thunk<audio_hw_device, &GceAudio::SetMode>;
+ rval->set_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::SetMicMute>;
+ rval->get_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::GetMicMute>;
+
+ rval->set_parameters = cvd::thunk<audio_hw_device, &GceAudio::SetParameters>;
+ rval->get_parameters = cvd::thunk<audio_hw_device, &GceAudio::GetParameters>;
+
+ rval->get_input_buffer_size =
+ cvd::thunk<audio_hw_device, &GceAudio::GetInputBufferSize>;
+
+ rval->open_input_stream =
+ cvd::thunk<audio_hw_device, &GceAudio::OpenInputStreamCurrentHAL>;
+ rval->close_input_stream =
+ cvd::thunk<audio_hw_device, &GceAudio::CloseInputStream>;
+
+ rval->open_output_stream =
+ cvd::thunk<audio_hw_device, &GceAudio::OpenOutputStreamCurrentHAL>;
+ rval->close_output_stream =
+ cvd::thunk<audio_hw_device, &GceAudio::CloseOutputStream>;
+
+ rval->dump = cvd::thunk<audio_hw_device, &GceAudio::Dump>;
+
+ *device = &rval->common;
+ return 0;
+}
+
+int GceAudio::SetParameters(const char *kvpairs) {
+ ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+ if (kvpairs) D("GceAudio::%s: kvpairs %s", __FUNCTION__, kvpairs);
+ return 0;
+}
+
+
+char* GceAudio::GetParameters(const char *keys) const {
+ ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
+ if (keys) D("GceAudio::%s: kvpairs %s", __FUNCTION__, keys);
+ return strdup("");
+}
+
+int GceAudio::SetStreamParameters(
+ struct audio_stream *stream, const char *kv_pairs) {
+ struct str_parms *parms = str_parms_create_str(kv_pairs);
+ if (!parms) {
+ return 0;
+ }
+ int sample_rate;
+ if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+ &sample_rate) >= 0) {
+ stream->set_sample_rate(stream, sample_rate);
+ }
+ int format;
+ if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_FORMAT,
+ &format) >= 0) {
+ stream->set_format(stream, static_cast<audio_format_t>(format));
+ }
+ int routing;
+ if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ &routing) >= 0) {
+ stream->set_device(stream, static_cast<audio_devices_t>(routing));
+ }
+ if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+ &routing) >= 0) {
+ stream->set_device(stream, static_cast<audio_devices_t>(routing));
+ }
+ str_parms_destroy(parms);
+ return 0;
+}
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio.h b/guest/hals/audio/legacy/vsoc_audio.h
new file mode 100644
index 0000000..7b96878
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+
+namespace cvd {
+
+class GceAudioInputStream;
+class GceAudioOutputStream;
+
+class GceAudio : public audio_hw_device {
+ public:
+ // This common code manipulates the parameters of input and output streams.
+ static int SetStreamParameters(struct audio_stream *, const char *);
+
+ ~GceAudio();
+
+ // Non-HAL methods that are part of the GCE implementation.
+ // Most of these are used by the input and output streams.
+
+ // Returns true if the microphone is muted. Used by input streams.
+ bool IsMicrophoneMuted() {
+ cvd::LockGuard<cvd::Mutex> guard(lock_);
+ return mic_muted_;
+ }
+
+ // Send a message to the connected streamer.
+ // Returns:
+ // 0 if there is no streamer.
+ // >0 if the message was sent.
+ // -1 if there was an error.
+ ssize_t SendMsg(const msghdr&, int flags);
+
+ // Sends a stream update to the connected streamer.
+ // Stream updates have no frames. Use SendMsg if the message has frames.
+ // 0 if there is no streamer.
+ // >0 if the message was sent.
+ // -1 if there was an error.
+ ssize_t SendStreamUpdate(
+ const gce_audio_message& stream_info, int flags);
+
+ // Callbacks for the Android audio_module HAL interface.
+ // Most of the comments below are copied from
+ // libhardware/include/hardware/audio.h
+ //
+ // Where the is a conflict the comments there apply.
+ // By default these methods return 0 on success -<errno> for failure.
+
+ // Opens the device.
+ static int Open(const hw_module_t* module, const char* name,
+ hw_device_t** device);
+
+ // Closes the device, closing any open input streams and output streams.
+ int Close();
+
+ // Closes the input stream, throwing away any data in the buffer.
+ void CloseInputStream(audio_stream_in* stream);
+
+ // Closes the output stream without waiting for the buffer to clear.
+ void CloseOutputStream(audio_stream_out* stream);
+
+ // Creates an audio patch between several source and sink ports.
+ // The handle is allocated by the HAL and should be unique for this
+ // audio HAL module.
+ // TODO(ghartman): Implement this as part of HAL 3.0
+ //int CreateAudioPatch(unsigned int num_sources,
+ // const struct audio_port_config *sources,
+ // unsigned int num_sinks,
+ // const struct audio_port_config *sinks,
+ // audio_patch_handle_t *handle);
+
+ // dumps the state of the audio hardware to the given fd.
+ // This information can be retrieved using the dumpsys utility.
+ int Dump(int fd) const;
+
+ // Fills the list of supported attributes for a given audio port.
+ // As input, "port" contains the information (type, role, address etc...)
+ // needed by the HAL to identify the port.
+ // As output, "port" contains possible attributes (sampling rates, formats,
+ // channel masks, gain controllers...) for this port.
+ // TODO(ghartman): Implement this as part of HAL 3.0
+ // int GetAudioPort(struct audio_port *port);
+
+ // Sets audio port configuration
+ // TODO(ghartman): Implement this as part of HAL 3.0
+ // int SetAudioPortConfig(const struct audio_port_config *config);
+
+ size_t GetInputBufferSize(const audio_config*) const;
+
+ // Gets the current master volume value for the HAL, if the HAL supports
+ // master volume control. AudioFlinger will query this value from the
+ // primary audio HAL when the service starts and use the value for setting
+ // the initial master volume across all HALs. HALs which do not support
+ // this method may leave it set to NULL.
+ int GetMasterVolume(float* /*volume*/);
+
+ // Get the current master mute status for the HAL, if the HAL supports
+ // master mute control. AudioFlinger will query this value from the primary
+ // audio HAL when the service starts and use the value for setting the
+ // initial master mute across all HALs. HALs which do not support this
+ // method may leave it set to NULL.
+ int GetMasterMute(bool* muted);
+
+ // Gets the audio mute status for the microphone.
+ int GetMicMute(bool* state) const;
+
+ // Retrieves the global audio parameters.
+ // TODO(ghartman): Implement this.
+ char* GetParameters(const char* keys) const;
+
+ // Enumerates what devices are supported by each audio_hw_device
+ // implementation.
+ // Return value is a bitmask of 1 or more values of audio_devices_t
+ // used by audio flinger.
+ // NOTE: audio HAL implementations starting with
+ // AUDIO_DEVICE_API_VERSION_2_0 do not implement this function.
+ // AUDIO_DEVICE_API_VERSION_2_0 was the current version as of JB-MR1
+ // All supported devices should be listed in audio_policy.conf
+ // file and the audio policy manager must choose the appropriate
+ // audio module based on information in this file.
+ uint32_t GetSupportedDevices() const;
+
+ // Checks to see if the audio hardware interface has been initialized.
+ // Always returns 0 to indicate success, but -ENODEV is also allowed to
+ // indicate failure.
+ int InitCheck() const;
+
+ // Creates an additional hardware input stream.
+ // Additional parameters were added in the 3.0 version of the HAL.
+ // These defaults make it easier to implement a cross-branch device.
+ int OpenInputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices, audio_config *config,
+ audio_stream_in **stream_in,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ const char* address = 0,
+ audio_source_t source = AUDIO_SOURCE_DEFAULT);
+
+ // Creates an additional output stream.
+ // The "address" parameter qualifies the "devices" audio device type if
+ // needed. On GCE we ignore it for now because we simulate a single SoC
+ // hw devices.
+ //
+ // The format format depends on the device type:
+ // Bluetooth devices use the MAC address of the device in the form
+ // "00:11:22:AA:BB:CC"
+ // USB devices use the ALSA card and device numbers in the form
+ // "card=X;device=Y"
+ // Other devices may use a number or any other string.
+ int OpenOutputStream(
+ audio_io_handle_t handle,
+ audio_devices_t devices, audio_output_flags_t flags,
+ audio_config* config, audio_stream_out** stream_out,
+ const char* address = 0);
+
+ // Releases an audio patch.
+ // TODO(ghartman): Implement this as part of HAL 3.0
+ //int ReleaseAudioPatch(audio_patch_handle_t handle);
+
+ // Sets the audio mute status for all audio activities. If any value other
+ // than 0 is returned, the software mixer will emulate this capability.
+ // The GCE implementation always returns 0.
+ int SetMasterMute(bool muted);
+
+ // Sets the audio volume for all audio activities other than voice call.
+ // Range between 0.0 and 1.0. If any value other than 0 is returned,
+ // the software mixer will emulate this capability.
+ // The GCE implementation always returns 0.
+ int SetMasterVolume(float volume);
+
+ // Sets the audio mute status for the microphone.
+ int SetMicMute(bool state);
+
+ // set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode
+ // is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is
+ // playing, and AUDIO_MODE_IN_CALL when a call is in progress.
+ int SetMode(audio_mode_t mode);
+
+ // Sets the global audio parameters.
+ // TODO(ghartman): Create a sensible implementation.
+ int SetParameters(const char* kvpairs);
+
+ // Sets the audio volume of a voice call. Range is between 0.0 and 1.0
+ int SetVoiceVolume(float volume);
+
+
+ private:
+ // HAL 3.0 modifies the signatures of OpenInputStream and OpenOutputStream.
+ // We don't want to fork the implementation, and we don't want #ifdefs all
+ // over the code. The current implementation defines OpenInputStream and
+ // OpenOutputStream with default values for the paramteres that were added,
+ // and then generates a HAL-specific wrapper to be used in the function
+ // table.
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+ typedef int OpenInputStreamHAL_t(
+ audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**,
+ audio_input_flags_t, const char*, audio_source_t);
+
+ int OpenInputStreamCurrentHAL(
+ audio_io_handle_t a, audio_devices_t b, audio_config* c,
+ audio_stream_in** d, audio_input_flags_t e, const char* f,
+ audio_source_t g) {
+ return OpenInputStream(a, b, c, d, e, f, g);
+ }
+
+ typedef int OpenOutputStreamHAL_t(
+ audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+ audio_config*, audio_stream_out**,
+ const char*);
+
+ int OpenOutputStreamCurrentHAL(
+ audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+ audio_config* d, audio_stream_out** e,
+ const char* f) {
+ return OpenOutputStream(a, b, c, d, e, f);
+ }
+#else
+ typedef int OpenInputStreamHAL_t(
+ audio_io_handle_t, audio_devices_t, audio_config*, audio_stream_in**);
+
+ int OpenInputStreamCurrentHAL(
+ audio_io_handle_t a, audio_devices_t b, audio_config* c,
+ audio_stream_in** d) {
+ return OpenInputStream(a, b, c, d);
+ }
+
+ typedef int OpenOutputStreamHAL_t(
+ audio_io_handle_t, audio_devices_t, audio_output_flags_t,
+ audio_config*, audio_stream_out**);
+
+ int OpenOutputStreamCurrentHAL(
+ audio_io_handle_t a, audio_devices_t b, audio_output_flags_t c,
+ audio_config* d, audio_stream_out** e) {
+ return OpenOutputStream(a, b, c, d, e);
+ }
+#endif
+
+ //TODO(ghartman): Update this when we support 3.0.
+#if defined(AUDIO_DEVICE_API_VERSION_2_0)
+ static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_2_0;
+#else
+ static const unsigned int version_ = AUDIO_DEVICE_API_VERSION_1_0;
+#endif
+
+ using AudioDataRegionView = vsoc::audio_data::AudioDataRegionView;
+ AudioDataRegionView* audio_data_rv_{};
+ std::unique_ptr<vsoc::RegionWorker> audio_worker_;
+
+ // Lock to protect the data below.
+ mutable cvd::Mutex lock_;
+ // State that is managed at the device level.
+ float voice_volume_;
+ float master_volume_;
+ bool master_muted_;
+ bool mic_muted_;
+ audio_mode_t mode_;
+ // There can be multiple input and output streams. This field is used
+ // to assign each one a unique identifier.
+ // TODO(ghartman): This can wrap after 2^32 streams. Ideally we should check
+ // the output_list_ to ensure that the stream number hasn't been assigned.
+ // However, streams don't really appear and disapper that often.
+ // We use the same counter for both input and output streams to make things
+ // a little easier on the client.
+ uint32_t next_stream_number_;
+ // List of the currently active output streams.
+ // Used to clean things up Close()
+ std::list<GceAudioOutputStream *> output_list_;
+ // List of the currently active input streams.
+ // Used to clean things up Close()
+ typedef std::map<uint32_t, GceAudioInputStream *> input_map_t;
+ input_map_t input_map_;
+
+ GceAudio() :
+ audio_hw_device(),
+ voice_volume_(0.0),
+ master_volume_(0.0),
+ master_muted_(false),
+ mic_muted_(false),
+ mode_(AUDIO_MODE_NORMAL),
+ next_stream_number_(1) { }
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
new file mode 100644
index 0000000..bb9e196
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstdint>
+#include <cinttypes>
+
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
+
+namespace cvd {
+
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+static inline size_t GceAudioFrameSize(const audio_stream_in* s) {
+ return audio_stream_in_frame_size(s);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_in* s) {
+
+ return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_in* s) {
+
+ return audio_stream_frame_size(&s->common);
+}
+#endif
+
+GceAudioInputStream::GceAudioInputStream(
+ cvd::GceAudio* dev, audio_devices_t devices, const audio_config& config)
+ : audio_stream_in(),
+ dev_(dev),
+ config_(config),
+ gain_(0.0),
+ device_(devices) {
+ common.get_sample_rate =
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetSampleRate>;
+ common.set_sample_rate =
+ cvd::thunk<audio_stream, &GceAudioInputStream::SetSampleRate>;
+ common.get_buffer_size =
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetBufferSize>;
+ common.get_channels =
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetChannels>;
+ common.get_device = cvd::thunk<audio_stream, &GceAudioInputStream::GetDevice>;
+ common.set_device = cvd::thunk<audio_stream, &GceAudioInputStream::SetDevice>;
+ common.get_format = cvd::thunk<audio_stream, &GceAudioInputStream::GetFormat>;
+ common.set_format = cvd::thunk<audio_stream, &GceAudioInputStream::SetFormat>;
+ common.standby = cvd::thunk<audio_stream, &GceAudioInputStream::Standby>;
+ common.dump = cvd::thunk<audio_stream, &GceAudioInputStream::Dump>;
+ common.set_parameters = GceAudio::SetStreamParameters;
+ common.get_parameters =
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetParameters>;
+ common.add_audio_effect =
+ cvd::thunk<audio_stream, &GceAudioInputStream::AddAudioEffect>;
+ common.remove_audio_effect = cvd::thunk<audio_stream,
+ &GceAudioInputStream::RemoveAudioEffect>;
+ set_gain = cvd::thunk<audio_stream_in, &GceAudioInputStream::SetGain>;
+ read = cvd::thunk<audio_stream_in,
+ &GceAudioInputStream::Read>;
+ get_input_frames_lost = cvd::thunk<audio_stream_in,
+ &GceAudioInputStream::GetInputFramesLost>;
+ frame_size_ = GceAudioFrameSize(this);
+ buffer_model_.reset(
+ new SimulatedInputBuffer(config_.sample_rate, GetBufferSize() /
+ frame_size_));
+ reported_lost_frames_ = 0;
+}
+
+gce_audio_message GceAudioInputStream::GetStreamDescriptor(
+ uint32_t stream_number, gce_audio_message::message_t event) {
+ gce_audio_message rval;
+ rval.message_type = event;
+ rval.stream_number = stream_number;
+ rval.frame_num = buffer_model_->GetCurrentItemNum();
+ rval.time_presented =
+ buffer_model_->GetLastUpdatedTime().SinceEpoch().GetTS();
+ rval.frame_rate = config_.sample_rate;
+ rval.channel_mask = config_.channel_mask;
+ rval.format = config_.format;
+ rval.frame_size = frame_size_;
+ return rval;
+}
+
+int GceAudioInputStream::Open(GceAudio* dev,
+ audio_io_handle_t /*handle*/,
+ audio_devices_t devices,
+ const audio_config& config,
+ GceAudioInputStream** stream_in) {
+ D("GceAudioInputStream::%s", __FUNCTION__);
+ *stream_in = new GceAudioInputStream(dev, devices, config);
+ return 0;
+}
+
+int GceAudioInputStream::SetFormat(audio_format_t format) {
+ config_.format = format;
+ frame_size_ = GceAudioFrameSize(this);
+ return 0;
+}
+
+int GceAudioInputStream::Dump(int fd) const {
+ D("GceAudioInputStream::%s", __FUNCTION__);
+ dprintf(
+ fd,
+ "\tInputSteam Dump:\n"
+ "\t\tsample rate: %u\n"
+ "\t\tbuffer size: %zu\n"
+ "\t\tchannel mask: %08x\n"
+ "\t\tformat: %d\n"
+ "\t\tdevice: %08x\n"
+ "\t\taudio dev: %p\n\n",
+ GetSampleRate(), GetBufferSize(),
+ GetChannels(), GetFormat(), device_, dev_);
+ return 0;
+}
+
+int GceAudioInputStream::SetSampleRate(uint32_t sample_rate) {
+ if (sample_rate != config_.sample_rate) {
+ config_.sample_rate = sample_rate;
+ buffer_model_.reset(
+ new SimulatedInputBuffer(sample_rate, GetBufferSize() / frame_size_));
+ reported_lost_frames_ = 0;
+ }
+ return 0;
+}
+
+char* GceAudioInputStream::GetParameters(const char* keys) const {
+ D("GceAudioInputStream::%s", __FUNCTION__);
+ if (keys) D("GceAudioInputStream::%s keys %s", __FUNCTION__, keys);
+
+ str_parms* query = str_parms_create_str(keys);
+ str_parms* reply = str_parms_create();
+
+ char value[256];
+ int ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ char* str;
+ if (ret >= 0) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, device_);
+ str = strdup(str_parms_to_str(reply));
+ } else {
+ str = strdup(keys);
+ }
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ return str;
+}
+
+
+ssize_t GceAudioInputStream::Read(void* buffer, size_t bytes) {
+ int64_t available = buffer_model_->RemoveFromInputBuffer(
+ bytes / frame_size_, false) * frame_size_;
+ ssize_t rval = available;
+ if ((rval != available) || (rval < 0)) {
+ ALOGE("GceAudioInputStream:%s got bad value from "
+ "RemoveFromInputBuffer %" PRId64, __FUNCTION__, available);
+ return -1;
+ }
+ memset(buffer, 0, rval);
+ return rval;
+}
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_input_stream.h b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
new file mode 100644
index 0000000..222194c
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "common/libs/utils/simulated_buffer.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+namespace cvd {
+
+namespace {
+static const int IN_BUFFER_BYTES = 4096;
+}
+
+class GceAudio;
+
+// Defines static callback functions for generic_stream_in HAL interface.
+class GceAudioInputStream : public audio_stream_in {
+ public:
+ // These methods are internal to the GCE audio implementation.
+ // Factory for new input streams.
+ static int Open(
+ GceAudio* dev, audio_io_handle_t handle,
+ audio_devices_t devices, const audio_config& config,
+ GceAudioInputStream** stream_in);
+
+ // Gets a description of this stream
+ gce_audio_message GetStreamDescriptor(
+ uint32_t stream_number, gce_audio_message::message_t event);
+
+ // audio_stream_in implementation. These definitions follow the ones
+ // in hardware/libhardware/include/hardware/audio.h
+
+ // Returns the sampling rate in Hz - eg. 44100.
+ uint32_t GetSampleRate() const { return config_.sample_rate; }
+
+ // Sets the sample rate
+ // no direct calls from JB and later, but called indirectly from
+ // GceAudio::SetStreamParamters when it finds
+ // AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+ int SetSampleRate(uint32_t rate);
+
+ // Returns the size of input/output buffer in bytes for this stream - eg.
+ // 4800.
+ // It should be a multiple of the frame size. See also get_input_buffer_size
+ size_t GetBufferSize() const {
+ return IN_BUFFER_BYTES;
+ }
+
+ // Returns the channel mask -
+ // e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+ audio_channel_mask_t GetChannels() const {
+ return config_.channel_mask;
+ }
+
+ // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+ audio_format_t GetFormat() const {
+ return config_.format;
+ }
+
+ // Sets the audio format
+ // no direct calls from JB and later, but called indirectly from
+ // GceAudio::SetStreamParamters when it finds
+ // AUDIO_PARAMETER_STREAM_FORMAT
+ int SetFormat(audio_format_t format);
+
+ // Puts the audio hardware input/output into standby mode.
+ // Driver should exit from standby mode at the next I/O operation.
+ // Returns 0 on success and <0 on failure.
+ int Standby() { return 0; }
+
+ // Dumps the state of the audio input/output device
+ int Dump(int fd) const;
+
+ // Returns the set of device(s) which this stream is connected to
+ audio_devices_t GetDevice() const {
+ return device_;
+ }
+
+ // Sets the device this stream is connected to.
+ // no direct calls from JB and later, but called indirectly from
+ // GceAudio::SetStreamParamters when it finds
+ // AUDIO_PARAMETER_STREAM_ROUTING for both input and output.
+ // AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by
+ // input streams only.
+ int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+ // sets audio stream parameters. The function accepts a list of
+ // parameter key value pairs in the form: key1=value1;key2=value2;...
+ //
+ // Some keys are reserved for standard parameters (See AudioParameter class)
+ //
+ // If the implementation does not accept a parameter change while
+ // the output is active but the parameter is acceptable otherwise, it must
+ // return -ENOSYS.
+ // The audio flinger will put the stream in standby and then change the
+ // parameter value.
+ // Uses GceAudio::SetStreamParameters
+
+ // Returns a pointer to a heap allocated string. The caller is responsible
+ // for freeing the memory for it using free().
+ char* GetParameters(const char* keys) const;
+
+ int AddAudioEffect(effect_handle_t /*effect*/) const {
+ return 0;
+ }
+
+ int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+ return 0;
+ }
+
+ // Input stream specific methods
+
+ // Sets the input gain for the audio driver. This method is for
+ // for future use as of M.
+ int SetGain(float gain) {
+ gain_ = gain;
+ return 0;
+ }
+
+ // Reads audio buffer in from audio driver. Returns number of bytes read, or
+ // a negative android::status_t. If at least one frame was read prior to the error,
+ // read should return that byte count and then return an error in the
+ // subsequent call.
+ ssize_t Read(void* buffer, size_t bytes);
+
+ // Return the amount of input frames lost in the audio driver since the
+ // last call of this function.
+ // Audio driver is expected to reset the value to 0 and restart counting
+ // upon returning the current value by this function call.
+ // Such loss typically occurs when the user space process is blocked
+ // longer than the capacity of audio driver buffers.
+ //
+ // Unit: the number of input audio frames
+ uint32_t GetInputFramesLost() {
+ int64_t cur_lost_frames = buffer_model_->GetLostInputItems();
+ uint32_t rval = cur_lost_frames - reported_lost_frames_;
+ reported_lost_frames_ = cur_lost_frames;
+ return rval;
+ }
+
+ private:
+ GceAudioInputStream(cvd::GceAudio* dev, audio_devices_t devices,
+ const audio_config& config);
+ std::unique_ptr<SimulatedInputBuffer> buffer_model_;
+ cvd::GceAudio *dev_;
+ audio_config config_;
+ float gain_;
+ audio_devices_t device_;
+ size_t frame_size_;
+ int64_t reported_lost_frames_;
+};
+
+}
diff --git a/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
new file mode 100644
index 0000000..571f95e
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory>
+
+#include <cutils/sockets.h>
+extern "C"{
+#include <cutils/str_parms.h>
+}
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/thunkers.h"
+#include "common/libs/time/monotonic_time.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio_output_stream.h"
+#include "guest/libs/remoter/remoter_framework_pkt.h"
+
+#if defined(AUDIO_DEVICE_API_VERSION_3_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+ return audio_stream_out_frame_size(s);
+}
+#elif defined(AUDIO_DEVICE_API_VERSION_2_0)
+static inline size_t GceAudioFrameSize(const audio_stream_out* s) {
+
+ return audio_stream_frame_size(&s->common);
+}
+#else
+static inline size_t GceAudioFrameSize(audio_stream_out* s) {
+
+ return audio_stream_frame_size(&s->common);
+}
+#endif
+
+namespace cvd {
+
+const size_t GceAudioOutputStream::kOutBufferSize;
+const size_t GceAudioOutputStream::kOutLatency;
+
+GceAudioOutputStream::GceAudioOutputStream(GceAudio* dev) :
+ audio_stream_out(),
+ dev_(dev),
+ device_(AUDIO_DEVICE_OUT_DEFAULT),
+ frame_count_(0),
+ left_volume_(0.0),
+ right_volume_(0.0) { }
+
+int GceAudioOutputStream::Dump(int fd) const {
+ D("GceAudioOutputStream::%s", __FUNCTION__);
+ dprintf(
+ fd,
+ "\tout_dump:\n"
+ "\t\tsample rate: %u\n"
+ "\t\tbuffer size: %zu\n"
+ "\t\tchannel mask: %08x\n"
+ "\t\tformat: %d\n"
+ "\t\tdevice: %08x\n"
+ "\t\taudio dev: %p\n\n",
+ GetSampleRate(),
+ GetBufferSize(),
+ GetChannels(),
+ GetFormat(),
+ device_,
+ dev_);
+ return 0;
+}
+
+int GceAudioOutputStream::GetNextWriteTimestamp(int64_t* nstime) const {
+ *nstime = cvd::time::Nanoseconds(
+ buffer_->GetNextOutputBufferItemTime().SinceEpoch()).count();
+ return 0;
+}
+
+namespace {
+struct StrParmsDestroyer {
+ void operator()(str_parms* parms) const {
+ if (parms) {
+ str_parms_destroy(parms);
+ }
+ }
+};
+
+typedef std::unique_ptr<str_parms, StrParmsDestroyer> StrParmsPtr;
+}
+
+int GceAudioOutputStream::SetParameters(const char* kv_pairs) {
+ int err = 0;
+ StrParmsPtr parms(str_parms_create_str(kv_pairs));
+ {
+ int fmt = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FORMAT, &fmt)
+ == 0) {
+ SetFormat(static_cast<audio_format_t>(fmt));
+ }
+ }
+ {
+ int sample_rate = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+ &sample_rate) == 0) {
+ SetSampleRate(static_cast<uint32_t>(sample_rate));
+ }
+ }
+ {
+ int routing = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_ROUTING,
+ &routing) == 0) {
+ device_ = static_cast<uint32_t>(routing);
+ }
+ }
+ {
+ int channels = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_CHANNELS,
+ &channels) == 0) {
+ message_header_.channel_mask = static_cast<audio_channel_mask_t>(channels);
+ }
+ }
+ {
+ int frame_count = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+ &frame_count) == 0) {
+ frame_count_ = static_cast<size_t>(frame_count);
+ }
+ }
+ {
+ int input_source = 0;
+ if (str_parms_get_int(parms.get(), AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
+ &input_source) == 0){
+ ALOGE("GceAudioOutputStream::%s AUDIO_PARAMETER_STREAM_INPUT_SOURCE"
+ " passed to an output stream", __FUNCTION__);
+ err = -EINVAL;
+ }
+ }
+ return err;
+}
+
+void GceAudioOutputStream::AddIntIfKeyPresent(
+ /*const */ str_parms* query, str_parms* reply, const char* key, int value) {
+ if (str_parms_get_str(query, key, NULL, 0) >= 0) {
+ str_parms_add_int(reply, key, value);
+ }
+}
+
+
+char* GceAudioOutputStream::GetParameters(const char* keys) const {
+ D("GceAudioOutputStream::%s", __FUNCTION__);
+ if (keys) D("%s keys %s", __FUNCTION__, keys);
+
+ StrParmsPtr query(str_parms_create_str(keys));
+ StrParmsPtr reply(str_parms_create());
+
+ AddIntIfKeyPresent(query.get(), reply.get(),
+ AUDIO_PARAMETER_STREAM_FORMAT,
+ static_cast<int>(GetFormat()));
+ AddIntIfKeyPresent(query.get(), reply.get(),
+ AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
+ static_cast<int>(GetSampleRate()));
+ AddIntIfKeyPresent(query.get(), reply.get(),
+ AUDIO_PARAMETER_STREAM_ROUTING,
+ static_cast<int>(device_));
+ AddIntIfKeyPresent(query.get(), reply.get(),
+ AUDIO_PARAMETER_STREAM_CHANNELS,
+ static_cast<int>(message_header_.channel_mask));
+ AddIntIfKeyPresent(query.get(), reply.get(),
+ AUDIO_PARAMETER_STREAM_FRAME_COUNT,
+ static_cast<int>(frame_count_));
+
+ char *str = str_parms_to_str(reply.get());
+ return str;
+}
+
+int GceAudioOutputStream::GetRenderPosition(uint32_t* dsp_frames) const {
+ *dsp_frames = buffer_->GetCurrentItemNum();
+ return 0;
+}
+
+ssize_t GceAudioOutputStream::Write(const void* buffer, size_t length) {
+ // We're always the blocking case for now.
+ static const bool blocking = true;
+ message_header_.frame_size = frame_size_;
+ frame_count_ += message_header_.num_frames_presented = length / frame_size_;
+ message_header_.message_type = gce_audio_message::DATA_SAMPLES;
+ // First do a nonblocking add
+ int64_t frames_accepted_without_blocking = buffer_->AddToOutputBuffer(
+ message_header_.num_frames_presented, false);
+ // This seems backward, but adding the items to the buffer first
+ // allows us to calculate the right frame number in the case of underflow.
+ message_header_.frame_num =
+ buffer_->GetNextOutputBufferItemNum() - frames_accepted_without_blocking;
+ message_header_.time_presented =
+ buffer_->GetLastUpdatedTime().SinceEpoch().GetTS();
+ // We want to send the message before blocking. If we're in blocking mode
+ // we will accept all of the frames.
+ if (blocking) {
+ message_header_.num_frames_accepted =
+ message_header_.num_frames_presented;
+ } else {
+ message_header_.num_frames_accepted = frames_accepted_without_blocking;
+ }
+ // Never exceed the maximum packet size, as defined by the interface.
+ // Clip off any frames that we can't transmit and increment the clipped
+ // count.
+ size_t transmitted_frame_size = length;
+ if (length > gce_audio_message::kMaxAudioFrameLen) {
+ transmitted_frame_size = gce_audio_message::kMaxAudioFrameLen;
+ message_header_.num_packets_shortened++;
+ }
+ message_header_.total_size =
+ sizeof(message_header_) + transmitted_frame_size;
+ // Now send the message. Do not block if the receiver isn't ready
+ // If this is a blocking write we will block after we have attempted to
+ // send the data to the receiver.
+ msghdr msg;
+ iovec msg_iov[2];
+ // We need a cast here because iov_base is defined non-const to support
+ // recvmsg et.al.
+ // There is no danger here:sendmsg does not write to the buffer.
+ msg_iov[0].iov_base = &message_header_;
+ msg_iov[0].iov_len = sizeof(message_header_);
+ msg_iov[1].iov_base = const_cast<void*>(buffer);
+ msg_iov[1].iov_len = transmitted_frame_size;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = msg_iov;
+ msg.msg_iovlen = arraysize(msg_iov);
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ if (dev_->SendMsg(msg, MSG_DONTWAIT) < 0) {
+ message_header_.num_packets_dropped++;
+ }
+ if (!blocking) {
+ return frames_accepted_without_blocking * frame_size_;
+ }
+ if ((message_header_.num_frames_presented) >
+ static_cast<size_t>(frames_accepted_without_blocking)) {
+ buffer_->AddToOutputBuffer(
+ message_header_.num_frames_presented -
+ frames_accepted_without_blocking, true);
+ }
+ return message_header_.num_frames_presented * frame_size_;
+}
+
+int GceAudioOutputStream::Open(
+ GceAudio* dev, audio_io_handle_t /*handle*/,
+ audio_devices_t devices, audio_output_flags_t /*flags*/,
+ audio_config* config, uint32_t stream_number,
+ GceAudioOutputStream** stream_out) {
+ D("GceAudioOutputStream::%s", __FUNCTION__);
+ *stream_out = NULL;
+ // Deleted by Close(); UniquePtr holds until end of Open().
+ std::unique_ptr<GceAudioOutputStream> out(
+ new GceAudioOutputStream(dev));
+ out->message_header_.stream_number = stream_number;
+ out->message_header_.format = config->format;
+ out->message_header_.channel_mask = config->channel_mask;
+ out->message_header_.frame_rate = config->sample_rate;
+ out->frame_count_ = config->frame_count;
+ out->common.get_sample_rate =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetSampleRate>;
+ out->common.set_sample_rate =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::SetSampleRate>;
+ out->common.get_buffer_size =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetBufferSize>;
+ out->common.get_channels =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetChannels>;
+ out->common.get_format = cvd::thunk<audio_stream, &GceAudioOutputStream::GetFormat>;
+ out->common.set_format = cvd::thunk<audio_stream, &GceAudioOutputStream::SetFormat>;
+ out->common.standby = cvd::thunk<audio_stream, &GceAudioOutputStream::Standby>;
+ out->common.dump = cvd::thunk<audio_stream, &GceAudioOutputStream::Dump>;
+ out->common.get_device = cvd::thunk<audio_stream, &GceAudioOutputStream::GetDevice>;
+ out->common.set_device = cvd::thunk<audio_stream, &GceAudioOutputStream::SetDevice>;
+ out->common.set_parameters =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::SetParameters>;
+ out->common.get_parameters =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetParameters>;
+ out->common.add_audio_effect =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::AddAudioEffect>;
+ out->common.remove_audio_effect =
+ cvd::thunk<audio_stream, &GceAudioOutputStream::RemoveAudioEffect>;
+
+ out->get_latency =
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetLatency>;
+ out->set_volume =
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::SetVolume>;
+ out->write =
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::Write>;
+ out->get_render_position =
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetRenderPosition>;
+ out->get_next_write_timestamp =
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetNextWriteTimestamp>;
+ out->device_ = devices;
+ out->frame_size_ = GceAudioFrameSize(out.get());
+
+ int64_t item_capacity =
+ out->frame_size_ == 0 ? 0 : out->GetBufferSize() / out->frame_size_;
+ if (item_capacity == 0) {
+ ALOGE("Attempt to create GceAudioOutputStream with frame_size_ of 0.");
+ return -EINVAL;
+ }
+ out->buffer_.reset(
+ new SimulatedOutputBuffer(
+ config->sample_rate, item_capacity));
+ *stream_out = out.release();
+ return 0;
+}
+
+} // namespace cvd
diff --git a/guest/hals/audio/legacy/vsoc_audio_output_stream.h b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
new file mode 100644
index 0000000..70ced6e
--- /dev/null
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+
+#include "common/libs/utils/simulated_buffer.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+
+namespace cvd {
+
+// Defines static callback functions for the audio_stream and audio_stream_out
+// interfaces in libhardware/include/hardware/audio.h
+//
+// Where the is a conflict the comments there apply.
+// By default these methods return 0 on success -<errno> for failure.
+class GceAudioOutputStream : public audio_stream_out {
+ public:
+ // Factory method for a new output stream.
+ static int Open(GceAudio* dev, audio_io_handle_t handle,
+ audio_devices_t devices, audio_output_flags_t flags,
+ audio_config* config, uint32_t stream_number,
+ GceAudioOutputStream** stream_out);
+
+ gce_audio_message GetStreamDescriptor(
+ gce_audio_message::message_t message_type) const {
+ gce_audio_message rval = message_header_;
+ rval.total_size = sizeof(rval);
+ rval.header_size = sizeof(rval);
+ rval.message_type = message_type;
+ rval.num_frames_presented = 0;
+ rval.num_frames_accepted = 0;
+ return rval;
+ }
+
+ // Method from audio_stream, listed in order of appearance.
+ // TODO(ghartman): Consider moving these if they could be shared with
+ // gce_audio_input_stream.
+
+
+ // Returns the sampling rate in Hz - eg. 44100.
+ uint32_t GetSampleRate() const {
+ return message_header_.frame_rate;
+ }
+
+ // Sets the sample rate
+ // AUDIO_PARAMETER_STREAM_SAMPLING_RATE
+ int SetSampleRate(uint32_t sample_rate) {
+ if (sample_rate != message_header_.frame_rate) {
+ message_header_.frame_rate = sample_rate;
+ // TODO(ghartman): The output buffer should be quantized at about 192
+ // bytes for better fidelity. Do this by passing
+ // frame_rate * frame_size / 192 and then rescaling the outputs.
+ // Or we could always create a quantized wrapper of the buffer...
+ buffer_.reset(
+ new SimulatedOutputBuffer(
+ sample_rate, GetBufferSize() / frame_size_));
+ }
+ return 0;
+ }
+
+ // Returns the size of input/output buffer in bytes for this stream.
+ // eg. 4800.
+ // It should be a multiple of the frame size. See also GetInputBufferSize.
+ size_t GetBufferSize() const {
+ return kOutBufferSize;
+ }
+
+ // Returns the channel mask -
+ // e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO
+ audio_channel_mask_t GetChannels() const {
+ return message_header_.channel_mask;
+ }
+
+ // Returns the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT
+ audio_format_t GetFormat() const {
+ return message_header_.format;
+ }
+
+ // Sets the audio format.
+ // Unused as of JB - use set_parameters with key
+ // AUDIO_PARAMETER_STREAM_FORMAT
+ int SetFormat(audio_format_t format) {
+ message_header_.format = format;
+ return 0;
+ }
+
+ // Puts the audio hardware input/output into standby mode.
+ // Driver should exit from standby mode at the next I/O operation.
+ // Returns 0 on success and <0 on failure.
+ // TODO(ghartman): This should reset some of the frame counts.
+ int Standby() {
+ return 0;
+ }
+
+ // dumps the state of the audio hardware to the given fd.
+ // This information can be retrieved using the dumpsys utility.
+ int Dump(int fd) const;
+
+ // Returns the set of device(s) which this stream is connected to.
+ // TODO(ghartman): Implement this.
+ audio_devices_t GetDevice() const { return device_; }
+
+ // Not directly called from JB forward.
+ // Called indirectly from SetParameters with the key
+ // AUDIO_PARAMETER_STREAM_ROUTING
+ int SetDevice(audio_devices_t device) { device_ = device; return 0; }
+
+ // Sets audio stream parameters. The function accepts a list of
+ // parameter key value pairs in the form: key1=value1;key2=value2;...
+ //
+ // Some keys are reserved for standard parameters (See AudioParameter class)
+ //
+ // If the implementation does not accept a parameter change while
+ // the output is active but the parameter is acceptable otherwise, it must
+ // return -ENOSYS.
+ //
+ // The audio flinger will put the stream in standby and then change the
+ // parameter value.
+ int SetParameters(const char* kv_pairs);
+
+ // Gets audio stream parameters. The function accepts a list of
+ // keys in the form: key1=value1;key2=value2;...
+ //
+ // Returns a pointer to a heap allocated string. The caller is responsible
+ // for freeing the memory for it using free().
+ // TODO(ghartman): Implement this.
+ char* GetParameters(const char* keys) const;
+
+ // TODO(ghartman): Implement this.
+ int AddAudioEffect(effect_handle_t /*effect*/) const {
+ static unsigned int printed = 0; // printed every 2^32-th call.
+ ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+ return 0;
+ }
+
+ // TODO(ghartman): Implement this.
+ int RemoveAudioEffect(effect_handle_t /*effect*/) const {
+ static unsigned int printed = 0; // printed every 2^32-th call.
+ ALOGE_IF(!printed++, "%s: not implemented", __FUNCTION__);
+ return 0;
+ }
+
+ // Methods defined in audio_stream_out
+
+ // Returns the audio hardware driver estimated latency in milliseconds.
+ // TODO(ghartman): Calculate this based on the format and the quantum.
+ uint32_t GetLatency() const {
+ return kOutLatency;
+ }
+
+ // Use this method in situations where audio mixing is done in the
+ // hardware. This method serves as a direct interface with hardware,
+ // allowing you to directly set the volume as apposed to via the framework.
+ // This method might produce multiple PCM outputs or hardware accelerated
+ // codecs, such as MP3 or AAC.
+ //
+ // Note that GCE simulates hardware mixing.
+ int SetVolume(float left_volume, float right_volume) {
+ left_volume_ = left_volume;
+ right_volume_ = right_volume;
+ return 0;
+ }
+
+ // Write audio buffer to driver. Returns number of bytes written, or a
+ // negative android::status_t. If at least one frame was written successfully prior
+ // to the error the driver will return that successful (short) byte count
+ // and then return an error in the subsequent call.
+ //
+ // If SetCallback() has previously been called to enable non-blocking mode
+ // the Write() is not allowed to block. It must write only the number of
+ // bytes that currently fit in the driver/hardware buffer and then return
+ // this byte count. If this is less than the requested write size the
+ // callback function must be called when more space is available in the
+ // driver/hardware buffer.
+ ssize_t Write(const void* buffer, size_t bytes);
+
+ // Returns the number of audio frames written by the audio dsp to DAC since
+ // the output has exited standby
+ // TODO(ghartman): Implement zeroing this in Standby().
+ int GetRenderPosition(uint32_t* dsp_frames) const;
+
+ // Gets the local time at which the next write to the audio driver will be
+ // presented. The units are microseconds, where the epoch is decided by the
+ // local audio HAL.
+ //
+ // The GCE implementation uses CLOCK_MONOTONIC, which also happens to line
+ // up with LocalTime.
+ int GetNextWriteTimestamp(int64_t*) const;
+
+ // Turns on non-blocking mode and sets the callback function for notifying
+ // completion of non-blocking write and drain.
+ // Calling this function implies that all future Write() and Drain()
+ // must be non-blocking and use the callback to signal completion.
+ //
+ // TODO(ghartman): Implement this URGENTLY.
+ //
+ // int SetCallback(stream_callback_t callback, void *cookie);
+
+ // Notifies to the audio driver to stop playback however the queued buffers
+ // are retained by the hardware. Useful for implementing pause/resume. Empty
+ // implementation if not supported however should be implemented for hardware
+ // with non-trivial latency. In the pause state audio hardware could still be
+ // using power. User may consider calling suspend after a timeout.
+ //
+ // Implementation of this function is mandatory for offloaded playback.
+ //
+ // TODO(ghartman): Implement this URGENTLY. There is already support in
+ // SimulatedBuffer.
+ // int Pause();
+
+ // Notifies to the audio driver to resume playback following a pause.
+ // Returns error if called without matching pause.
+ //
+ // Implementation of this function is mandatory for offloaded playback.
+ //
+ // TODO(ghartman): Implement this URGENTLY.
+ //
+ // int Resume();
+
+ // Requests notification when data buffered by the driver/hardware has
+ // been played. If set_callback() has previously been called to enable
+ // non-blocking mode, the drain() must not block, instead it should return
+ // quickly and completion of the drain is notified through the callback.
+ // If set_callback() has not been called, the drain() must block until
+ // completion.
+ //
+ // If type==AUDIO_DRAIN_ALL, the drain completes when all previously written
+ // data has been played.
+ //
+ // If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all
+ // data for the current track has played to allow time for the framework
+ // to perform a gapless track switch.
+ //
+ // Drain must return immediately on stop() and flush() call
+ //
+ // Implementation of this function is mandatory for offloaded playback.
+ //
+ // TODO(ghartman): Implement this URGENTLY.
+ //
+ // int Drain(audio_drain_type_t type);
+
+ // Notifies to the audio driver to flush the queued data. Stream must already
+ // be paused before calling Flush().
+ //
+ // Implementation of this function is mandatory for offloaded playback.
+ //
+ // TODO(ghartman): Implement this URGENTLY.
+ //
+ // int Flush();
+
+ // Returns a recent count of the number of audio frames presented to an
+ // external observer. This excludes frames which have been written but are
+ // still in the pipeline.
+ //
+ // The count is not reset to zero when output enters standby.
+ // Also returns the value of CLOCK_MONOTONIC as of this presentation count.
+ // The returned count is expected to be 'recent',
+ // but does not need to be the most recent possible value.
+ // However, the associated time should correspond to whatever count is
+ // returned.
+ //
+ // Example: assume that N+M frames have been presented, where M is a
+ // 'small' number.
+ // Then it is permissible to return N instead of N+M,
+ // and the timestamp should correspond to N rather than N+M.
+ // The terms 'recent' and 'small' are not defined.
+ // They reflect the quality of the implementation.
+ //
+ // 3.0 and higher only.
+ //
+ // TODO(ghartman): Implement this URGENTLY.
+ //
+ // int GetPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+ private:
+ // If key is present in query, add key=value; to reply.
+ // query should be pointer to const, but the str_parms functions aren't
+ // const-correct, so neither is this.
+ static void AddIntIfKeyPresent(
+ /*const*/ str_parms* query, str_parms* reply, const char* key, int value);
+
+
+ explicit GceAudioOutputStream(cvd::GceAudio*);
+
+ static const size_t kOutBufferSize = 3840;
+ static const size_t kOutLatency = 2;
+
+ gce_audio_message message_header_;
+ std::unique_ptr<SimulatedOutputBuffer> buffer_;
+ cvd::GceAudio *dev_;
+ audio_devices_t device_;
+ size_t frame_size_;
+ size_t frame_count_;
+ float left_volume_;
+ float right_volume_;
+};
+
+}
diff --git a/guest/hals/gralloc/Android.mk b/guest/hals/gralloc/Android.mk
new file mode 100644
index 0000000..68ffffa
--- /dev/null
+++ b/guest/hals/gralloc/Android.mk
@@ -0,0 +1,63 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gralloc.cutf_ivsh-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ gralloc.cpp \
+ mapper.cpp
+
+LOCAL_C_INCLUDES += \
+ device/google/cuttlefish_common \
+ device/google/cuttlefish_kernel \
+ hardware/libhardware/include \
+ system/core/base/include
+
+LOCAL_CFLAGS := \
+ -DLOG_TAG=\"gralloc_vsoc\" \
+ -Wno-missing-field-initializers \
+ -Wall -Werror \
+ $(VSOC_VERSION_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libcutils \
+ cuttlefish_auto_resources \
+ libcuttlefish_fs \
+ liblog \
+ vsoc_lib
+
+LOCAL_HEADER_LIBRARIES := \
+ libhardware_headers
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/gralloc/gralloc.cpp b/guest/hals/gralloc/gralloc.cpp
new file mode 100644
index 0000000..d21d918
--- /dev/null
+++ b/guest/hals/gralloc/gralloc.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2017 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 <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+#include <stdlib.h>
+
+#include "guest/hals/gralloc/gralloc_vsoc_priv.h"
+#include "guest/vsoc/lib/gralloc_region_view.h"
+
+using vsoc::gralloc::GrallocRegionView;
+
+namespace {
+
+static const int kSwiftShaderPadding = 4;
+
+inline void formatToYcbcr(
+ int format, int width, int height, void* base_v, android_ycbcr* ycbcr) {
+ uintptr_t it = reinterpret_cast<uintptr_t>(base_v);
+ // Clear reserved fields;
+ memset(ycbcr, 0, sizeof(*ycbcr));
+ switch (format) {
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ ycbcr->ystride = align(width, 16);
+ ycbcr->cstride = align(ycbcr->ystride / 2, 16);
+ ycbcr->chroma_step = 1;
+ ycbcr->y = reinterpret_cast<void*>(it);
+ it += ycbcr->ystride * height;
+ ycbcr->cr = reinterpret_cast<void*>(it);
+ it += ycbcr->cstride * height / 2;
+ ycbcr->cb = reinterpret_cast<void*>(it);
+ break;
+ default:
+ ALOGE("%s: can't deal with format=0x%x", __FUNCTION__, format);
+ }
+}
+
+inline int formatToBytesPerPixel(int format) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ return 8;
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ // The camera 3.0 implementation assumes that IMPLEMENTATION_DEFINED
+ // means HAL_PIXEL_FORMAT_RGBA_8888
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ return 4;
+ case HAL_PIXEL_FORMAT_RGB_888:
+ return 3;
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ return 2;
+ case HAL_PIXEL_FORMAT_BLOB:
+ return 1;
+ default:
+ ALOGE("%s: unknown format=%d", __FUNCTION__, format);
+ return 8;
+ }
+}
+
+inline int formatToBytesPerFrame(int format, int w, int h) {
+ int bytes_per_pixel = formatToBytesPerPixel(format);
+ int w16, h16;
+ int y_size, c_size;
+
+ switch (format) {
+ // BLOB is used to allocate buffers for JPEG formatted data. Bytes per pixel
+ // is 1, the desired buffer size is in w, and h should be 1. We refrain from
+ // adding additional padding, although the caller is likely to round
+ // up to a page size.
+ case HAL_PIXEL_FORMAT_BLOB:
+ return bytes_per_pixel * w * h;
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ android_ycbcr strides;
+ formatToYcbcr(format, w, h, NULL, &strides);
+ y_size = strides.ystride * h;
+ c_size = strides.cstride * h / 2;
+ return (y_size + 2 * c_size + kSwiftShaderPadding);
+ /*case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:*/
+ default:
+ w16 = align(w, 16);
+ h16 = align(h, 16);
+ return bytes_per_pixel * w16 * h16 + kSwiftShaderPadding;
+ }
+}
+
+}
+
+/******************************************************************************/
+
+void dump(struct alloc_device_t */*dev*/, char */*buff*/, int /*buff_len*/) {}
+
+/******************************************************************************/
+
+int lock(struct gralloc_module_t const* /*module*/,
+ buffer_handle_t handle,
+ int /*usage*/,
+ int /*l*/,
+ int /*t*/,
+ int /*w*/,
+ int /*h*/,
+ void** vaddr) {
+ if (!vaddr || vsoc_buffer_handle_t::validate(handle)) {
+ return -EINVAL;
+ }
+ // TODO(jemoreira): Check allocation usage flags against requested usage.
+ const vsoc_buffer_handle_t* hnd =
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+ void* mapped = reference_buffer(hnd);
+ if (mapped == NULL) {
+ ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+ return -1;
+ }
+ *vaddr = mapped;
+ return 0;
+}
+
+int unlock(struct gralloc_module_t const* /*module*/, buffer_handle_t handle) {
+ if (vsoc_buffer_handle_t::validate(handle)) {
+ return -EINVAL;
+ }
+ return unreference_buffer(
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+int lock_ycbcr(struct gralloc_module_t const* module,
+ buffer_handle_t handle,
+ int usage,
+ int l,
+ int t,
+ int w,
+ int h,
+ struct android_ycbcr* ycbcr) {
+ void* mapped;
+ int retval = lock(module, handle, usage, l, t, w, h, &mapped);
+ if (retval) {
+ return retval;
+ }
+ const vsoc_buffer_handle_t* hnd =
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+ formatToYcbcr(hnd->format, w, h, mapped, ycbcr);
+ return 0;
+}
+
+/******************************************************************************/
+
+static int gralloc_alloc(alloc_device_t* /*dev*/,
+ int w,
+ int h,
+ int format,
+ int /*usage*/,
+ buffer_handle_t* pHandle,
+ int* pStrideInPixels) {
+ int fd = -1;
+
+ int bytes_per_pixel = formatToBytesPerPixel(format);
+ int bytes_per_line;
+ int stride_in_pixels;
+ int size = 0;
+ uint32_t offset = 0;
+ // SwiftShader can't handle RGB_888, so fail fast and hard if we try to create
+ // a gralloc buffer in this format.
+ ALOG_ASSERT(format != HAL_PIXEL_FORMAT_RGB_888);
+ if (format == HAL_PIXEL_FORMAT_YV12) {
+ bytes_per_line = align(bytes_per_pixel * w, 16);
+ } else {
+ bytes_per_line = align(bytes_per_pixel * w, 8);
+ }
+ size = align(size + formatToBytesPerFrame(format, w, h), PAGE_SIZE);
+ size += PAGE_SIZE;
+ fd = GrallocRegionView::GetInstance()->AllocateBuffer(size, &offset);
+ if (fd < 0) {
+ ALOGE("Unable to allocate buffer (%s)", strerror(-fd));
+ return fd;
+ }
+
+ stride_in_pixels = bytes_per_line / bytes_per_pixel;
+ vsoc_buffer_handle_t* hnd = new vsoc_buffer_handle_t(fd,
+ offset,
+ size,
+ format,
+ w, h,
+ stride_in_pixels);
+ void* addr =
+ reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(hnd));
+ if (!addr) {
+ ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+ return -EIO;
+ }
+
+ *pHandle = hnd;
+ *pStrideInPixels = stride_in_pixels;
+
+ return 0;
+}
+
+static int gralloc_free(alloc_device_t* /*dev*/, buffer_handle_t handle) {
+ // No need to do anything else, the buffer will be atomatically deallocated
+ // when the handle is closed.
+ return unreference_buffer(
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+static int register_buffer(struct gralloc_module_t const* /*module*/,
+ buffer_handle_t handle) {
+ if (vsoc_buffer_handle_t::validate(handle)) {
+ return -EINVAL;
+ }
+ void* addr =
+ reference_buffer(reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+ if (!addr) {
+ ALOGE("Unable to reference buffer, %s", __FUNCTION__);
+ return -EIO;
+ }
+ return 0;
+}
+
+int unregister_buffer(struct gralloc_module_t const* /*module*/,
+ buffer_handle_t handle) {
+ if (vsoc_buffer_handle_t::validate(handle)) {
+ return -EINVAL;
+ }
+ return unreference_buffer(
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle));
+}
+
+/******************************************************************************/
+
+static int gralloc_device_close(struct hw_device_t *dev) {
+ vsoc_alloc_device_t* pdev = reinterpret_cast<vsoc_alloc_device_t*>(dev);
+ if (pdev) {
+ free(pdev);
+ }
+ return 0;
+}
+
+static int gralloc_device_open(
+ const hw_module_t* module, const char* name, hw_device_t** device) {
+ int status = -EINVAL;
+ if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {
+ vsoc_alloc_device_t *dev;
+ dev = (vsoc_alloc_device_t*) malloc(sizeof(*dev));
+ LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+
+ /* initialize our state here */
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->device.common.tag = HARDWARE_DEVICE_TAG;
+ dev->device.common.version = 0; // TODO(jemoreira): Bump to 0_2 when stable
+ dev->device.common.module = const_cast<hw_module_t*>(module);
+ dev->device.common.close = gralloc_device_close;
+
+ dev->device.alloc = gralloc_alloc;
+ dev->device.free = gralloc_free;
+
+ if (!GrallocRegionView::GetInstance()) {
+ LOG_FATAL("Unable to instantiate the gralloc region");
+ free(dev);
+ return -EIO;
+ }
+
+ *device = &dev->device.common;
+ status = 0;
+ }
+ // TODO(jemoreira): Consider opening other type of devices (framebuffer)
+ return status;
+}
+
+/******************************************************************************/
+
+static struct hw_module_methods_t gralloc_module_methods = {
+ .open = gralloc_device_open
+};
+
+struct vsoc_gralloc_module_t HAL_MODULE_INFO_SYM = {
+ .base = {
+ .common = {
+ .tag = HARDWARE_MODULE_TAG,
+ .version_major = GRALLOC_MODULE_API_VERSION_0_2,
+ .version_minor = 0,
+ .id = GRALLOC_HARDWARE_MODULE_ID,
+ .name = "VSoC X86 Graphics Memory Allocator Module",
+ .author = "The Android Open Source Project",
+ .methods = &gralloc_module_methods,
+ .dso = NULL,
+ .reserved = {0},
+ },
+ .registerBuffer = register_buffer,
+ .unregisterBuffer = unregister_buffer,
+ .lock = lock,
+ .unlock = unlock,
+ .perform = NULL,
+ .lock_ycbcr = lock_ycbcr,
+ .getTransportSize = NULL,
+ .validateBufferSize = NULL,
+ },
+};
diff --git a/guest/hals/gralloc/gralloc_vsoc_priv.h b/guest/hals/gralloc/gralloc_vsoc_priv.h
new file mode 100644
index 0000000..1ec5659
--- /dev/null
+++ b/guest/hals/gralloc/gralloc_vsoc_priv.h
@@ -0,0 +1,100 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <cutils/native_handle.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+struct vsoc_alloc_device_t {
+ alloc_device_t device;
+};
+
+struct vsoc_gralloc_module_t {
+ gralloc_module_t base;
+};
+
+static_assert(sizeof(int) >= 4, "At least 4 bytes are needed for offsets");
+
+struct vsoc_buffer_handle_t : public native_handle {
+ // File descriptors
+ int fd;
+ // ints
+ int magic;
+ int format;
+ int x_res;
+ int y_res;
+ int stride_in_pixels;
+ int size;
+ // buffer offset in bytes divided by PAGE_SIZE
+ int offset;
+
+ static inline int sNumInts() {
+ return ((sizeof(vsoc_buffer_handle_t) - sizeof(native_handle_t)) /
+ sizeof(int) -
+ sNumFds);
+ }
+ static const int sNumFds = 1;
+ static const int sMagic = 0xc63752f4;
+
+ vsoc_buffer_handle_t(int fd,
+ int offset,
+ int size,
+ int format,
+ int x_res,
+ int y_res,
+ int stride_in_pixels)
+ : fd(fd),
+ magic(sMagic),
+ format(format),
+ x_res(x_res),
+ y_res(y_res),
+ stride_in_pixels(stride_in_pixels),
+ size(size),
+ offset(offset) {
+ version = sizeof(native_handle);
+ numInts = sNumInts();
+ numFds = sNumFds;
+ }
+
+ ~vsoc_buffer_handle_t() {
+ magic = 0;
+ }
+
+ static int validate(const native_handle* handle) {
+ const vsoc_buffer_handle_t* hnd =
+ reinterpret_cast<const vsoc_buffer_handle_t*>(handle);
+ if (!hnd || hnd->version != sizeof(native_handle) ||
+ hnd->numInts != sNumInts() || hnd->numFds != sNumFds ||
+ hnd->magic != sMagic) {
+ ALOGE("Invalid gralloc handle (at %p)", handle);
+ return -EINVAL;
+ }
+ return 0;
+ }
+};
+
+// These functions are to be used to map/unmap gralloc buffers. They are thread
+// safe and ensure that the same buffer never gets mapped twice.
+void* reference_buffer(const vsoc_buffer_handle_t* hnd);
+int unreference_buffer(const vsoc_buffer_handle_t* hnd);
+
+// TODO(jemoreira): Move this to a place where it can be used by the gralloc
+// region as well.
+inline int align(int input, int alignment) {
+ return (input + alignment - 1) & -alignment;
+}
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
index ac789fb..4bbc9c0 100644
--- a/guest/hals/gralloc/legacy/Android.mk
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -18,6 +18,7 @@
VSOC_GRALLOC_COMMON_SRC_FILES := \
gralloc.cpp \
+ framebuffer.cpp \
mapper.cpp \
region_registry.cpp
@@ -46,7 +47,8 @@
libbase \
liblog \
libutils \
- libcutils
+ libcutils \
+ vsoc_lib
LOCAL_VENDOR_MODULE := true
diff --git a/guest/hals/gralloc/legacy/framebuffer.cpp b/guest/hals/gralloc/legacy/framebuffer.cpp
new file mode 100644
index 0000000..5a2b7f0
--- /dev/null
+++ b/guest/hals/gralloc/legacy/framebuffer.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <sys/mman.h>
+
+#include <dlfcn.h>
+
+#include <cutils/ashmem.h>
+#include <log/log.h>
+#include <cutils/properties.h>
+
+#include <sys/system_properties.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <cutils/atomic.h>
+
+#if defined(__ANDROID__)
+#include <linux/fb.h>
+#endif
+
+#include "gralloc_vsoc_priv.h"
+#include "region_registry.h"
+
+#include "common/libs/auto_resources/auto_resources.h"
+#include "common/libs/threads/cuttlefish_thread.h"
+#include "common/vsoc/lib/screen_region_view.h"
+
+using vsoc::screen::ScreenRegionView;
+
+/*****************************************************************************/
+
+struct fb_context_t {
+ framebuffer_device_t device;
+};
+
+/*****************************************************************************/
+
+static int fb_setSwapInterval(struct framebuffer_device_t* dev, int interval) {
+ if (interval < dev->minSwapInterval || interval > dev->maxSwapInterval) {
+ return -EINVAL;
+ }
+ // FIXME: implement fb_setSwapInterval
+ return 0;
+}
+
+/*
+ * These functions (and probably the entire framebuffer device) are most likely
+ * not used when the hardware composer device is present, however is hard to be
+ * 100% sure.
+ */
+static int fb_setUpdateRect(
+ struct framebuffer_device_t* dev __unused, int l, int t, int w, int h) {
+ if (((w|h) <= 0) || ((l|t)<0)) {
+ return -EINVAL;
+ }
+ // TODO(jemoreira): Find a way to broadcast this with the framebuffer control.
+ return 0;
+}
+
+static int fb_post(struct framebuffer_device_t* dev __unused,
+ buffer_handle_t buffer_handle) {
+ static int frame_buffer_idx = 0;
+
+ auto screen_view = ScreenRegionView::GetInstance();
+
+ void* frame_buffer = screen_view->GetBuffer(frame_buffer_idx);
+ const private_handle_t* p_handle =
+ reinterpret_cast<const private_handle_t*>(buffer_handle);
+ void* buffer;
+ int retval =
+ reinterpret_cast<gralloc_module_t*>(dev->common.module)
+ ->lock(reinterpret_cast<const gralloc_module_t*>(dev->common.module),
+ buffer_handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
+ p_handle->x_res, p_handle->y_res, &buffer);
+ if (retval != 0) {
+ ALOGE("Got error code %d from lock function", retval);
+ return -1;
+ }
+ memcpy(frame_buffer, buffer, screen_view->buffer_size());
+ screen_view->BroadcastNewFrame(frame_buffer_idx);
+
+ frame_buffer_idx = (frame_buffer_idx + 1) % screen_view->number_of_buffers();
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+static int fb_close(struct hw_device_t *dev) {
+ fb_context_t* ctx = (fb_context_t*)dev;
+ if (ctx) {
+ free(ctx);
+ }
+ return 0;
+}
+
+int fb_device_open(
+ hw_module_t const* module, const char* name, hw_device_t** device) {
+ if (strcmp(name, GRALLOC_HARDWARE_FB0) != 0) {
+ return -EINVAL;
+ }
+ /* initialize our state here */
+ fb_context_t* dev = (fb_context_t*) malloc(sizeof(*dev));
+ LOG_FATAL_IF(!dev, "%s: malloc returned NULL.", __FUNCTION__);
+ memset(dev, 0, sizeof(*dev));
+
+ /* initialize the procs */
+ dev->device.common.tag = HARDWARE_DEVICE_TAG;
+ dev->device.common.version = 0;
+ dev->device.common.module = const_cast<hw_module_t*>(module);
+ dev->device.common.close = fb_close;
+ dev->device.setSwapInterval = fb_setSwapInterval;
+ dev->device.post = fb_post;
+ dev->device.setUpdateRect = fb_setUpdateRect;
+
+ auto screen_view = ScreenRegionView::GetInstance();
+
+ int stride =
+ screen_view->line_length() / screen_view->bytes_per_pixel();
+ int format = HAL_PIXEL_FORMAT_RGBX_8888;
+ const_cast<uint32_t&>(dev->device.flags) = 0;
+ const_cast<uint32_t&>(dev->device.width) = screen_view->x_res();
+ const_cast<uint32_t&>(dev->device.height) = screen_view->y_res();
+ const_cast<int&>(dev->device.stride) = stride;
+ const_cast<int&>(dev->device.format) = format;
+ const_cast<float&>(dev->device.xdpi) = screen_view->dpi();
+ const_cast<float&>(dev->device.ydpi) = screen_view->dpi();
+ const_cast<float&>(dev->device.fps) =
+ (screen_view->refresh_rate_hz() * 1000) / 1000.0f;
+ const_cast<int&>(dev->device.minSwapInterval) = 1;
+ const_cast<int&>(dev->device.maxSwapInterval) = 1;
+ *device = &dev->device.common;
+
+ return 0;
+}
diff --git a/guest/hals/gralloc/legacy/gralloc.cpp b/guest/hals/gralloc/legacy/gralloc.cpp
index dbe2838..8a0cbd2 100644
--- a/guest/hals/gralloc/legacy/gralloc.cpp
+++ b/guest/hals/gralloc/legacy/gralloc.cpp
@@ -36,6 +36,7 @@
#include <hardware/gralloc.h>
#include "common/libs/auto_resources/auto_resources.h"
+#include "common/vsoc/lib/screen_region_view.h"
#include "gralloc_vsoc_priv.h"
#include "region_registry.h"
@@ -161,7 +162,7 @@
*device = &dev->device.common;
status = 0;
} else {
- ALOGE("Need to create framebuffer, but it is unsupported");
+ status = fb_device_open(module, name, device);
}
return status;
}
diff --git a/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
index 390c654..2544b5a 100644
--- a/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
+++ b/guest/hals/gralloc/legacy/gralloc_vsoc_priv.h
@@ -29,6 +29,8 @@
#include <linux/fb.h>
+#include "common/vsoc/lib/screen_region_view.h"
+
#ifndef GRALLOC_MODULE_API_VERSION_0_2
// This structure will be defined in later releases of Android. Declare it
// here to allow us to structure the code well.
@@ -43,20 +45,6 @@
};
#endif
-namespace vsoc {
-namespace screen {
-
-struct ScreenRegionView {
- static int align(int input) {
- auto constexpr alignment = 16;
- return (input + alignment - 1) & -alignment;
- }
- static constexpr int kSwiftShaderPadding = 4;
-};
-
-}
-}
-
/*****************************************************************************/
struct private_handle_t;
@@ -309,6 +297,9 @@
}
}
+int fb_device_open(
+ const hw_module_t* module, const char* name, hw_device_t** device);
+
int gralloc_lock(
gralloc_module_t const* module,
buffer_handle_t handle, int usage,
diff --git a/guest/hals/gralloc/mapper.cpp b/guest/hals/gralloc/mapper.cpp
new file mode 100644
index 0000000..e9ba2eb
--- /dev/null
+++ b/guest/hals/gralloc/mapper.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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 "gralloc_vsoc_priv.h"
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+
+#include <cutils/hashmap.h>
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+#include <log/log.h>
+
+namespace {
+
+const size_t g_page_size = sysconf(_SC_PAGESIZE);
+
+struct HmLockGuard {
+ HmLockGuard(Hashmap* map) : map_(map) {
+ hashmapLock(map_);
+ }
+ ~HmLockGuard() {
+ hashmapUnlock(map_);
+ }
+ private:
+ Hashmap* map_;
+};
+
+int offset_hash(void* key) {
+ return *reinterpret_cast<int*>(key);
+}
+
+bool offset_equals(void* key1, void* key2) {
+ return *reinterpret_cast<int*>(key1) ==
+ *reinterpret_cast<int*>(key2);
+}
+
+// Keeps track of how many times a buffer is locked in the current process.
+struct GrallocBuffer {
+ void* vaddr;
+ int ref_count;
+ GrallocBuffer() : vaddr(NULL), ref_count(0) {}
+
+ static Hashmap* mapped_buffers() {
+ static Hashmap* mapped_buffers =
+ hashmapCreate(19, offset_hash, offset_equals);
+ return mapped_buffers;
+ }
+};
+
+}
+
+void* reference_buffer(const vsoc_buffer_handle_t* hnd) {
+ Hashmap* map = GrallocBuffer::mapped_buffers();
+ HmLockGuard lock_guard(map);
+ GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+ hashmapGet(map, const_cast<int*>(&hnd->offset)));
+ if (!buffer) {
+ buffer = new GrallocBuffer();
+ hashmapPut(map, const_cast<int*>(&hnd->offset), buffer);
+ }
+
+ if (!buffer->vaddr) {
+ void* mapped =
+ mmap(NULL, hnd->size, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->fd, 0);
+ if (mapped == MAP_FAILED) {
+ ALOGE("Unable to map buffer (offset: %d, size: %d): %s",
+ hnd->offset,
+ hnd->size,
+ strerror(errno));
+ return NULL;
+ }
+ // Set up the guard pages. The last page is always a guard
+ uintptr_t base = uintptr_t(mapped);
+ uintptr_t addr = base + hnd->size - g_page_size;
+ if (mprotect((void*)addr, g_page_size, PROT_NONE) == -1) {
+ ALOGW("Unable to protect last page of buffer (offset: %d, size: %d): %s",
+ hnd->offset,
+ hnd->size,
+ strerror(errno));
+ }
+ buffer->vaddr = mapped;
+ }
+ buffer->ref_count++;
+ return buffer->vaddr;
+}
+
+int unreference_buffer(const vsoc_buffer_handle_t* hnd) {
+ int result = 0;
+ Hashmap* map = GrallocBuffer::mapped_buffers();
+ HmLockGuard lock_guard(map);
+ GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
+ hashmapGet(map, const_cast<int*>(&hnd->offset)));
+ if (!buffer) {
+ ALOGE("Unreferencing an unknown buffer (offset: %d, size: %d)",
+ hnd->offset,
+ hnd->size);
+ return -EINVAL;
+ }
+ if (buffer->ref_count == 0) {
+ ALOGE("Unbalanced reference/unreference on buffer (offset: %d, size: %d)",
+ hnd->offset,
+ hnd->size);
+ return -EINVAL;
+ }
+ buffer->ref_count--;
+ if (buffer->ref_count == 0) {
+ result = munmap(buffer->vaddr, hnd->size);
+ if (result) {
+ ALOGE("Unable to unmap buffer (offset: %d, size: %d): %s",
+ hnd->offset,
+ hnd->size,
+ strerror(errno));
+ }
+ buffer->vaddr = NULL;
+ }
+ return result;
+}
diff --git a/guest/hals/hwcomposer/vsoc-future/Android.mk b/guest/hals/hwcomposer/vsoc-future/Android.mk
new file mode 100644
index 0000000..bcc104e
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc-future/Android.mk
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := hwcomposer.cutf_ivsh-future
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ hwcomposer.cpp
+
+LOCAL_C_INCLUDES += \
+ device/google/cuttlefish_kernel \
+ device/google/cuttlefish_common \
+ device/google/cuttlefish_common/guest/hals/hwcomposer \
+ hardware/libhardware/include \
+ system/core/base/include
+
+LOCAL_CFLAGS := \
+ -DLOG_TAG=\"hwcomposer_vsoc\" \
+ -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ liblog \
+ vsoc_lib
+
+ifeq (0, $(shell test $(PLATFORM_SDK_VERSION) -ge 21; echo $$?))
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
+LOCAL_VENDOR_MODULE := true
+# See b/67109557
+ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
+LOCAL_MULTILIB := first
+endif
+
+LOCAL_HEADER_LIBRARIES := \
+ libhardware_headers
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
new file mode 100644
index 0000000..f18712c
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2017 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 <pthread.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+#include "guest/hals/gralloc/gralloc_vsoc_priv.h"
+
+// This file contains just a skeleton hwcomposer, the first step in the
+// multisided vsoc hwcomposer for cuttlefish.
+
+using vsoc::screen::ScreenRegionView;
+
+// TODO(jemoreira): ScreenRegionView may become the HWC region
+namespace {
+
+// Ensures that the layer does not include any inconsistencies
+int SanityCheckLayer(const hwc_layer_1& layer) {
+ // Check displayFrame
+ if (layer.displayFrame.left > layer.displayFrame.right ||
+ layer.displayFrame.top > layer.displayFrame.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__, layer.displayFrame.left, layer.displayFrame.right,
+ layer.displayFrame.top, layer.displayFrame.bottom);
+ return -EINVAL;
+ }
+ // Check sourceCrop
+ if (layer.sourceCrop.left > layer.sourceCrop.right ||
+ layer.sourceCrop.top > layer.sourceCrop.bottom) {
+ ALOGE(
+ "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
+ "%d, bottom = %d]",
+ __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+ layer.sourceCrop.top, layer.sourceCrop.bottom);
+ return -EINVAL;
+ }
+ const vsoc_buffer_handle_t* p_handle =
+ reinterpret_cast<const vsoc_buffer_handle_t*>(layer.handle);
+ if (!p_handle) {
+ ALOGE("Layer has a NULL buffer handle");
+ return -EINVAL;
+ }
+ if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 ||
+ layer.sourceCrop.right > p_handle->x_res ||
+ layer.sourceCrop.bottom > p_handle->y_res) {
+ ALOGE(
+ "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, "
+ "right = %d, top = %d, bottom = %d], handle = [width = %d, height = "
+ "%d]",
+ __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
+ layer.sourceCrop.top, layer.sourceCrop.bottom, p_handle->x_res,
+ p_handle->y_res);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+struct vsoc_hwc_device {
+ hwc_composer_device_1_t base;
+ const hwc_procs_t* procs;
+ pthread_t vsync_thread;
+ int64_t vsync_base_timestamp;
+ int32_t vsync_period_ns;
+ uint32_t frame_num;
+};
+
+void* vsync_thread(void* arg) {
+ vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(arg);
+ setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
+
+ int64_t base_timestamp = pdev->vsync_base_timestamp;
+ int64_t last_logged = base_timestamp / 1e9;
+ int sent = 0;
+ int last_sent = 0;
+ static const int log_interval = 60;
+ void (*vsync_proc)(const struct hwc_procs*, int, int64_t) = nullptr;
+ bool log_no_procs = true, log_no_vsync = true;
+ while (true) {
+ struct timespec rt;
+ if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+ ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
+ strerror(errno));
+ }
+ int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+ // Given now's timestamp calculate the time of the next vsync.
+ timestamp += pdev->vsync_period_ns -
+ (timestamp - base_timestamp) % pdev->vsync_period_ns;
+
+ rt.tv_sec = timestamp / 1e9;
+ rt.tv_nsec = timestamp % static_cast<int32_t>(1e9);
+ int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL);
+ if (err == -1) {
+ ALOGE("error in vsync thread: %s", strerror(errno));
+ if (errno == EINTR) {
+ continue;
+ }
+ }
+
+ // The vsync thread is started on device open, it may run before the
+ // registerProcs callback has a chance to be called, so we need to make sure
+ // procs is not NULL before dereferencing it.
+ if (pdev && pdev->procs) {
+ vsync_proc = pdev->procs->vsync;
+ } else if (log_no_procs) {
+ log_no_procs = false;
+ ALOGI("procs is not set yet, unable to deliver vsync event");
+ }
+ if (vsync_proc) {
+ vsync_proc(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
+ ++sent;
+ } else if (log_no_vsync) {
+ log_no_vsync = false;
+ ALOGE("vsync callback is null (but procs was already set)");
+ }
+ if (rt.tv_sec - last_logged > log_interval) {
+ ALOGI("Sent %d syncs in %ds", sent - last_sent, log_interval);
+ last_logged = rt.tv_sec;
+ last_sent = sent;
+ }
+ }
+
+ return NULL;
+}
+
+int hwc_prepare(struct hwc_composer_device_1* /*dev*/, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
+ hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+ if (!list) return 0;
+
+ for (size_t i = 0; i < list->numHwLayers; i++) {
+ if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+ continue;
+ }
+ list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
+ }
+
+ return 0;
+}
+
+int hwc_set(struct hwc_composer_device_1* dev, size_t numDisplays,
+ hwc_display_contents_1_t** displays) {
+ if (!numDisplays || !displays) return 0;
+ hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY];
+ if (!list) return 0;
+ if (!dev) {
+ ALOGE("%s: dev is NULL", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ for (size_t i = 0; i < list->numHwLayers; i++) {
+ if (vsoc_buffer_handle_t::validate(list->hwLayers[i].handle)) {
+ return -EINVAL;
+ }
+ if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
+ if (SanityCheckLayer(list->hwLayers[i])) {
+ ALOGW("Skipping layer %zu due to failed sanity check", i);
+ continue;
+ }
+ const vsoc_buffer_handle_t* fb_handle =
+ reinterpret_cast<const vsoc_buffer_handle_t*>(
+ list->hwLayers[i].handle);
+ ScreenRegionView::GetInstance()->BroadcastNewFrame(fb_handle->offset);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int hwc_eventControl(struct hwc_composer_device_1* /*dev*/, int disp, int event,
+ int /*enabled*/) {
+ if (event == HWC_EVENT_VSYNC || disp != HWC_DISPLAY_PRIMARY) {
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int hwc_blank(struct hwc_composer_device_1* /*dev*/, int disp, int /*blank*/) {
+ if (disp != HWC_DISPLAY_PRIMARY) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) {
+ vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev);
+ switch (what) {
+ case HWC_BACKGROUND_LAYER_SUPPORTED:
+ // we don't support the background layer
+ *value = 0;
+ break;
+ case HWC_VSYNC_PERIOD:
+ *value = pdev->vsync_period_ns;
+ break;
+ case HWC_DISPLAY_TYPES_SUPPORTED:
+ // We only support the primary display
+ *value = HWC_DISPLAY_PRIMARY_BIT;
+ break;
+ default:
+ // unsupported query
+ ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+void hwc_registerProcs(struct hwc_composer_device_1* dev,
+ hwc_procs_t const* procs) {
+ reinterpret_cast<vsoc_hwc_device*>(dev)->procs = procs;
+}
+
+void hwc_dump(struct hwc_composer_device_1* /*dev*/, char* /*buff*/,
+ int /*buff_len*/) {}
+
+int hwc_getDisplayConfigs(struct hwc_composer_device_1* /*dev*/, int disp,
+ uint32_t* configs, size_t* numConfigs) {
+ if (*numConfigs == 0) return 0;
+
+ if (disp == HWC_DISPLAY_PRIMARY) {
+ configs[0] = 0;
+ *numConfigs = 1;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int32_t vsoc_hwc_attribute(uint32_t attribute) {
+ auto screen_view = ScreenRegionView::GetInstance();
+ switch (attribute) {
+ case HWC_DISPLAY_VSYNC_PERIOD:
+ return 1000000000 / screen_view->refresh_rate_hz();
+ case HWC_DISPLAY_WIDTH:
+ return screen_view->x_res();
+ case HWC_DISPLAY_HEIGHT:
+ return screen_view->y_res();
+ case HWC_DISPLAY_DPI_X:
+ case HWC_DISPLAY_DPI_Y:
+ // The number of pixels per thousand inches
+ return screen_view->dpi() * 1000;
+ case HWC_DISPLAY_COLOR_TRANSFORM:
+ // TODO(jemoreira): Add the other color transformations
+ return HAL_COLOR_TRANSFORM_IDENTITY;
+ default:
+ ALOGE("unknown display attribute %u", attribute);
+ return -EINVAL;
+ }
+}
+
+int hwc_getDisplayAttributes(struct hwc_composer_device_1* /*dev*/, int disp,
+ uint32_t /*config*/, const uint32_t* attributes,
+ int32_t* values) {
+ if (disp != HWC_DISPLAY_PRIMARY) {
+ ALOGE("Unknown display type %u", disp);
+ return -EINVAL;
+ }
+
+ for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
+ values[i] = vsoc_hwc_attribute(attributes[i]);
+ }
+
+ return 0;
+}
+
+int hwc_close(hw_device_t* device) {
+ vsoc_hwc_device* dev = reinterpret_cast<vsoc_hwc_device*>(device);
+ pthread_kill(dev->vsync_thread, SIGTERM);
+ pthread_join(dev->vsync_thread, NULL);
+ delete dev;
+ return 0;
+}
+
+int hwc_open(const struct hw_module_t* module, const char* name,
+ struct hw_device_t** device) {
+ ALOGI("Opening vsoc hwcomposer device: %s", __FUNCTION__);
+ if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
+ ALOGE("%s called with bad name %s", __FUNCTION__, name);
+ return -EINVAL;
+ }
+
+ vsoc_hwc_device* dev = new vsoc_hwc_device();
+ if (!dev) {
+ ALOGE("%s failed to allocate dev", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(dev, 0, sizeof(*dev));
+
+ int refreshRate = 60;
+ dev->vsync_period_ns = 1000000000 / refreshRate;
+ struct timespec rt;
+ if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
+ ALOGE("%s:%d error in clock_gettime: %s", __FILE__, __LINE__,
+ strerror(errno));
+ }
+ dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+
+ dev->base.common.tag = HARDWARE_DEVICE_TAG;
+ dev->base.common.version = HWC_DEVICE_API_VERSION_1_1;
+ dev->base.common.module = const_cast<hw_module_t*>(module);
+ dev->base.common.close = hwc_close;
+
+ dev->base.prepare = hwc_prepare;
+ dev->base.set = hwc_set;
+ dev->base.query = hwc_query;
+ dev->base.registerProcs = hwc_registerProcs;
+ dev->base.dump = hwc_dump;
+ dev->base.blank = hwc_blank;
+ dev->base.eventControl = hwc_eventControl;
+ dev->base.getDisplayConfigs = hwc_getDisplayConfigs;
+ dev->base.getDisplayAttributes = hwc_getDisplayAttributes;
+
+ if (!ScreenRegionView::GetInstance()) {
+ ALOGE("Unable to open screen region (%s)", __FUNCTION__);
+ delete dev;
+ return -1;
+ }
+
+ int ret = pthread_create(&dev->vsync_thread, NULL, vsync_thread, dev);
+ if (ret) {
+ ALOGE("failed to start vsync thread: %s", strerror(ret));
+ delete dev;
+ } else {
+ *device = &dev->base.common;
+ }
+
+ return -ret;
+}
+
+struct hw_module_methods_t hwc_module_methods = {hwc_open};
+
+} // namespace
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+ HWC_MODULE_API_VERSION_0_1,
+ HARDWARE_HAL_API_VERSION,
+ HWC_HARDWARE_MODULE_ID,
+ "Cuttlefish hwcomposer module",
+ "Google",
+ &hwc_module_methods,
+ NULL,
+ {0}}};
diff --git a/guest/hals/hwcomposer/vsoc/Android.bp b/guest/hals/hwcomposer/vsoc/Android.bp
new file mode 100644
index 0000000..559a1c1
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/Android.bp
@@ -0,0 +1,47 @@
+// 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.
+
+
+cc_library_shared {
+ name: "hwcomposer.cutf_ivsh_ashmem",
+ relative_install_path: "hw",
+ defaults: ["cuttlefish_guest_only"],
+ cflags: [
+ "-DGATHER_STATS",
+ // Uncomment the following line to revert to GL compositions
+ // "-DUSE_OLD_HWCOMPOSER",
+ ],
+ srcs: [
+ "vsoc_screen_view.cpp",
+ "hwcomposer.cpp",
+ ],
+ export_include_dirs: ["."],
+ static_libs: [
+ "libyuv_static",
+ "hwcomposer_common"
+ ],
+ shared_libs: [
+ "cuttlefish_auto_resources",
+ "liblog",
+ "libhardware",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libsync",
+ "libjpeg",
+ "libcuttlefish_utils",
+ "libcuttlefish_fs",
+ "vsoc_lib",
+ ],
+}
diff --git a/guest/hals/hwcomposer/vsoc/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
new file mode 100644
index 0000000..67713f2
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "hwc.cutf_cvm"
+
+#include <cutils/properties.h>
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/hwcomposer_defs.h>
+#include <log/log.h>
+
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
+
+#include "guest/hals/hwcomposer/vsoc/vsoc_screen_view.h"
+
+static int hwc_open(const struct hw_module_t* module, const char* name,
+ struct hw_device_t** device) {
+ std::unique_ptr<cvd::ScreenView> screen_view(new cvd::VSoCScreenView());
+ if (!screen_view) {
+ ALOGE("Failed to instantiate screen view");
+ return -1;
+ }
+
+ return cvd::cvd_hwc_open(std::move(screen_view), module, name, device);
+}
+
+static struct hw_module_methods_t hwc_module_methods = {
+ hwc_open,
+};
+
+hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
+ HWC_MODULE_API_VERSION_0_1,
+ HARDWARE_HAL_API_VERSION,
+ HWC_HARDWARE_MODULE_ID,
+ "VSoC hwcomposer module",
+ "Google",
+ &hwc_module_methods,
+ NULL,
+ {0}}};
diff --git a/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp
new file mode 100644
index 0000000..6ca9ac2
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "guest/hals/hwcomposer/vsoc/vsoc_screen_view.h"
+
+#include <sys/time.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+
+using vsoc::layout::screen::TimeSpec;
+using vsoc::screen::ScreenRegionView;
+
+namespace cvd {
+namespace {
+
+TimeSpec TimeSpecFromSystemStruct(const timespec* spec) {
+ return {static_cast<uint32_t>(spec->tv_sec),
+ static_cast<uint32_t>(spec->tv_nsec), 0};
+}
+
+void VSoCStatsFromCvdStats(vsoc::layout::screen::CompositionStats* vsoc_stats,
+ const cvd::CompositionStats* stats) {
+ vsoc_stats->num_prepare_calls = stats->num_prepare_calls;
+ vsoc_stats->num_layers = stats->num_layers;
+ vsoc_stats->num_hwcomposited_layers = stats->num_hwcomposited_layers;
+ vsoc_stats->last_vsync = TimeSpecFromSystemStruct(&stats->last_vsync);
+ vsoc_stats->prepare_start = TimeSpecFromSystemStruct(&stats->prepare_start);
+ vsoc_stats->prepare_end = TimeSpecFromSystemStruct(&stats->prepare_end);
+ vsoc_stats->set_start = TimeSpecFromSystemStruct(&stats->set_start);
+ vsoc_stats->set_end = TimeSpecFromSystemStruct(&stats->set_end);
+}
+
+} // namespace
+
+VSoCScreenView::VSoCScreenView()
+ : region_view_(ScreenRegionView::GetInstance()) {}
+
+VSoCScreenView::~VSoCScreenView() {}
+
+void VSoCScreenView::Broadcast(int buffer_id,
+ const cvd::CompositionStats* stats) {
+ if (stats) {
+ vsoc::layout::screen::CompositionStats vsoc_stats;
+ VSoCStatsFromCvdStats(&vsoc_stats, stats);
+ region_view_->BroadcastNewFrame(buffer_id, &vsoc_stats);
+ } else {
+ region_view_->BroadcastNewFrame(buffer_id);
+ }
+}
+
+void* VSoCScreenView::GetBuffer(int fb_index) {
+ return region_view_->GetBuffer(fb_index);
+}
+
+int32_t VSoCScreenView::x_res() const { return region_view_->x_res(); }
+
+int32_t VSoCScreenView::y_res() const { return region_view_->y_res(); }
+
+int32_t VSoCScreenView::dpi() const { return region_view_->dpi(); }
+
+int32_t VSoCScreenView::refresh_rate() const {
+ return region_view_->refresh_rate_hz();
+}
+
+int VSoCScreenView::num_buffers() const {
+ return region_view_->number_of_buffers();
+}
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h
new file mode 100644
index 0000000..1ecace6
--- /dev/null
+++ b/guest/hals/hwcomposer/vsoc/vsoc_screen_view.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/vsoc/lib/screen_region_view.h"
+#include "guest/hals/hwcomposer/common/screen_view.h"
+
+namespace cvd {
+
+class VSoCScreenView : public ScreenView {
+ public:
+ VSoCScreenView();
+ virtual ~VSoCScreenView();
+
+ void Broadcast(int buffer_id,
+ const CompositionStats* stats = nullptr) override;
+ void* GetBuffer(int fb_index) override;
+
+ int32_t x_res() const override;
+ int32_t y_res() const override;
+ int32_t dpi() const override;
+ int32_t refresh_rate() const override;
+
+ int num_buffers() const override;
+
+ private:
+ vsoc::screen::ScreenRegionView* region_view_;
+};
+
+} // namespace cvd
diff --git a/guest/vsoc/lib/Android.bp b/guest/vsoc/lib/Android.bp
new file mode 100644
index 0000000..d534a5d
--- /dev/null
+++ b/guest/vsoc/lib/Android.bp
@@ -0,0 +1,77 @@
+//
+// 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.
+
+cc_binary {
+ name: "vsoc_driver_test",
+ srcs: [
+ "vsoc_driver_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "cuttlefish_auto_resources",
+ "libcuttlefish_fs",
+ "libbase",
+ ],
+ static_libs: [
+ "libgtest",
+ ],
+ cflags: [
+ "-DGTEST_OS_LINUX_ANDROID",
+ "-DGTEST_HAS_STD_STRING",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
+
+cc_binary {
+ name: "vsoc_guest_region_e2e_test",
+ srcs: [
+ "guest_region_e2e_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "cuttlefish_auto_resources",
+ "libcuttlefish_fs",
+ "libbase",
+ ],
+ static_libs: [
+ "libgtest",
+ ],
+ cflags: [
+ "-DGTEST_OS_LINUX_ANDROID",
+ "-DGTEST_HAS_STD_STRING",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
+
+cc_binary {
+ name: "vsoc_managed_region_e2e_test",
+ srcs: [
+ "managed_region_e2e_test.cpp",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "cuttlefish_auto_resources",
+ "libcuttlefish_fs",
+ "libbase",
+ ],
+ static_libs: [
+ "libgtest",
+ ],
+ cflags: [
+ "-DGTEST_OS_LINUX_ANDROID",
+ "-DGTEST_HAS_STD_STRING",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/vsoc/lib/e2e_test_common.h b/guest/vsoc/lib/e2e_test_common.h
new file mode 100644
index 0000000..521f4c6
--- /dev/null
+++ b/guest/vsoc/lib/e2e_test_common.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#define DEATH_TEST_MESSAGE "abort converted to exit of 2 during death test"
+
+static inline void disable_tombstones() {
+ // We don't want a tombstone, and we're already in the child, so we modify the
+ // behavior of LOG(ABORT) to print the well known message and do an
+ // error-based exit.
+ android::base::SetAborter([](const char*) {
+ fputs(DEATH_TEST_MESSAGE, stderr);
+ fflush(stderr);
+ exit(2);
+ });
+}
diff --git a/guest/vsoc/lib/gralloc_region_view.cpp b/guest/vsoc/lib/gralloc_region_view.cpp
new file mode 100644
index 0000000..2c42f57
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 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 "guest/vsoc/lib/gralloc_region_view.h"
+
+#include <common/vsoc/lib/lock_guard.h>
+#include <log/log.h>
+#include <sys/types.h>
+#include <uapi/vsoc_shm.h>
+#include <atomic>
+
+using vsoc::gralloc::GrallocRegionView;
+using vsoc::layout::gralloc::BufferEntry;
+using vsoc::layout::gralloc::GrallocBufferLayout;
+using vsoc::layout::gralloc::GrallocManagerLayout;
+
+namespace {
+template <typename T>
+inline T gralloc_align(T val) {
+ return (val + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
+// Use the thread id to identify the original creator of a buffer.
+inline uint32_t gralloc_owned_value() {
+ return gettid();
+}
+
+} // namespace
+
+bool GrallocRegionView::Open() {
+ if (!vsoc::ManagerRegionView<GrallocRegionView, GrallocManagerLayout>::Open()) {
+ return false;
+ }
+ std::shared_ptr<vsoc::RegionControl> managed_region =
+ vsoc::RegionControl::Open(
+ GrallocManagerLayout::ManagedRegion::region_name);
+ if (!managed_region) {
+ LOG_FATAL("Unable to open managed region");
+ return false;
+ }
+ offset_of_buffer_memory_ = gralloc_align<uint32_t>(
+ managed_region->region_desc().offset_of_region_data);
+ total_buffer_memory_ =
+ managed_region->region_size() - offset_of_buffer_memory_;
+
+ // TODO(jemoreira): Handle the case of unexpected values in the region.
+ return true;
+}
+
+int GrallocRegionView::AllocateBuffer(size_t size, uint32_t* begin_offset) {
+ size = gralloc_align<size_t>(size);
+ // Cache the value of buffer_count in shared memory.
+ uint32_t buffer_count_local = 0;
+ {
+ vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+ buffer_count_local = data()->buffer_count;
+ }
+ // Find a free buffer entry of the appropriate size.
+ for (uint32_t idx = 0; idx < buffer_count_local; ++idx) {
+ BufferEntry& entry = data()->buffers_table[idx];
+ if (entry.owned_by == VSOC_REGION_FREE && entry.buffer_size() == size) {
+ int fd = control_->CreateFdScopedPermission(
+ GrallocManagerLayout::ManagedRegion::region_name,
+ pointer_to_region_offset(&entry.owned_by),
+ gralloc_owned_value(),
+ entry.buffer_begin,
+ entry.buffer_end);
+ if (fd >= 0) {
+ if (begin_offset) {
+ *begin_offset = entry.buffer_begin;
+ }
+ return fd;
+ }
+ }
+ }
+
+ // We couldn't find any suitable buffer, create one
+ {
+ vsoc::LockGuard<vsoc::layout::GuestLock>(&data()->new_buffer_lock);
+ // don't use the cached value here!!!
+ uint32_t idx = data()->buffer_count;
+ if (pointer_to_region_offset(&data()->buffers_table[idx + 1]) >
+ control_->region_size()) {
+ ALOGE(
+ "Out of memory in gralloc_manager (total: %d, used: %d, "
+ "requested: %d)",
+ static_cast<int>(control_->region_size()),
+ static_cast<int>(
+ pointer_to_region_offset(&data()->buffers_table[idx])),
+ static_cast<int>(sizeof(data()->buffers_table[idx])));
+ return -ENOMEM;
+ }
+ if (total_buffer_memory_ - data()->allocated_buffer_memory < size) {
+ ALOGE(
+ "Out of memory in gralloc_memory (total: %d, used: %d, requested: %d)",
+ static_cast<int>(total_buffer_memory_),
+ static_cast<int>(data()->allocated_buffer_memory),
+ static_cast<int>(size));
+ return -ENOMEM;
+ }
+ // Initialize the buffer entry and acquire ownership
+ // Do it before increasing buffer_count so that another thread looking for
+ // free entries doesn't find this one
+ BufferEntry& new_entry = data()->buffers_table[idx];
+ new_entry.buffer_begin =
+ offset_of_buffer_memory_ + data()->allocated_buffer_memory;
+ data()->allocated_buffer_memory += size;
+ new_entry.buffer_end = new_entry.buffer_begin + size;
+ int fd = control_->CreateFdScopedPermission(
+ GrallocManagerLayout::ManagedRegion::region_name,
+ pointer_to_region_offset(&new_entry.owned_by),
+ gralloc_owned_value(),
+ new_entry.buffer_begin,
+ new_entry.buffer_end);
+ if (fd < 0) {
+ LOG_FATAL(
+ "Unexpected error while creating fd scoped permission over "
+ "uncontested memory: %s",
+ strerror(-fd));
+ return fd;
+ }
+ // Increment buffer_count now that the entry can't be taken from us
+ data()->buffer_count++;
+ if (begin_offset) {
+ *begin_offset = new_entry.buffer_begin;
+ }
+ return fd;
+ }
+}
diff --git a/guest/vsoc/lib/gralloc_region_view.h b/guest/vsoc/lib/gralloc_region_view.h
new file mode 100644
index 0000000..ef5553f
--- /dev/null
+++ b/guest/vsoc/lib/gralloc_region_view.h
@@ -0,0 +1,52 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 <common/vsoc/shm/gralloc_layout.h>
+#include <guest/vsoc/lib/manager_region_view.h>
+#include <memory>
+#include <stdlib.h>
+
+namespace vsoc {
+namespace gralloc {
+
+class GrallocRegionView : public vsoc::ManagerRegionView<
+ GrallocRegionView,
+ vsoc::layout::gralloc::GrallocManagerLayout> {
+ public:
+ friend TypedRegionView<
+ GrallocRegionView, vsoc::layout::gralloc::GrallocManagerLayout>;
+ GrallocRegionView() = default;
+ // Allocates a gralloc buffer of (at least) the specified size. Returns a file
+ // descriptor that exposes the buffer when mmapped from 0 to (the page
+ // aligned) size (and fails to mmap anything outside of that range) or a
+ // negative number in case of error (e.g not enough free memory left).
+ // TODO(jemoreira): Include debug info like stride, width, height, etc
+ int AllocateBuffer(size_t size, uint32_t* begin_offset = nullptr);
+
+ protected:
+ GrallocRegionView(const GrallocRegionView&) = delete;
+ GrallocRegionView & operator=(const GrallocRegionView&) = delete;
+
+ bool Open();
+
+ uint32_t offset_of_buffer_memory_{};
+ uint32_t total_buffer_memory_{};
+};
+
+} // namespace gralloc
+} // namespace vsoc
diff --git a/guest/vsoc/lib/guest_lock.cpp b/guest/vsoc/lib/guest_lock.cpp
new file mode 100644
index 0000000..247b48b
--- /dev/null
+++ b/guest/vsoc/lib/guest_lock.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void GuestLock::Lock() {
+ uint32_t tid = gettid();
+ uint32_t expected_value;
+ uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+ while (1) {
+ if (TryLock(tid, &expected_value)) {
+ return;
+ }
+ SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+ }
+}
+
+void GuestLock::Unlock() {
+ Sides sides_to_signal = UnlockCommon(gettid());
+ if (sides_to_signal != Sides::NoSides) {
+ SingleSidedSignal::Signal(&lock_uint32_);
+ }
+}
+
+bool GuestLock::Recover() {
+ return RecoverSingleSided();
+}
+
+} // namespace layout
+} // namespace vsoc
diff --git a/guest/vsoc/lib/guest_region_e2e_test.cpp b/guest/vsoc/lib/guest_region_e2e_test.cpp
new file mode 100644
index 0000000..5fd644a
--- /dev/null
+++ b/guest/vsoc/lib/guest_region_e2e_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the guest.
+ */
+
+#include "guest/vsoc/lib/e2e_test_common.h"
+#include "common/vsoc/lib/e2e_test_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+template <typename View>
+void DeathTestView() {
+ disable_tombstones();
+ // View::GetInstance should never return.
+ EXPECT_FALSE(!!View::GetInstance());
+}
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+// indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetGuestStrings(View* in) {
+ size_t num_data = in->string_size();
+ EXPECT_LE(2U, num_data);
+ for (size_t i = 0; i < num_data; ++i) {
+ EXPECT_TRUE(!in->guest_string(i)[0] ||
+ !strcmp(in->guest_string(i), View::Layout::guest_pattern));
+ in->set_guest_string(i, View::Layout::guest_pattern);
+ EXPECT_STREQ(in->guest_string(i), View::Layout::guest_pattern);
+ }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+ size_t num_data = in->string_size();
+ EXPECT_LE(2U, num_data);
+ for (size_t i = 0; i < num_data; ++i) {
+ EXPECT_STREQ(View::Layout::host_pattern, in->host_string(i));
+ }
+}
+
+TEST(RegionTest, BasicPeerTests) {
+ auto primary = vsoc::E2EPrimaryRegionView::GetInstance();
+ auto secondary = vsoc::E2ESecondaryRegionView::GetInstance();
+ ASSERT_TRUE(!!primary);
+ ASSERT_TRUE(!!secondary);
+ LOG(INFO) << "Regions are open";
+ SetGuestStrings(primary);
+ LOG(INFO) << "Primary guest strings are set";
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+ LOG(INFO) << "Verified no early second interrupt";
+ EXPECT_TRUE(primary->MaybeInterruptPeer());
+ LOG(INFO) << "Interrupt sent. Waiting for first interrupt from peer";
+ primary->WaitForInterrupt();
+ LOG(INFO) << "First interrupt received";
+ CheckPeerStrings(primary);
+ LOG(INFO) << "Verified peer's primary strings";
+ SetGuestStrings(secondary);
+ LOG(INFO) << "Secondary guest strings are set";
+ EXPECT_TRUE(secondary->MaybeInterruptPeer());
+ LOG(INFO) << "Second interrupt sent";
+ secondary->WaitForInterrupt();
+ LOG(INFO) << "Second interrupt received";
+ CheckPeerStrings(secondary);
+ LOG(INFO) << "Verified peer's secondary strings";
+
+ // Test signals
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+ LOG(INFO) << "Verified no early second signal";
+ primary->SendSignal(vsoc::layout::Sides::Peer,
+ &primary->data()->guest_to_host_signal);
+ LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+ primary->WaitForInterrupt();
+ int count = 0; // counts the number of signals received.
+ primary->ProcessSignalsFromPeer(
+ [&primary, &count](uint32_t offset) {
+ ++count;
+ EXPECT_TRUE(offset == primary->host_to_guest_signal_offset());
+ });
+ EXPECT_TRUE(count == 1);
+ LOG(INFO) << "Signal received on primary region";
+ secondary->SendSignal(vsoc::layout::Sides::Peer,
+ &secondary->data()->guest_to_host_signal);
+ LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+ secondary->WaitForInterrupt();
+ count = 0;
+ secondary->ProcessSignalsFromPeer(
+ [&secondary, &count](uint32_t offset) {
+ ++count;
+ EXPECT_TRUE(offset == secondary->host_to_guest_signal_offset());
+ });
+ EXPECT_TRUE(count == 1);
+ LOG(INFO) << "Signal received on secondary region";
+
+ EXPECT_FALSE(primary->HasIncomingInterrupt());
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+ LOG(INFO) << "PASS: BasicPeerTests";
+}
+
+TEST(RegionTest, MissingRegionDeathTest) {
+ // EXPECT_DEATH creates a child for the test, so we do it out here.
+ // DeathTestGuestRegion will actually do the deadly call after ensuring
+ // that we don't create an unwanted tombstone.
+ EXPECT_EXIT(DeathTestView<vsoc::E2EUnfindableRegionView>(),
+ testing::ExitedWithCode(2),
+ ".*" DEATH_TEST_MESSAGE ".*");
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2) {
+ // gtest tries to leave temporary files in the current directory, so make the
+ // current directory something that we control.
+ if (chdir(argv[1]) != 0) {
+ abort();
+ }
+ }
+ android::base::InitLogging(argv);
+ testing::InitGoogleTest(&argc, argv);
+ int rval = RUN_ALL_TESTS();
+ if (!rval) {
+ auto region = vsoc::E2EPrimaryRegionView::GetInstance();
+ region->guest_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+ LOG(INFO) << "stage_1_guest_region_e2e_tests PASSED";
+ } else {
+ LOG(ERROR) << "stage_1_guest_region_e2e_tests FAILED";
+ }
+ return rval;
+}
diff --git a/guest/vsoc/lib/managed_region_e2e_test.cpp b/guest/vsoc/lib/managed_region_e2e_test.cpp
new file mode 100644
index 0000000..390a6b7
--- /dev/null
+++ b/guest/vsoc/lib/managed_region_e2e_test.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/managed_e2e_test_region_layout.h"
+#include "guest/vsoc/lib/e2e_test_common.h"
+#include "guest/vsoc/lib/manager_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+using vsoc::layout::e2e_test::E2EManagedTestRegionLayout;
+using vsoc::layout::e2e_test::E2EManagerTestRegionLayout;
+
+// Region view classes to allow calling the Open() function from the test.
+class E2EManagedTestRegionView
+ : public vsoc::TypedRegionView<
+ E2EManagedTestRegionView,
+ E2EManagedTestRegionLayout> {
+ public:
+ using vsoc::TypedRegionView<
+ E2EManagedTestRegionView, E2EManagedTestRegionLayout>::Open;
+};
+class E2EManagerTestRegionView
+ : public vsoc::ManagerRegionView<
+ E2EManagerTestRegionView,
+ E2EManagerTestRegionLayout> {
+ public:
+ using vsoc::ManagerRegionView<
+ E2EManagerTestRegionView, E2EManagerTestRegionLayout>::Open;
+};
+
+class ManagedRegionTest {
+ public:
+ void testManagedRegionFailMap() {
+ E2EManagedTestRegionView managed_region;
+ disable_tombstones();
+ // managed_region.Open should never return.
+ EXPECT_FALSE(managed_region.Open());
+ }
+
+ void testManagedRegionMap() {
+ EXPECT_TRUE(manager_region_.Open());
+
+ // Maps correctly with permission
+ const uint32_t owned_value = 65, begin_offset = 4096, end_offset = 8192;
+ int perm_fd = manager_region_.CreateFdScopedPermission(
+ &manager_region_.data()->data[0], owned_value, begin_offset,
+ end_offset);
+ EXPECT_TRUE(perm_fd >= 0);
+ fd_scoped_permission perm;
+ ASSERT_TRUE(ioctl(perm_fd, VSOC_GET_FD_SCOPED_PERMISSION, &perm) == 0);
+ void* mapped_ptr = mmap(NULL, perm.end_offset - perm.begin_offset,
+ PROT_WRITE | PROT_READ, MAP_SHARED, perm_fd, 0);
+ EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+
+ // Owned value gets written
+ EXPECT_TRUE(manager_region_.data()->data[0] == owned_value);
+
+ // Data written to the mapped memory stays there after unmap
+ std::string str = "managed by e2e_manager";
+ strcpy(reinterpret_cast<char*>(mapped_ptr), str.c_str());
+ EXPECT_TRUE(munmap(mapped_ptr, end_offset - begin_offset) == 0);
+ mapped_ptr = mmap(NULL, end_offset - begin_offset, PROT_WRITE | PROT_READ,
+ MAP_SHARED, perm_fd, 0);
+ EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+ EXPECT_TRUE(strcmp(reinterpret_cast<char*>(mapped_ptr), str.c_str()) == 0);
+
+ // Create permission elsewhere in the region, map same offset and length,
+ // ensure data isn't there
+ EXPECT_TRUE(munmap(mapped_ptr, end_offset - begin_offset) == 0);
+ close(perm_fd);
+ EXPECT_TRUE(manager_region_.data()->data[0] == 0);
+ perm_fd = manager_region_.CreateFdScopedPermission(
+ &manager_region_.data()->data[1], owned_value, begin_offset + 4096,
+ end_offset + 4096);
+ EXPECT_TRUE(perm_fd >= 0);
+ mapped_ptr = mmap(NULL, end_offset - begin_offset, PROT_WRITE | PROT_READ,
+ MAP_SHARED, perm_fd, 0);
+ EXPECT_FALSE(mapped_ptr == MAP_FAILED);
+ EXPECT_FALSE(strcmp(reinterpret_cast<char*>(mapped_ptr), str.c_str()) == 0);
+ }
+ ManagedRegionTest() {}
+
+ private:
+ E2EManagerTestRegionView manager_region_;
+};
+
+TEST(ManagedRegionTest, ManagedRegionFailMap) {
+ ManagedRegionTest test;
+ EXPECT_EXIT(test.testManagedRegionFailMap(), testing::ExitedWithCode(2),
+ ".*" DEATH_TEST_MESSAGE ".*");
+}
+
+TEST(ManagedRegionTest, ManagedRegionMap) {
+ ManagedRegionTest test;
+ test.testManagedRegionMap();
+}
+
+int main(int argc, char** argv) {
+ if (argc == 2) {
+ // gtest tries to leave temporary files in the current directory, so make the
+ // current directory something that we control.
+ if (chdir(argv[1]) != 0) {
+ abort();
+ }
+ }
+ android::base::InitLogging(argv);
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/guest/vsoc/lib/manager_region_view.h b/guest/vsoc/lib/manager_region_view.h
new file mode 100644
index 0000000..c2a873d
--- /dev/null
+++ b/guest/vsoc/lib/manager_region_view.h
@@ -0,0 +1,58 @@
+#pragma once
+
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/typed_region_view.h"
+
+namespace vsoc {
+
+/**
+ * Adds methods to create file descriptor scoped permissions. Just like
+ * TypedRegionView it can be directly constructed or subclassed.
+ *
+ * The Layout type must (in addition to requirements for TypedRegionView) also
+ * provide a nested type for the layout of the managed region.
+ */
+template <typename View, typename Layout>
+class ManagerRegionView : public TypedRegionView<View, Layout> {
+ public:
+ ManagerRegionView() = default;
+ /**
+ * Creates a fd scoped permission on the managed region.
+ *
+ * The managed_region_fd is in/out parameter that can be a not yet open file
+ * descriptor. If the fd is not open yet it will open the managed region
+ * device and then create the permission. If the function returns EBUSY
+ * (meaning that we lost the race to acquire the memory) the same fd can (and
+ * is expected to) be used in a subsequent call to create a permission on
+ * another memory location.
+ *
+ * On success returns an open fd with the requested permission asociated to
+ * it. If another thread/process acquired ownership of *owner_ptr before this
+ * one returns -EBUSY, returns a different negative number otherwise.
+ */
+ int CreateFdScopedPermission(uint32_t* owner_ptr, uint32_t owned_val,
+ uint32_t begin_offset,
+ uint32_t end_offset) {
+ return this->control_->CreateFdScopedPermission(
+ Layout::ManagedRegion::region_name,
+ this->pointer_to_region_offset(owner_ptr), owned_val, begin_offset,
+ end_offset);
+ }
+};
+
+} // namespace vsoc
diff --git a/guest/vsoc/lib/region_control.cpp b/guest/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..1fcbd13
--- /dev/null
+++ b/guest/vsoc/lib/region_control.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <thread>
+
+#include <android-base/logging.h>
+#include <uapi/vsoc_shm.h>
+
+using cvd::SharedFD;
+
+namespace {
+class GuestRegionControl : public vsoc::RegionControl {
+ public:
+ explicit GuestRegionControl(const SharedFD& region_fd,
+ const vsoc_device_region& desc)
+ : region_fd_{region_fd} {
+ region_desc_ = desc;
+ }
+ virtual bool InterruptPeer() override;
+ virtual void InterruptSelf() override;
+ virtual void WaitForInterrupt() override;
+ virtual void* Map() override;
+ virtual int SignalSelf(uint32_t offset) override;
+ virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override;
+
+ protected:
+ int CreateFdScopedPermission(const char* managed_region_name,
+ uint32_t owner_offset, uint32_t owned_val,
+ uint32_t begin_offset,
+ uint32_t end_offset) override;
+ cvd::SharedFD region_fd_;
+};
+
+std::string device_path_from_name(const char* region_name) {
+ return std::string("/dev/") + region_name;
+}
+
+bool GuestRegionControl::InterruptPeer() {
+ int rval = region_fd_->Ioctl(VSOC_SEND_INTERRUPT_TO_HOST, 0);
+ if ((rval == -1) && (errno != EBUSY)) {
+ LOG(INFO) << __FUNCTION__ << ": ioctl failed (" << strerror(errno) << ")";
+ }
+ return !rval;
+}
+
+void GuestRegionControl::InterruptSelf() {
+ region_fd_->Ioctl(VSOC_SELF_INTERRUPT, 0);
+}
+
+void GuestRegionControl::WaitForInterrupt() {
+ region_fd_->Ioctl(VSOC_WAIT_FOR_INCOMING_INTERRUPT, 0);
+}
+
+int GuestRegionControl::SignalSelf(uint32_t offset) {
+ return region_fd_->Ioctl(VSOC_COND_WAKE, reinterpret_cast<void*>(offset));
+}
+
+int GuestRegionControl::WaitForSignal(uint32_t offset,
+ uint32_t expected_value) {
+ struct vsoc_cond_wait wait;
+ wait.offset = offset;
+ wait.value = expected_value;
+ wait.wake_time_sec = 0;
+ wait.wake_time_nsec = 0;
+ wait.wait_type = VSOC_WAIT_IF_EQUAL;
+ wait.wakes = 1000;
+ wait.reserved_1 = 0;
+ int rval = region_fd_->Ioctl(VSOC_COND_WAIT, &wait);
+ if (rval == -1) {
+ return rval;
+ }
+ // Clamp the number of wakes if it overflows an integer.
+ rval = wait.wakes;
+ if (rval >= 0) {
+ return rval;
+ }
+ return INT_MAX;
+}
+
+int GuestRegionControl::CreateFdScopedPermission(
+ const char* managed_region_name, uint32_t owner_offset,
+ uint32_t owned_value, uint32_t begin_offset,
+ uint32_t end_offset) {
+ if (!region_fd_->IsOpen()) {
+ LOG(FATAL) << "Can't create permission before opening controller region";
+ return -EINVAL;
+ }
+ int managed_region_fd =
+ open(device_path_from_name(managed_region_name).c_str(), O_RDWR);
+ if (managed_region_fd < 0) {
+ int errno_ = errno;
+ LOG(FATAL) << "Can't open managed region: " << managed_region_name;
+ return -errno_;
+ }
+
+ fd_scoped_permission_arg perm;
+ perm.perm.begin_offset = begin_offset;
+ perm.perm.end_offset = end_offset;
+ perm.perm.owned_value = owned_value;
+ perm.perm.owner_offset = owner_offset;
+ perm.managed_region_fd = managed_region_fd;
+ LOG(INFO) << "owner offset: " << perm.perm.owner_offset;
+ int retval = region_fd_->Ioctl(VSOC_CREATE_FD_SCOPED_PERMISSION, &perm);
+ if (retval) {
+ int errno_ = errno;
+ close(managed_region_fd);
+ if (errno_ != EBUSY) {
+ LOG(FATAL) << "Unable to create fd scoped permission ("
+ << strerror(errno_) << ")";
+ }
+ return -errno_;
+ }
+ return managed_region_fd;
+}
+
+void* GuestRegionControl::Map() {
+ region_base_ =
+ region_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+ if (region_base_ == MAP_FAILED) {
+ LOG(FATAL) << "mmap failed (" << region_fd_->StrError() << ")";
+ region_base_ = nullptr;
+ }
+ return region_base_;
+}
+} // namespace
+
+// domain is here to ensure that this method has the same signature as the
+// method on host regions.
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+ const char* region_name) {
+ std::string path = device_path_from_name(region_name);
+ SharedFD fd = SharedFD::Open(path.c_str(), O_RDWR);
+ if (!fd->IsOpen()) {
+ LOG(FATAL) << "Unable to open region " << region_name << " ("
+ << fd->StrError() << ")";
+ return nullptr;
+ }
+ vsoc_device_region desc;
+ if (fd->Ioctl(VSOC_DESCRIBE_REGION, &desc)) {
+ LOG(FATAL) << "Unable to obtain region descriptor (" << fd->StrError()
+ << ")";
+ return nullptr;
+ }
+ return std::shared_ptr<vsoc::RegionControl>(new GuestRegionControl(fd, desc));
+}
diff --git a/guest/vsoc/lib/region_view.cpp b/guest/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..db1bec0
--- /dev/null
+++ b/guest/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+ return control_->region_desc().host_to_guest_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+ return control_->region_desc().guest_to_host_signal_table;
+}
diff --git a/guest/vsoc/lib/vsoc_driver_test.cpp b/guest/vsoc/lib/vsoc_driver_test.cpp
new file mode 100644
index 0000000..ff20ecb
--- /dev/null
+++ b/guest/vsoc/lib/vsoc_driver_test.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+/*
+ * Stand-alone tests for the ioctls in the vsoc driver.
+ */
+
+#include "uapi/vsoc_shm.h"
+#include <atomic>
+#include <stdint.h>
+#include "common/vsoc/lib/e2e_test_region_view.h"
+#include "guest/vsoc/lib/manager_region_view.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+void BasicWaitForSignal(vsoc::E2EPrimaryRegionView* region,
+ uint32_t expected_start,
+ uint32_t expected_finish) {
+ ASSERT_EQ(expected_start, region->read_guest_self_register());
+ int rval = region->wait_guest_self_register(expected_start);
+ EXPECT_LE(0, rval);
+ EXPECT_GT(5, rval);
+ EXPECT_EQ(expected_finish, region->read_guest_self_register());
+}
+
+TEST(FutexTest, BasicFutexTests) {
+ constexpr uint32_t INITIAL_SIGNAL = 0;
+ constexpr uint32_t SILENT_UPDATE_SIGNAL = 1;
+ constexpr uint32_t WAKE_SIGNAL = 2;
+ auto region = vsoc::E2EPrimaryRegionView::GetInstance();
+ ASSERT_TRUE(region != NULL);
+ LOG(INFO) << "Regions are open";
+ region->write_guest_self_register(INITIAL_SIGNAL);
+ std::thread waiter(BasicWaitForSignal, region, INITIAL_SIGNAL, WAKE_SIGNAL);
+ sleep(1);
+ LOG(INFO) << "Still waiting. Trying to wake wrong address";
+ region->signal_guest_to_host_register();
+ sleep(1);
+ LOG(INFO) << "Still waiting. Trying to wake without update";
+ region->signal_guest_self_register();
+ sleep(1);
+ LOG(INFO) << "Still waiting. Trying to wake without signal";
+ region->write_guest_self_register(SILENT_UPDATE_SIGNAL);
+ sleep(1);
+ LOG(INFO) << "Still waiting. Trying to wake with signal";
+ region->write_guest_self_register(WAKE_SIGNAL);
+ region->signal_guest_self_register();
+ waiter.join();
+ LOG(INFO) << "Wake worked";
+ LOG(INFO) << "PASS: BasicPeerTests";
+}
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv);
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/host/commands/Android.bp b/host/commands/Android.bp
index c200611..a6f059d 100644
--- a/host/commands/Android.bp
+++ b/host/commands/Android.bp
@@ -19,6 +19,7 @@
"launch",
"stop_cvd",
"ivserver",
+ "record_audio",
"virtual_usb_manager",
"kernel_log_monitor",
"config_server",
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index 75d85d0..c5ac4c9 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -44,6 +44,7 @@
],
shared_libs: [
"cdisk_spec",
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
@@ -56,6 +57,7 @@
static_libs: [
"libcuttlefish_host_config",
"libcuttlefish_vm_manager",
+ "libivserver",
"libgflags",
"libxml2",
"libjsoncpp",
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index 09e76e6..5ae5a91 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -9,6 +9,7 @@
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "common/vsoc/lib/vsoc_memory.h"
#include "host/commands/assemble_cvd/boot_image_unpacker.h"
#include "host/commands/assemble_cvd/data_image.h"
#include "host/commands/assemble_cvd/image_aggregator.h"
@@ -112,6 +113,11 @@
DEFINE_string(vnc_server_binary,
vsoc::DefaultHostArtifactsPath("bin/vnc_server"),
"Location of the vnc server binary.");
+DEFINE_bool(start_stream_audio, false,
+ "Whether to start the stream audio process.");
+DEFINE_string(stream_audio_binary,
+ vsoc::DefaultHostArtifactsPath("bin/stream_audio"),
+ "Location of the stream_audio binary.");
DEFINE_string(virtual_usb_manager_binary,
vsoc::DefaultHostArtifactsPath("bin/virtual_usb_manager"),
"Location of the virtual usb manager binary.");
@@ -123,6 +129,8 @@
"Location of the ivshmem server binary.");
DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
"The port on which the vnc server should listen");
+DEFINE_int32(stream_audio_port, GetPerInstanceDefault(7444),
+ "The port on which stream_audio should listen.");
DEFINE_string(socket_forward_proxy_binary,
vsoc::DefaultHostArtifactsPath("bin/socket_forward_proxy"),
"Location of the socket_forward_proxy binary.");
@@ -273,6 +281,7 @@
const cvd::BootImageUnpacker& boot_image_unpacker,
const cvd::FetcherConfig& fetcher_config) {
vsoc::CuttlefishConfig tmp_config_obj;
+ auto& memory_layout = *vsoc::VSoCMemoryLayout::Get();
// Set this first so that calls to PerInstancePath below are correct
tmp_config_obj.set_instance_dir(FLAGS_instance_dir);
if (!vm_manager::VmManager::IsValidName(FLAGS_vm_manager)) {
@@ -435,7 +444,7 @@
tmp_config_obj.PerInstanceInternalPath("ivshmem_socket_qemu"));
tmp_config_obj.set_ivshmem_client_socket_path(
tmp_config_obj.PerInstanceInternalPath("ivshmem_socket_client"));
- tmp_config_obj.set_ivshmem_vector_count(0);
+ tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
if (tmp_config_obj.adb_mode().count(vsoc::AdbMode::Usb) > 0) {
tmp_config_obj.set_usb_v1_socket_name(
@@ -478,6 +487,10 @@
tmp_config_obj.set_vnc_server_binary(FLAGS_vnc_server_binary);
tmp_config_obj.set_vnc_server_port(FLAGS_vnc_server_port);
+ tmp_config_obj.set_enable_stream_audio(FLAGS_start_stream_audio);
+ tmp_config_obj.set_stream_audio_binary(FLAGS_stream_audio_binary);
+ tmp_config_obj.set_stream_audio_port(FLAGS_stream_audio_port);
+
tmp_config_obj.set_restart_subprocesses(FLAGS_restart_subprocesses);
tmp_config_obj.set_run_adb_connector(FLAGS_run_adb_connector);
tmp_config_obj.set_adb_connector_binary(FLAGS_adb_connector_binary);
diff --git a/host/commands/ivserver/Android.bp b/host/commands/ivserver/Android.bp
new file mode 100644
index 0000000..4fc47a9
--- /dev/null
+++ b/host/commands/ivserver/Android.bp
@@ -0,0 +1,83 @@
+//
+// Copyright (C) 2017 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.
+
+cc_library_host_static {
+ name: "libivserver",
+ srcs: [
+ "hald_client.cc",
+ "ivserver.cc",
+ "options.cc",
+ "qemu_client.cc",
+ "vsocsharedmem.cc",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ shared_libs: [
+ "libbase",
+ "vsoc_lib",
+ ],
+ static_libs: [
+ "libjsoncpp",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
+
+cc_test_host {
+ name: "hald_client_test",
+ srcs: [
+ "hald_client_test.cc",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ shared_libs: [
+ "libcuttlefish_fs",
+ "cuttlefish_auto_resources",
+ "libbase",
+ "vsoc_lib",
+ ],
+ static_libs: [
+ "libivserver",
+ "libjsoncpp",
+ "libgmock",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
+
+cc_binary_host {
+ name: "ivserver",
+ srcs: [
+ "main.cpp",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ shared_libs: [
+ "vsoc_lib",
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ "cuttlefish_auto_resources",
+ "liblog",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libivserver",
+ "libgflags",
+ "libjsoncpp",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/ivserver/hald_client.cc b/host/commands/ivserver/hald_client.cc
new file mode 100644
index 0000000..98a83f6
--- /dev/null
+++ b/host/commands/ivserver/hald_client.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 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 "host/commands/ivserver/hald_client.h"
+
+#include <string>
+
+#include <glog/logging.h>
+
+namespace ivserver {
+namespace {
+// The protocol between host-clients and the ivserver could change.
+// Clients should verify what version they are talking to during the handshake.
+const uint32_t kHaldClientProtocolVersion = 0;
+} // anonymous namespace
+
+std::unique_ptr<HaldClient> HaldClient::New(const VSoCSharedMemory& shmem,
+ const cvd::SharedFD& clientfd) {
+ std::unique_ptr<HaldClient> res;
+
+ if (!clientfd->IsOpen()) {
+ LOG(WARNING) << "Invalid socket passed to HaldClient: "
+ << clientfd->StrError();
+ return res;
+ }
+
+ res.reset(new HaldClient(clientfd));
+ if (!res->PerformHandshake(shmem)) {
+ LOG(ERROR) << "HalD handshake failed. Dropping connection.";
+ res.reset();
+ }
+
+ return res;
+}
+
+HaldClient::HaldClient(const cvd::SharedFD& client_socket)
+ : client_socket_(client_socket) {}
+
+bool HaldClient::PerformHandshake(const VSoCSharedMemory& shared_mem) {
+ int rval =
+ client_socket_->Send(&kHaldClientProtocolVersion,
+ sizeof(kHaldClientProtocolVersion), MSG_NOSIGNAL);
+ if (rval != sizeof(kHaldClientProtocolVersion)) {
+ LOG(ERROR) << "failed to send protocol version: "
+ << client_socket_->StrError();
+ return false;
+ }
+
+ int16_t region_name_len;
+ if (client_socket_->Recv(®ion_name_len, sizeof(region_name_len),
+ MSG_NOSIGNAL) != sizeof(region_name_len)) {
+ LOG(ERROR) << "Error receiving region name length: "
+ << client_socket_->StrError();
+ return false;
+ }
+
+ if (region_name_len <= 0 ||
+ region_name_len > VSOC_DEVICE_NAME_SZ) {
+ LOG(ERROR) << "Invalid region length received: " << region_name_len;
+ return false;
+ }
+
+ std::vector<char> region_name_data(region_name_len);
+ rval = client_socket_->Recv(region_name_data.data(), region_name_len,
+ MSG_NOSIGNAL);
+ if (rval != region_name_len) {
+ LOG(ERROR) << "Incomplete region name length received. Want: "
+ << region_name_len << ", got: " << rval;
+ return false;
+ }
+
+ std::string region_name(region_name_data.begin(), region_name_data.end());
+ LOG(INFO) << "New HALD requesting region: " << region_name;
+
+ // Send Host, Guest and SharedMemory FDs associated with this region.
+ cvd::SharedFD guest_to_host_efd;
+ cvd::SharedFD host_to_guest_efd;
+
+ if (!shared_mem.GetEventFdPairForRegion(region_name, &guest_to_host_efd,
+ &host_to_guest_efd)) {
+ LOG(ERROR) << "Region " << region_name << " was not found.";
+ return false;
+ }
+
+ if (!guest_to_host_efd->IsOpen()) {
+ LOG(ERROR) << "Host channel is not open; last known error: "
+ << guest_to_host_efd->StrError();
+ return false;
+ }
+
+ if (!host_to_guest_efd->IsOpen()) {
+ LOG(ERROR) << "Guest channel is not open; last known error: "
+ << host_to_guest_efd->StrError();
+ return false;
+ }
+
+ // TODO(ender): delete this once no longer necessary. Currently, absence of
+ // payload makes RecvMsgAndFDs hang forever.
+ uint64_t control_data = 0;
+ struct iovec vec {
+ &control_data, sizeof(control_data)
+ };
+ cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+ cvd::SharedFD fds[3] = {guest_to_host_efd, host_to_guest_efd,
+ shared_mem.SharedMemFD()};
+ rval = client_socket_->SendMsgAndFDs<3>(hdr, MSG_NOSIGNAL, fds);
+ if (rval == -1) {
+ LOG(ERROR) << "failed to send Host FD: " << client_socket_->StrError();
+ return false;
+ }
+
+ LOG(INFO) << "HALD managing region: " << region_name << " connected.";
+ return true;
+}
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/hald_client.h b/host/commands/ivserver/hald_client.h
new file mode 100644
index 0000000..3b0fe2b
--- /dev/null
+++ b/host/commands/ivserver/hald_client.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <unistd.h>
+#include <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// Handles a HAL deamon client connection & handshake.
+class HaldClient final {
+ public:
+ static std::unique_ptr<HaldClient> New(const VSoCSharedMemory &shared_mem,
+ const cvd::SharedFD &client_fd);
+
+ private:
+ cvd::SharedFD client_socket_;
+
+ // Initialize new instance of HAL Client.
+ HaldClient(const cvd::SharedFD &client_fd);
+
+ // Perform handshake with HAL Client.
+ // If the region is not found approprate status (-1) is sent.
+ // Note that for every new client connected a unique ClientConnection object
+ // will be created and after the handshake it will be destroyed.
+ bool PerformHandshake(const VSoCSharedMemory &shared_mem);
+
+ HaldClient(const HaldClient &) = delete;
+ HaldClient &operator=(const HaldClient &) = delete;
+};
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/hald_client_test.cc b/host/commands/ivserver/hald_client_test.cc
new file mode 100644
index 0000000..ee65530
--- /dev/null
+++ b/host/commands/ivserver/hald_client_test.cc
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <memory>
+#include <string>
+#include <thread>
+
+#include <glog/logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/hald_client.h"
+#include "host/commands/ivserver/vsocsharedmem_mock.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SetArgPointee;
+
+namespace ivserver {
+namespace test {
+
+class HaldClientTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ std::string socket_location;
+ ASSERT_TRUE(GetTempLocation(&socket_location))
+ << "Could not create temp file";
+ LOG(INFO) << "Temp file location: " << socket_location;
+
+ hald_server_socket_ = cvd::SharedFD::SocketLocalServer(
+ socket_location.c_str(), false, SOCK_STREAM, 0666);
+ test_socket_ = cvd::SharedFD::SocketLocalClient(socket_location.c_str(),
+ false, SOCK_STREAM);
+ hald_socket_ =
+ cvd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr);
+
+ EXPECT_TRUE(hald_server_socket_->IsOpen());
+ EXPECT_TRUE(test_socket_->IsOpen());
+ EXPECT_TRUE(hald_socket_->IsOpen());
+ }
+
+ bool GetTempLocation(std::string* target) {
+ char temp_location[] = "/tmp/hald-client-test-XXXXXX";
+ mktemp(temp_location);
+ *target = temp_location;
+ if (target->size()) {
+ cleanup_files_.emplace_back(*target);
+ return true;
+ }
+ return false;
+ }
+
+ void TearDown() override {
+ hald_socket_->Close();
+ test_socket_->Close();
+ hald_server_socket_->Close();
+
+ for (const std::string& file : cleanup_files_) {
+ unlink(file.c_str());
+ }
+ }
+
+ protected:
+ ::testing::NiceMock<VSoCSharedMemoryMock> vsoc_;
+
+ cvd::SharedFD hald_server_socket_;
+ cvd::SharedFD hald_socket_;
+ cvd::SharedFD test_socket_;
+
+ private:
+ std::vector<std::string> cleanup_files_;
+};
+
+TEST_F(HaldClientTest, HandshakeTerminatedByHald) {
+ std::thread thread(
+ [this]() {
+ auto client(HaldClient::New(vsoc_, hald_socket_));
+ EXPECT_FALSE(client)
+ << "Handshake should not complete when client terminates early.";
+ });
+
+ test_socket_->Close();
+ thread.join();
+}
+
+TEST_F(HaldClientTest, HandshakeTerminatedByInvalidRegionSize) {
+ uint16_t sizes[] = {0, VSOC_DEVICE_NAME_SZ + 1, 0xffff};
+
+ for (uint16_t size : sizes) {
+ std::thread thread([this, size]() {
+ auto client(HaldClient::New(vsoc_, hald_socket_));
+ EXPECT_FALSE(client) << "Handle should not be created when size is "
+ << size;
+ });
+
+ int32_t proto_version;
+ EXPECT_EQ(sizeof(proto_version),
+ static_cast<size_t>(test_socket_->Recv(
+ &proto_version, sizeof(proto_version), MSG_NOSIGNAL)));
+
+ test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL);
+ thread.join();
+ }
+}
+
+TEST_F(HaldClientTest, FullSaneHandshake) {
+ std::string temp;
+ ASSERT_TRUE(GetTempLocation(&temp));
+ cvd::SharedFD host_fd(
+ cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+ EXPECT_TRUE(host_fd->IsOpen());
+
+ ASSERT_TRUE(GetTempLocation(&temp));
+ cvd::SharedFD guest_fd(
+ cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+ EXPECT_TRUE(guest_fd->IsOpen());
+
+ ASSERT_TRUE(GetTempLocation(&temp));
+ cvd::SharedFD shmem_fd(
+ cvd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+ EXPECT_TRUE(shmem_fd->IsOpen());
+
+ const std::string test_location("testing");
+ EXPECT_CALL(vsoc_, GetEventFdPairForRegion(test_location, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(host_fd), SetArgPointee<2>(guest_fd),
+ Return(true)));
+ EXPECT_CALL(vsoc_, SharedMemFD()).WillOnce(ReturnRef(shmem_fd));
+
+ std::thread thread([this]() {
+ auto client(HaldClient::New(vsoc_, hald_socket_));
+ EXPECT_TRUE(client);
+ });
+
+ int32_t proto_version;
+ EXPECT_EQ(sizeof(proto_version),
+ static_cast<size_t>(test_socket_->Recv(
+ &proto_version, sizeof(proto_version), MSG_NOSIGNAL)));
+
+ uint16_t size = test_location.size();
+ EXPECT_EQ(sizeof(size), static_cast<size_t>(test_socket_->Send(
+ &size, sizeof(size), MSG_NOSIGNAL)));
+ EXPECT_EQ(size, static_cast<size_t>(test_socket_->Send(test_location.data(),
+ size, MSG_NOSIGNAL)));
+
+ // TODO(ender): delete this once no longer necessary. Currently, absence of
+ // payload makes RecvMsgAndFDs hang forever.
+ uint64_t control_data;
+ struct iovec vec {
+ &control_data, sizeof(control_data)
+ };
+ cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+ cvd::SharedFD fds[3];
+
+ EXPECT_GT(test_socket_->RecvMsgAndFDs<3>(hdr, MSG_NOSIGNAL, &fds), 0);
+ EXPECT_TRUE(fds[0]->IsOpen());
+ EXPECT_TRUE(fds[1]->IsOpen());
+ EXPECT_TRUE(fds[2]->IsOpen());
+
+ thread.join();
+}
+
+} // namespace test
+} // namespace ivserver
diff --git a/host/commands/ivserver/ivserver.cc b/host/commands/ivserver/ivserver.cc
new file mode 100644
index 0000000..ca274cc
--- /dev/null
+++ b/host/commands/ivserver/ivserver.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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 "host/commands/ivserver/ivserver.h"
+
+#include <sys/select.h>
+#include <algorithm>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/commands/ivserver/hald_client.h"
+#include "host/commands/ivserver/qemu_client.h"
+
+namespace ivserver {
+
+IVServer::IVServer(const IVServerOptions &options, int qemu_channel_fd,
+ int client_channel_fd)
+ : vsoc_shmem_(VSoCSharedMemory::New(options.shm_file_path)) {
+ if (qemu_channel_fd > 0) {
+ qemu_channel_ = cvd::SharedFD::Dup(qemu_channel_fd);
+ } else {
+ LOG_IF(WARNING, unlink(options.qemu_socket_path.c_str()) == 0)
+ << "Removed existing unix socket: " << options.qemu_socket_path
+ << ". We can't confirm yet whether another instance is running.";
+ qemu_channel_ = cvd::SharedFD::SocketLocalServer(
+ options.qemu_socket_path.c_str(), false, SOCK_STREAM, 0666);
+ }
+ LOG_IF(FATAL, !qemu_channel_->IsOpen())
+ << "Could not create QEmu channel: " << qemu_channel_->StrError();
+
+ if (client_channel_fd > 0) {
+ client_channel_ = cvd::SharedFD::Dup(client_channel_fd);
+ } else {
+ LOG_IF(WARNING, unlink(options.client_socket_path.c_str()) == 0)
+ << "Removed existing unix socket: " << options.client_socket_path
+ << ". We can't confirm yet whether another instance is running.";
+ client_channel_ = cvd::SharedFD::SocketLocalServer(
+ options.client_socket_path.c_str(), false, SOCK_STREAM, 0666);
+ }
+ LOG_IF(FATAL, !client_channel_->IsOpen())
+ << "Could not create Client channel: " << client_channel_->StrError();
+}
+
+void IVServer::Serve() {
+ while (true) {
+ cvd::SharedFDSet rset;
+ rset.Set(qemu_channel_);
+ rset.Set(client_channel_);
+ cvd::Select(&rset, nullptr, nullptr, nullptr);
+
+ if (rset.IsSet(qemu_channel_)) {
+ HandleNewQemuConnection();
+ }
+
+ if (rset.IsSet(client_channel_)) {
+ HandleNewClientConnection();
+ }
+ }
+
+ LOG(FATAL) << "Control reached out of event loop";
+}
+
+void IVServer::HandleNewClientConnection() {
+ std::unique_ptr<HaldClient> res = HaldClient::New(
+ *vsoc_shmem_, cvd::SharedFD::Accept(*client_channel_, nullptr, nullptr));
+ if (!res) {
+ LOG(WARNING) << "Rejecting unsuccessful HALD connection.";
+ }
+}
+
+void IVServer::HandleNewQemuConnection() {
+ std::unique_ptr<QemuClient> res = QemuClient::New(
+ *vsoc_shmem_, cvd::SharedFD::Accept(*qemu_channel_, nullptr, nullptr));
+
+ if (!res) {
+ LOG(WARNING) << "Could not accept new QEmu client.";
+ }
+}
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/ivserver.h b/host/commands/ivserver/ivserver.h
new file mode 100644
index 0000000..d28d14b
--- /dev/null
+++ b/host/commands/ivserver/ivserver.h
@@ -0,0 +1,51 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/options.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// This class is responsible for orchestrating the setup and then serving
+// new connections.
+class IVServer final {
+ public:
+ // The qemu_channel_fd and client_channel_fd are the server sockets. If
+ // non-positive values are provided the server will create those sockets
+ // itself.
+ IVServer(const IVServerOptions &options, int qemu_channel_fd,
+ int client_channel_fd);
+ IVServer(const IVServer &) = delete;
+ IVServer& operator=(const IVServer&) = delete;
+
+ // Serves incoming client and qemu connection.
+ // This method should never return.
+ void Serve();
+
+ private:
+ void HandleNewClientConnection();
+ void HandleNewQemuConnection();
+
+ std::unique_ptr<VSoCSharedMemory> vsoc_shmem_;
+ cvd::SharedFD qemu_channel_;
+ cvd::SharedFD client_channel_;
+};
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/main.cpp b/host/commands/ivserver/main.cpp
new file mode 100644
index 0000000..16aa747
--- /dev/null
+++ b/host/commands/ivserver/main.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 <unistd.h>
+
+#include <thread>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/commands/ivserver/ivserver.h"
+#include "host/commands/ivserver/options.h"
+
+DEFINE_int32(
+ qemu_socket_fd, -1,
+ "A file descriptor to use as the server Qemu connects to. If not specified "
+ "a unix socket will be created in the default location.");
+DEFINE_int32(
+ client_socket_fd, -1,
+ "A file descriptor to use as the server clients connects to. If not "
+ "specified a unix socket will be created in the default location.");
+
+int main(int argc, char* argv[]) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (!config) {
+ LOG(ERROR) << "Unable to get cuttlefish config";
+ return 1;
+ }
+
+ ivserver::IVServer server(
+ ivserver::IVServerOptions(config->mempath(),
+ config->ivshmem_qemu_socket_path(),
+ vsoc::GetDomain()),
+ FLAGS_qemu_socket_fd, FLAGS_client_socket_fd);
+
+ // Close the file descriptors as they have been dupped by now.
+ if (FLAGS_qemu_socket_fd > 0) {
+ close(FLAGS_qemu_socket_fd);
+ }
+ if (FLAGS_client_socket_fd > 0) {
+ close(FLAGS_client_socket_fd);
+ }
+
+ server.Serve();
+
+ return 0;
+}
diff --git a/host/commands/ivserver/options.cc b/host/commands/ivserver/options.cc
new file mode 100644
index 0000000..0f19397
--- /dev/null
+++ b/host/commands/ivserver/options.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 "host/commands/ivserver/options.h"
+
+namespace ivserver {
+
+IVServerOptions::IVServerOptions(const std::string &shm_file_path,
+ const std::string &qemu_socket_path,
+ const std::string &client_socket_path)
+ : shm_file_path(shm_file_path),
+ qemu_socket_path(qemu_socket_path),
+ client_socket_path(client_socket_path) {}
+
+std::ostream &operator<<(std::ostream &out, const IVServerOptions &options) {
+ out << "\nshm_file: " << options.shm_file_path
+ << "\nqemu_socket_path: " << options.qemu_socket_path
+ << "\nclient_socket_path: " << options.client_socket_path << std::endl;
+
+ return out;
+}
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/options.h b/host/commands/ivserver/options.h
new file mode 100644
index 0000000..40b3246
--- /dev/null
+++ b/host/commands/ivserver/options.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <iostream>
+#include <string>
+
+namespace ivserver {
+
+const uint16_t kIVServerMajorVersion = 1;
+const uint16_t kIVServerMinorVersion = 0;
+const uint32_t kIVServerDefaultShmSizeInMiB = 4;
+
+//
+// structure that contains the various options to start the server.
+//
+struct IVServerOptions final {
+ IVServerOptions(const std::string &shm_file_path,
+ const std::string &qemu_socket_path,
+ const std::string &client_socket_path);
+
+ //
+ // We still need a friend here
+ //
+ friend std::ostream &operator<<(std::ostream &out,
+ const IVServerOptions &opts);
+
+ const std::string shm_file_path;
+ const std::string qemu_socket_path;
+ const std::string client_socket_path;
+};
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/qemu_client.cc b/host/commands/ivserver/qemu_client.cc
new file mode 100644
index 0000000..660c915
--- /dev/null
+++ b/host/commands/ivserver/qemu_client.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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 "host/commands/ivserver/qemu_client.h"
+
+#include <glog/logging.h>
+
+namespace ivserver {
+
+std::unique_ptr<QemuClient> QemuClient::New(const VSoCSharedMemory& shmem,
+ const cvd::SharedFD& socket) {
+ std::unique_ptr<QemuClient> res;
+ if (!socket->IsOpen()) {
+ LOG(WARNING) << "Invalid socket passed to QemuClient: "
+ << socket->StrError();
+ return res;
+ }
+
+ res.reset(new QemuClient(std::move(socket)));
+ if (!res->PerformHandshake(shmem)) {
+ LOG(ERROR) << "Qemu handshake failed. Dropping connection.";
+ res.reset();
+ }
+
+ return res;
+}
+
+QemuClient::QemuClient(cvd::SharedFD socket) : client_socket_(socket) {}
+
+// Once the QemuClient object is constructed, invoking the following
+// method will perform the actual handshake with a QEMU instance.
+bool QemuClient::PerformHandshake(const VSoCSharedMemory& shmem) {
+ LOG(INFO) << "New QEmu client connected.";
+ // 1. The protocol version number, currently zero. The client should
+ // close the connection on receipt of versions it can't handle.
+ int64_t msg = QemuConstants::kIvshMemProtocolVersion;
+ int rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+ if (rval != sizeof(msg)) {
+ LOG(ERROR) << "Failed to send protocol version: "
+ << client_socket_->StrError();
+ return false;
+ }
+
+ // 2. The client's ID. This is unique among all clients of this server.
+ // IDs must be between 0 and 65535, because the Doorbell register
+ // provides only 16 bits for them.
+ msg = QemuConstants::kGuestID;
+ rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+ if (rval != sizeof(msg)) {
+ LOG(ERROR) << "Failed to send VM Id: " << client_socket_->StrError();
+ return false;
+ }
+
+ // 3. Connect notifications for existing other clients, if any. This is
+ // a peer ID (number between 0 and 65535 other than the client's ID),
+ // repeated N times. Each repetition is accompanied by one file
+ // descriptor. These are for interrupting the peer with that ID using
+ // vector 0,..,N-1, in order. If the client is configured for fewer
+ // vectors, it closes the extra file descriptors. If it is configured
+ // for more, the extra vectors remain unconnected.
+ for (const auto& region_data : shmem.Regions()) {
+ if (!SendSocketInfo(kHostID, region_data.host_fd)) {
+ LOG(ERROR) << "Failed to send Host Side FD for region "
+ << region_data.device_name << ": " << client_socket_->StrError();
+ return false;
+ }
+ }
+
+ // 4. Interrupt setup. This is the client's own ID, repeated N times.
+ // Each repetition is accompanied by one file descriptor. These are
+ // for receiving interrupts from peers using vector 0,..,N-1, in
+ // order. If the client is configured for fewer vectors, it closes
+ // the extra file descriptors. If it is configured for more, the
+ // extra vectors remain unconnected.
+ for (const auto& region_data : shmem.Regions()) {
+ if (!SendSocketInfo(kGuestID, region_data.guest_fd)) {
+ LOG(ERROR) << "Failed to send Guest Side FD for region "
+ << region_data.device_name << ": " << client_socket_->StrError();
+ return false;
+ }
+ }
+
+ // 5. The number -1, accompanied by the file descriptor for the shared
+ // memory.
+ if (!SendSocketInfo(kSharedMem, shmem.SharedMemFD())) {
+ LOG(ERROR) << "Failed to send Shared Memory socket: "
+ << client_socket_->StrError();
+ return false;
+ }
+
+
+ LOG(INFO) << "QEmu handshake completed.";
+ return true;
+}
+
+bool QemuClient::SendSocketInfo(QemuConstants message,
+ const cvd::SharedFD& socket) {
+ struct iovec vec {
+ &message, sizeof(message)
+ };
+ cvd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+ cvd::SharedFD fds[] = {socket};
+ int rval = client_socket_->SendMsgAndFDs(hdr, 0, fds);
+ if (rval == -1) {
+ LOG(ERROR) << "failed to send shared_mem_fd: "
+ << client_socket_->StrError();
+ return false;
+ }
+ return true;
+}
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/qemu_client.h b/host/commands/ivserver/qemu_client.h
new file mode 100644
index 0000000..31cd211
--- /dev/null
+++ b/host/commands/ivserver/qemu_client.h
@@ -0,0 +1,69 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <memory>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+
+// QemuClient manages individual QEmu connections using protocol specified
+// in documentation file distributed as part of QEmu 2.8 package under:
+// docs/specs/ivshmem-spec.txt
+// Alternatively, please point your browser to the following URL:
+// https://github.com/qemu/qemu/blob/stable-2.8/docs/specs/ivshmem-spec.txt
+class QemuClient final {
+ public:
+ static std::unique_ptr<QemuClient> New(const VSoCSharedMemory &shmem,
+ const cvd::SharedFD &connection);
+
+ cvd::SharedFD client_socket() const { return client_socket_; }
+
+ private:
+ enum QemuConstants : int64_t {
+ kIvshMemProtocolVersion = 0,
+ // Marker for the shared memory file
+ kSharedMem = -1,
+ // HostID is in fact a Peer ID and can take multiple values, depending on
+ // how many subsystems we would like Guest to talk to.
+ kHostID = 0,
+ // GuestID is a unique form of Peer ID (see above), that identifies newly
+ // created quest in IvSharedMem world.
+ kGuestID = 1
+ };
+
+ static_assert(QemuConstants::kHostID != QemuConstants::kGuestID,
+ "Guest and host should have different IDs");
+
+ cvd::SharedFD client_socket_;
+
+ // Initialize new instance of QemuClient.
+ QemuClient(cvd::SharedFD qemu_listener_socket);
+
+ // Once the QemuClient object is constructed, invoking the following
+ // method will perform the actual handshake with a QEMU instance.
+ bool PerformHandshake(const VSoCSharedMemory &shmem_fd);
+
+ // Send socket data to Qemu.
+ bool SendSocketInfo(QemuConstants message, const cvd::SharedFD &socket);
+
+ QemuClient(const QemuClient &) = delete;
+ QemuClient &operator=(const QemuClient &) = delete;
+};
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/shm_layout.txt b/host/commands/ivserver/shm_layout.txt
new file mode 100644
index 0000000..39d66a7
--- /dev/null
+++ b/host/commands/ivserver/shm_layout.txt
@@ -0,0 +1,88 @@
+(Please use a smaller font and/or appropriate terminal width to see this line without wrapping)
+
+
+ REGION DESCRIPTORS
+ (starts at page 0)
+ +------------------------------+
+ | major_version: u16 |
+ +------------------------------+
+ | minor_version: u16 |
+ +------------------------------+
+ (size of shared memory) | size: u32 |
+ +------------------------------+
+ (number of regions) | region_count: u32 |
+ +------------------------------+
+ (offset of region descriptor data) | vsoc_region_desc_offset: u32 |o-----+
+ +------------------------------+ |
+ | | |
+ | .. .. .. | |
+ | | |
+ (region 0 descriptor start) +------------------------------+ <----+
+ | current_version: u16 |
+ +------------------------------+
+ | min_compatible_version: u16 |
+ +------------------------------+
+ (start offset of region 0) | region_begin_offset: u32 |o--------------------+
+ +------------------------------+ |
+ (end offset of region 0) | region_end_offset: u32 |o----------------------------------------------------------------------+
+ +------------------------------+ | |
+ | offset_of_region_data: u32 |o-----+ | |
+ +------------------------------+ | | |
+ | guest_to_host_signal_table | | | |
+ |______________________________| | | |
+ +----------------------------+ | | |
+ | num_nodes_lg2: u32 | | | |
+ +----------------------------+ | | |
+ |futex_uaddr_table_offset:u32|o------------------+ | |
+ +----------------------------+ | | | |
+ | intr_signaled_offset: u32 |o----------------+ | | |
+ +----------------------------+ | | | | |
+ +------------------------------+ | | | | |
+ | host_to_guest_signal_table | | | | | |
+ |______________________________| | | | | |
+ +----------------------------+ | | | | |
+ | num_nodes_lg2: u32 | | | | | |
+ +----------------------------+ | | | | |
+ |futex_uaddr_table_offset:u32|o--------------+ | | | |
+ +----------------------------+ | | | | \/ REGION AREA (page aligned) |
+ | intr_signaled_offset: u32 |o------|-----+ | | | +---------------------------------------+ |
+ +----------------------------+ | | | | | | region area before start of guest to | |
+ +------------------------------+ | | | | | | host signal table. | |
+ | device_name: char[16] | | | | | | | (currently does not exist) | |
+ (region 0 descriptor end) +------------------------------+ | | | | +->+---------------------------------------+ |
+ (region 1 descriptor start) | | | | | | | uaddr_offset_0 : u32 | |
+ | current_version: u16 | | | | | +---------------------------------------+ |
+ +------------------------------+ | | | | | uaddr_offset_1 : u32 | |
+ | min_compatible_version: u16 | | | | | +---------------------------------------+ |
+ +------------------------------+ | | | | | ... .. ... | |
+ | region_begin_offset: u32 | | | | | +---------------------------------------+ |
+ +------------------------------+ | | | | | uaddr_offset_(2^num_nodes_lg2 - 1):u32| |
+ | region_end_offset: u32 | | | | +--->+---------------------------------------+ |
+ +------------------------------+ | | | | (interrupt_signaled_area) : u32 | |
+ | offset_of_region_data: u32 | | | +----->+---------------------------------------+ |
+ +------------------------------+ | | | uaddr_offset_0 : u32 | |
+ | guest_to_host_signal_table | | | +---------------------------------------+ |
+ +------------------------------+ | | | ... .. ... | |
+ | host_to_guest_signal_table | | | +---------------------------------------+ |
+ +------------------------------+ | | | uaddr_offset_(2^num_nodes_lg2 - 1):u32| |
+ | device_name: char[16] | | +------->+---------------------------------------+ |
+ (region 1 descriptor end) +------------------------------+ | | (interrupt_signaled_area) : u32 | |
+ ... ... ... +------------->+---------------------------------------+ |
+ +------------------------------+ | | |
+ | current_version: u16 | | | |
+ +------------------------------+ | | |
+ | min_compatible_version: u16 | | region specific data | |
+ +------------------------------+ | (defined by region specific agreement | |
+ | region_begin_offset: u32 | | between HAL and host-side process) | |
+ +------------------------------+ | | |
+ | region_end_offset: u32 | | | |
+ +------------------------------+ | | |
+ | offset_of_region_data: u32 | +---------------------------------------+ <-------+
+ +------------------------------+
+ | guest_to_host_signal_table |
+ +------------------------------+
+ | host_to_guest_signal_table |
+ +------------------------------+
+ | device_name: char[16] |
+ +------------------------------+
+ END OF REGION DESCRIPTORS
diff --git a/host/commands/ivserver/vsocsharedmem.cc b/host/commands/ivserver/vsocsharedmem.cc
new file mode 100644
index 0000000..eb448d8
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 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 "host/commands/ivserver/vsocsharedmem.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <algorithm>
+#include <tuple>
+
+#include <glog/logging.h>
+
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+namespace {
+
+class VSoCSharedMemoryImpl : public VSoCSharedMemory {
+ public:
+ VSoCSharedMemoryImpl(const std::map<std::string, size_t> &name_to_region_idx,
+ const std::vector<Region> ®ions,
+ const std::string &path);
+
+ bool GetEventFdPairForRegion(const std::string ®ion_name,
+ cvd::SharedFD *guest_to_host,
+ cvd::SharedFD *host_to_guest) const override;
+
+ const cvd::SharedFD &SharedMemFD() const override;
+
+ const std::vector<Region> &Regions() const override;
+
+ private:
+ void CreateLayout();
+
+ cvd::SharedFD shared_mem_fd_;
+ const std::map<std::string, size_t> region_name_to_index_;
+ const std::vector<Region> region_data_;
+
+ VSoCSharedMemoryImpl(const VSoCSharedMemoryImpl &) = delete;
+ VSoCSharedMemoryImpl &operator=(const VSoCSharedMemoryImpl &other) = delete;
+};
+
+VSoCSharedMemoryImpl::VSoCSharedMemoryImpl(
+ const std::map<std::string, size_t> &name_to_region_idx,
+ const std::vector<Region> ®ions, const std::string &path)
+ : shared_mem_fd_(cvd::SharedFD::Open(path.c_str(), O_RDWR)),
+ region_name_to_index_{name_to_region_idx},
+ region_data_{regions} {
+ LOG_IF(FATAL, !shared_mem_fd_->IsOpen())
+ << "Error in creating shared_memory file: " << shared_mem_fd_->StrError();
+}
+
+const cvd::SharedFD &VSoCSharedMemoryImpl::SharedMemFD() const {
+ return shared_mem_fd_;
+}
+
+const std::vector<VSoCSharedMemory::Region> &VSoCSharedMemoryImpl::Regions()
+ const {
+ return region_data_;
+}
+
+bool VSoCSharedMemoryImpl::GetEventFdPairForRegion(
+ const std::string ®ion_name, cvd::SharedFD *guest_to_host,
+ cvd::SharedFD *host_to_guest) const {
+ auto it = region_name_to_index_.find(region_name);
+ if (it == region_name_to_index_.end()) return false;
+
+ *guest_to_host = region_data_[it->second].host_fd;
+ *host_to_guest = region_data_[it->second].guest_fd;
+ return true;
+}
+
+} // anonymous namespace
+
+std::unique_ptr<VSoCSharedMemory> VSoCSharedMemory::New(
+ const std::string &path) {
+ auto device_layout = vsoc::VSoCMemoryLayout::Get();
+
+ std::map<std::string, size_t> name_to_region_idx;
+ std::vector<Region> regions;
+ regions.reserve(device_layout->GetRegions().size());
+
+ for (auto region_spec : device_layout->GetRegions()) {
+ auto device_name = region_spec->region_name();
+
+ // Create one pair of eventfds for this region. Note that the guest to host
+ // eventfd is non-blocking, whereas the host to guest eventfd is blocking.
+ // This is in anticipation of blocking semantics for the host side locks.
+ auto host_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+ if (!host_fd->IsOpen()) {
+ LOG(ERROR) << "Failed to create host eventfd for " << device_name << ": "
+ << host_fd->StrError();
+ return nullptr;
+ }
+ auto guest_fd = cvd::SharedFD::Event(0, EFD_NONBLOCK);
+ if (!guest_fd->IsOpen()) {
+ LOG(ERROR) << "Failed to create guest eventfd for " << device_name << ": "
+ << guest_fd->StrError();
+ return nullptr;
+ }
+
+ auto region_idx = regions.size();
+ name_to_region_idx[device_name] = region_idx;
+ regions.emplace_back(device_name, host_fd, guest_fd);
+ }
+
+ return std::unique_ptr<VSoCSharedMemory>(
+ new VSoCSharedMemoryImpl(name_to_region_idx, regions, path));
+}
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/vsocsharedmem.h b/host/commands/ivserver/vsocsharedmem.h
new file mode 100644
index 0000000..613c6a0
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem.h
@@ -0,0 +1,59 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <inttypes.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "uapi/vsoc_shm.h"
+
+namespace ivserver {
+
+class VSoCSharedMemory {
+ public:
+ // Region describes a VSoCSharedMem region.
+ struct Region {
+ Region() = default;
+ explicit Region(const char *device_name, const cvd::SharedFD &host_fd,
+ const cvd::SharedFD &guest_fd)
+ : device_name(device_name), host_fd(host_fd), guest_fd(guest_fd) {}
+ const char *device_name;
+ cvd::SharedFD host_fd;
+ cvd::SharedFD guest_fd;
+ };
+
+ VSoCSharedMemory() = default;
+ virtual ~VSoCSharedMemory() = default;
+
+ static std::unique_ptr<VSoCSharedMemory> New(const std::string &name);
+
+ virtual bool GetEventFdPairForRegion(const std::string ®ion_name,
+ cvd::SharedFD *guest_to_host,
+ cvd::SharedFD *host_to_guest) const = 0;
+
+ virtual const cvd::SharedFD &SharedMemFD() const = 0;
+ virtual const std::vector<Region> &Regions() const = 0;
+
+ private:
+ VSoCSharedMemory(const VSoCSharedMemory &) = delete;
+ VSoCSharedMemory &operator=(const VSoCSharedMemory &other) = delete;
+};
+
+} // namespace ivserver
diff --git a/host/commands/ivserver/vsocsharedmem_mock.h b/host/commands/ivserver/vsocsharedmem_mock.h
new file mode 100644
index 0000000..4ed1f16
--- /dev/null
+++ b/host/commands/ivserver/vsocsharedmem_mock.h
@@ -0,0 +1,33 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 <gmock/gmock.h>
+
+#include "host/commands/ivserver/vsocsharedmem.h"
+
+namespace ivserver {
+namespace test {
+class VSoCSharedMemoryMock : public VSoCSharedMemory {
+ public:
+ MOCK_CONST_METHOD3(GetEventFdPairForRegion,
+ bool(const std::string&, cvd::SharedFD*, cvd::SharedFD*));
+ MOCK_CONST_METHOD0(SharedMemFD, const cvd::SharedFD&());
+ MOCK_CONST_METHOD0(Regions,
+ const std::vector<VSoCSharedMemory::Region>&());
+};
+} // namespace test
+} // namespace ivserver
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index 6dd6426..e2db615 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -24,6 +24,7 @@
"cuttlefish_glog",
],
shared_libs: [
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
@@ -33,6 +34,7 @@
static_libs: [
"libcuttlefish_host_config",
"libcuttlefish_vm_manager",
+ "libivserver",
"libgflags",
"libxml2",
"libjsoncpp",
diff --git a/host/commands/record_audio/Android.bp b/host/commands/record_audio/Android.bp
new file mode 100644
index 0000000..13f47ef
--- /dev/null
+++ b/host/commands/record_audio/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary_host {
+ name: "record_audio",
+ srcs: [
+ "main.cc",
+ ],
+ include_dirs: [
+ "frameworks/av/cmds/stagefright",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcuttlefish_utils",
+ "vsoc_lib",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ "libgflags",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/record_audio/main.cc b/host/commands/record_audio/main.cc
new file mode 100644
index 0000000..3c024b8
--- /dev/null
+++ b/host/commands/record_audio/main.cc
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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 "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+#include "WaveWriter.h"
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+#include <iostream>
+#include <signal.h>
+
+using AudioDataRegionView = vsoc::audio_data::AudioDataRegionView;
+using WaveWriter = android::WaveWriter;
+
+DEFINE_string(output_file, "", "Location of the output audio file.");
+DEFINE_bool(verbose, false, "Enable verbose logging.");
+
+volatile bool gDone = false;
+static void SigIntHandler(int /* sig */) {
+ gDone = true;
+}
+
+int main(int argc, char **argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ LOG_IF(FATAL, FLAGS_output_file.empty())
+ << "--output_file must be specified.";
+
+ AudioDataRegionView *audio_data_rv =
+ AudioDataRegionView::GetInstance(vsoc::GetDomain().c_str());
+
+ auto worker = audio_data_rv->StartWorker();
+
+ std::unique_ptr<WaveWriter> writer;
+ int64_t frameCount = 0LL;
+
+ // The configuration the writer is setup for.
+ gce_audio_message writer_hdr;
+
+ uint8_t buffer[4096];
+
+ gDone = false;
+
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SigIntHandler;
+
+ struct sigaction oact;
+ sigaction(SIGINT, &act, &oact);
+
+ while (!gDone) {
+ intptr_t res = audio_data_rv->data()->audio_queue.Read(
+ audio_data_rv,
+ reinterpret_cast<char *>(buffer),
+ sizeof(buffer));
+
+ if (res < 0) {
+ std::cerr << "CircularPacketQueue::Read returned " << res << std::endl;
+ continue;
+ }
+
+ CHECK_GE(static_cast<size_t>(res), sizeof(gce_audio_message));
+
+ gce_audio_message hdr;
+ std::memcpy(&hdr, buffer, sizeof(gce_audio_message));
+
+ if (hdr.message_type != gce_audio_message::DATA_SAMPLES) {
+ continue;
+ }
+
+ const size_t payloadSize = res - sizeof(gce_audio_message);
+
+ if (FLAGS_verbose) {
+ std::cerr
+ << "stream "
+ << hdr.stream_number
+ << ", frame "
+ << hdr.frame_num
+ << ", rate "
+ << hdr.frame_rate
+ << ", channel_mask "
+ << hdr.channel_mask
+ << ", format "
+ << hdr.format
+ << ", payload_size "
+ << payloadSize
+ << std::endl;
+ }
+
+ if (!writer) {
+ const size_t numChannels = hdr.frame_size / sizeof(int16_t);
+
+ writer.reset(
+ new WaveWriter(FLAGS_output_file.c_str(), numChannels, hdr.frame_rate));
+
+ frameCount = hdr.frame_num;
+ writer_hdr = hdr;
+ } else if (writer_hdr.frame_size != hdr.frame_size
+ || writer_hdr.frame_rate != hdr.frame_rate
+ || writer_hdr.stream_number != hdr.stream_number) {
+ std::cerr << "Audio configuration changed. Aborting." << std::endl;
+ break;
+ }
+
+ int64_t framesMissing = hdr.frame_num - frameCount;
+ if (framesMissing > 0) {
+ // TODO(andih): Insert silence here, if necessary.
+ }
+
+ frameCount = hdr.frame_num;
+
+ writer->Append(&buffer[sizeof(gce_audio_message)], payloadSize);
+ }
+
+ std::cout << "DONE" << std::endl;
+
+ return 0;
+}
+
diff --git a/host/commands/run_cvd/Android.bp b/host/commands/run_cvd/Android.bp
index a8621e9..29238c9 100644
--- a/host/commands/run_cvd/Android.bp
+++ b/host/commands/run_cvd/Android.bp
@@ -19,11 +19,14 @@
"launch.cc",
"process_monitor.cc",
"main.cc",
+ "screen_region_handler.cc",
+ "vsoc_shared_memory.cc",
],
header_libs: [
"cuttlefish_glog",
],
shared_libs: [
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
@@ -33,6 +36,7 @@
static_libs: [
"libcuttlefish_host_config",
"libcuttlefish_vm_manager",
+ "libivserver",
"libgflags",
"libxml2",
"libjsoncpp",
diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc
index e4eb858..5c42e59 100644
--- a/host/commands/run_cvd/launch.cc
+++ b/host/commands/run_cvd/launch.cc
@@ -8,8 +8,10 @@
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/shm/screen_layout.h"
#include "host/commands/run_cvd/runner_defs.h"
#include "host/commands/run_cvd/pre_launch_initializers.h"
+#include "host/commands/run_cvd/vsoc_shared_memory.h"
#include "host/libs/vm_manager/crosvm_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
@@ -18,6 +20,20 @@
namespace {
+cvd::SharedFD CreateIvServerUnixSocket(const std::string& path) {
+ return cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM,
+ 0666);
+}
+
+std::string GetGuestPortArg() {
+ constexpr int kEmulatorPort = 5555;
+ return std::string{"--guest_ports="} + std::to_string(kEmulatorPort);
+}
+
+std::string GetHostPortArg(const vsoc::CuttlefishConfig& config) {
+ return std::string{"--host_ports="} + std::to_string(config.host_port());
+}
+
std::string GetAdbConnectorTcpArg(const vsoc::CuttlefishConfig& config) {
return std::string{"127.0.0.1:"} + std::to_string(config.host_port());
}
@@ -32,6 +48,10 @@
return config.adb_mode().count(mode) > 0;
}
+bool AdbTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+ return AdbModeEnabled(config, vsoc::AdbMode::Tunnel);
+}
+
bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
return config.vsock_guest_cid() > 2
&& AdbModeEnabled(config, vsoc::AdbMode::VsockTunnel);
@@ -43,9 +63,11 @@
}
bool AdbTcpConnectorEnabled(const vsoc::CuttlefishConfig& config) {
+ bool tunnel = AdbTunnelEnabled(config);
bool vsock_tunnel = AdbVsockTunnelEnabled(config);
bool vsock_half_tunnel = AdbVsockHalfTunnelEnabled(config);
- return config.run_adb_connector() && (vsock_tunnel || vsock_half_tunnel);
+ return config.run_adb_connector()
+ && (tunnel || vsock_tunnel || vsock_half_tunnel);
}
bool AdbVsockConnectorEnabled(const vsoc::CuttlefishConfig& config) {
@@ -71,6 +93,32 @@
return config.logcat_mode() == cvd::kLogcatVsockMode;
}
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config) {
+ // Resize screen region
+ auto actual_width = cvd::AlignToPowerOf2(config.x_res() * 4, 4);// align to 16
+ uint32_t screen_buffers_size =
+ config.num_screen_buffers() *
+ cvd::AlignToPageSize(actual_width * config.y_res() + 16 /* padding */);
+ screen_buffers_size +=
+ (config.num_screen_buffers() - 1) * 4096; /* Guard pages */
+
+ // TODO(b/79170615) Resize gralloc region too.
+
+ vsoc::CreateSharedMemoryFile(
+ config.mempath(),
+ {{vsoc::layout::screen::ScreenLayout::region_name, screen_buffers_size}});
+
+
+ cvd::Command ivserver(config.ivserver_binary());
+ ivserver.AddParameter(
+ "-qemu_socket_fd=",
+ CreateIvServerUnixSocket(config.ivshmem_qemu_socket_path()));
+ ivserver.AddParameter(
+ "-client_socket_fd=",
+ CreateIvServerUnixSocket(config.ivshmem_client_socket_path()));
+ return ivserver;
+}
+
std::vector<cvd::SharedFD> LaunchKernelLogMonitor(
const vsoc::CuttlefishConfig& config,
cvd::ProcessMonitor* process_monitor,
@@ -266,6 +314,17 @@
return false;
}
+void LaunchStreamAudioIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor,
+ std::function<bool(MonitorEntry*)> callback) {
+ if (config.enable_stream_audio()) {
+ auto port_options = "-port=" + std::to_string(config.stream_audio_port());
+ cvd::Command stream_audio(config.stream_audio_binary());
+ stream_audio.AddParameter(port_options);
+ process_monitor->StartSubprocess(std::move(stream_audio), callback);
+ }
+}
+
void LaunchAdbConnectorIfEnabled(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config,
cvd::SharedFD adbd_events_pipe) {
@@ -292,6 +351,17 @@
}
}
+void LaunchSocketForwardProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config) {
+ if (AdbTunnelEnabled(config)) {
+ cvd::Command adb_tunnel(config.socket_forward_proxy_binary());
+ adb_tunnel.AddParameter(GetGuestPortArg());
+ adb_tunnel.AddParameter(GetHostPortArg(config));
+ process_monitor->StartSubprocess(std::move(adb_tunnel),
+ GetOnSubprocessExitCallback(config));
+ }
+}
+
void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config) {
if (AdbVsockTunnelEnabled(config)) {
@@ -315,3 +385,14 @@
GetOnSubprocessExitCallback(config));
}
}
+
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config) {
+ if (config.enable_ivserver()) {
+ process_monitor->StartSubprocess(GetIvServerCommand(config),
+ GetOnSubprocessExitCallback(config));
+
+ // Initialize the regions that require so before the VM starts.
+ PreLaunchInitializers::Initialize(config);
+ }
+}
diff --git a/host/commands/run_cvd/launch.h b/host/commands/run_cvd/launch.h
index 4db6e12..714b5bd 100644
--- a/host/commands/run_cvd/launch.h
+++ b/host/commands/run_cvd/launch.h
@@ -6,6 +6,7 @@
#include "host/commands/run_cvd/process_monitor.h"
#include "host/libs/config/cuttlefish_config.h"
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config);
std::vector <cvd::SharedFD> LaunchKernelLogMonitor(
const vsoc::CuttlefishConfig& config,
cvd::ProcessMonitor* process_monitor,
@@ -19,10 +20,17 @@
bool LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
cvd::ProcessMonitor* process_monitor,
std::function<bool(cvd::MonitorEntry*)> callback);
+void LaunchStreamAudioIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor,
+ std::function<bool(cvd::MonitorEntry*)> callback);
void LaunchAdbConnectorIfEnabled(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config,
cvd::SharedFD adbd_events_pipe);
+void LaunchSocketForwardProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config);
void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
const vsoc::CuttlefishConfig& config);
+void LaunchIvServerIfEnabled(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config);
void LaunchTombstoneReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
cvd::ProcessMonitor* process_monitor);
diff --git a/host/commands/run_cvd/main.cc b/host/commands/run_cvd/main.cc
index a4ed8cc..dd5ed4d 100644
--- a/host/commands/run_cvd/main.cc
+++ b/host/commands/run_cvd/main.cc
@@ -46,9 +46,12 @@
#include "common/libs/utils/files.h"
#include "common/libs/utils/subprocess.h"
#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "common/vsoc/shm/screen_layout.h"
#include "host/commands/run_cvd/launch.h"
#include "host/commands/run_cvd/runner_defs.h"
#include "host/commands/run_cvd/process_monitor.h"
+#include "host/commands/run_cvd/vsoc_shared_memory.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/commands/kernel_log_monitor/kernel_log_server.h"
#include <host/libs/vm_manager/crosvm_manager.h>
@@ -452,6 +455,8 @@
LaunchTombstoneReceiverIfEnabled(*config, &process_monitor);
LaunchUsbServerIfEnabled(*config, &process_monitor);
+
+ LaunchIvServerIfEnabled(&process_monitor, *config);
// Launch the e2e tests after the ivserver is ready
LaunchE2eTestIfEnabled(&process_monitor, boot_state_machine, *config);
@@ -469,7 +474,10 @@
}
// Start other host processes
+ LaunchSocketForwardProxyIfEnabled(&process_monitor, *config);
LaunchSocketVsockProxyIfEnabled(&process_monitor, *config);
+ LaunchStreamAudioIfEnabled(*config, &process_monitor,
+ GetOnSubprocessExitCallback(*config));
LaunchAdbConnectorIfEnabled(&process_monitor, *config, adbd_events_pipe);
ServerLoop(launcher_monitor_socket, &process_monitor); // Should not return
diff --git a/host/commands/run_cvd/screen_region_handler.cc b/host/commands/run_cvd/screen_region_handler.cc
new file mode 100644
index 0000000..9be969e
--- /dev/null
+++ b/host/commands/run_cvd/screen_region_handler.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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 <glog/logging.h>
+
+#include "common/vsoc/lib/screen_region_view.h"
+#include "host/commands/run_cvd/pre_launch_initializers.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+void InitializeScreenRegion(const vsoc::CuttlefishConfig& config) {
+ auto region =
+ vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+ if (!region) {
+ LOG(FATAL) << "Screen region was not found";
+ return;
+ }
+ auto dest = region->data();
+ dest->x_res = config.x_res();
+ dest->y_res = config.y_res();
+ dest->dpi = config.dpi();
+ dest->refresh_rate_hz = config.refresh_rate_hz();
+}
diff --git a/host/commands/run_cvd/vsoc_shared_memory.cc b/host/commands/run_cvd/vsoc_shared_memory.cc
new file mode 100644
index 0000000..0278cad
--- /dev/null
+++ b/host/commands/run_cvd/vsoc_shared_memory.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2018 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 "host/commands/run_cvd/vsoc_shared_memory.h"
+
+#include <unistd.h>
+
+#include <map>
+#include <vector>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "glog/logging.h"
+
+#include "uapi/vsoc_shm.h"
+
+namespace vsoc {
+
+namespace {
+
+uint32_t OffsetOfRegionData(const VSoCRegionLayout& layout) {
+ uint32_t offset = 0;
+ // Signal tables
+ offset +=
+ (1 << layout.guest_to_host_signal_table_log_size()) * sizeof(uint32_t);
+ offset +=
+ (1 << layout.host_to_guest_signal_table_log_size()) * sizeof(uint32_t);
+ // Interrup signals
+ offset += 2 * sizeof(uint32_t);
+ return offset;
+}
+
+struct VSoCRegionAllocator {
+ const VSoCRegionLayout* region_layout;
+ uint32_t begin_offset;
+ uint32_t region_size;
+
+ VSoCRegionAllocator(const VSoCRegionLayout& layout, uint32_t offset,
+ uint32_t requested_layout_increase = 0)
+ : region_layout(&layout),
+ begin_offset(offset),
+ region_size(cvd::AlignToPageSize(OffsetOfRegionData(layout) +
+ layout.layout_size() +
+ requested_layout_increase)) {}
+};
+
+// Writes a region's signal table layout to shared memory. Returns the region
+// offset of free memory after the table and interrupt signaled word.
+uint32_t WriteSignalTableDescription(vsoc_signal_table_layout* layout,
+ uint32_t offset, int log_size) {
+ layout->num_nodes_lg2 = log_size;
+ // First the signal table
+ layout->futex_uaddr_table_offset = offset;
+ offset += (1 << log_size) * sizeof(uint32_t);
+ // Then the interrupt signaled word
+ layout->interrupt_signalled_offset = offset;
+ offset += sizeof(uint32_t);
+ return offset;
+}
+
+// Writes a region's layout description to shared memory
+void WriteRegionDescription(vsoc_device_region* shmem_region_desc,
+ const VSoCRegionAllocator& allocator) {
+ // Region versions are deprecated, write some sensible value
+ shmem_region_desc->current_version = 0;
+ shmem_region_desc->min_compatible_version = 0;
+
+ shmem_region_desc->region_begin_offset = allocator.begin_offset;
+ shmem_region_desc->region_end_offset =
+ allocator.begin_offset + allocator.region_size;
+ shmem_region_desc->offset_of_region_data =
+ OffsetOfRegionData(*allocator.region_layout);
+ strncpy(shmem_region_desc->device_name,
+ allocator.region_layout->region_name(), VSOC_DEVICE_NAME_SZ - 1);
+ shmem_region_desc->device_name[VSOC_DEVICE_NAME_SZ - 1] = '\0';
+ // Guest to host signal table at the beginning of the region
+ uint32_t offset = 0;
+ offset = WriteSignalTableDescription(
+ &shmem_region_desc->guest_to_host_signal_table, offset,
+ allocator.region_layout->guest_to_host_signal_table_log_size());
+ // Host to guest signal table right after
+ offset = WriteSignalTableDescription(
+ &shmem_region_desc->host_to_guest_signal_table, offset,
+ allocator.region_layout->host_to_guest_signal_table_log_size());
+ // Double check that the region metadata does not collide with the data
+ if (offset > shmem_region_desc->offset_of_region_data) {
+ LOG(FATAL) << "Error: Offset of region data too small (is "
+ << shmem_region_desc->offset_of_region_data << " should be "
+ << offset << " ) for region "
+ << allocator.region_layout->region_name() << ". This is a bug";
+ }
+}
+
+void WriteLayout(void* shared_memory,
+ const std::vector<VSoCRegionAllocator>& allocators,
+ uint32_t file_size) {
+ // Device header
+ static_assert(CURRENT_VSOC_LAYOUT_MAJOR_VERSION == 2,
+ "Region layout code must be updated");
+ auto header = reinterpret_cast<vsoc_shm_layout_descriptor*>(shared_memory);
+ header->major_version = CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+ header->minor_version = CURRENT_VSOC_LAYOUT_MINOR_VERSION;
+ header->size = file_size;
+ header->region_count = allocators.size();
+
+ std::map<std::string, size_t> region_idx_by_name;
+ for (size_t idx = 0; idx < allocators.size(); ++idx) {
+ region_idx_by_name[allocators[idx].region_layout->region_name()] = idx;
+ }
+
+ // Region descriptions go right after the layout descriptor
+ header->vsoc_region_desc_offset = sizeof(vsoc_shm_layout_descriptor);
+ auto region_descriptions = reinterpret_cast<vsoc_device_region*>(header + 1);
+ for (size_t idx = 0; idx < allocators.size(); ++idx) {
+ auto shmem_region_desc = ®ion_descriptions[idx];
+ const auto& region = *allocators[idx].region_layout;
+ WriteRegionDescription(shmem_region_desc, allocators[idx]);
+ // Handle managed_by links
+ if (region.managed_by()) {
+ auto manager_idx = region_idx_by_name.at(region.managed_by());
+ if (manager_idx == VSOC_REGION_WHOLE) {
+ LOG(FATAL) << "Region '" << region.region_name() << "' has owner "
+ << region.managed_by() << " with index " << manager_idx
+ << " which is the default value for regions without an "
+ "owner. Choose a different region to be at index "
+ << manager_idx
+ << ", make sure the chosen region is NOT the owner of any "
+ "other region";
+ }
+ shmem_region_desc->managed_by = manager_idx;
+ } else {
+ shmem_region_desc->managed_by = VSOC_REGION_WHOLE;
+ }
+ }
+}
+} // namespace
+
+void CreateSharedMemoryFile(
+ const std::string& path,
+ const std::map<std::string, uint32_t>& layout_increases) {
+ // TODO(ender): Lock the file after creation and check lock status upon second
+ // execution attempt instead of throwing an error.
+ LOG_IF(WARNING, unlink(path.c_str()) == 0)
+ << "Removed existing instance of " << path
+ << ". We currently don't know if another instance of daemon is running";
+ auto shared_mem_fd = cvd::SharedFD::Open(
+ path.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ LOG_IF(FATAL, !shared_mem_fd->IsOpen())
+ << "Error in creating shared_memory file: " << shared_mem_fd->StrError();
+
+ auto region_layouts = VSoCMemoryLayout::Get()->GetRegions();
+ std::vector<VSoCRegionAllocator> allocators;
+ uint32_t file_size =
+ cvd::AlignToPageSize(sizeof(vsoc_shm_layout_descriptor) +
+ region_layouts.size() * sizeof(vsoc_device_region));
+ for (auto layout : region_layouts) {
+ allocators.emplace_back(*layout, file_size /* offset */,
+ layout_increases.count(layout->region_name())
+ ? layout_increases.at(layout->region_name())
+ : 0);
+ file_size += allocators.back().region_size;
+ }
+ file_size = cvd::RoundUpToNextPowerOf2(file_size);
+
+ int truncate_res = shared_mem_fd->Truncate(file_size);
+ LOG_IF(FATAL, truncate_res == -1)
+ << "Error in sizing up the shared memory file: "
+ << shared_mem_fd->StrError();
+
+ void* mmap_addr =
+ shared_mem_fd->Mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, 0);
+ LOG_IF(FATAL, mmap_addr == MAP_FAILED)
+ << "Error mmaping file: " << strerror(errno);
+ WriteLayout(mmap_addr, allocators, file_size);
+ munmap(mmap_addr, file_size);
+}
+
+} // namespace vsoc
diff --git a/host/commands/run_cvd/vsoc_shared_memory.h b/host/commands/run_cvd/vsoc_shared_memory.h
new file mode 100644
index 0000000..94c03a1
--- /dev/null
+++ b/host/commands/run_cvd/vsoc_shared_memory.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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 <map>
+#include <string>
+
+namespace vsoc {
+
+// Creates the shared memory file and initializes it with with the region
+// information. The size of each region is determined to be the smallest
+// multiple of page size that can accommodate the header and the layout. The
+// size of the layout can be increased by specifyng it on the
+// layout_increases_by_region_name parameter.
+void CreateSharedMemoryFile(
+ const std::string& path,
+ const std::map<std::string, uint32_t>& layout_increases_by_region_name = {});
+
+} // namespace vsoc
diff --git a/host/commands/virtual_usb_manager/Android.bp b/host/commands/virtual_usb_manager/Android.bp
index 40c8c74..9c86b70 100644
--- a/host/commands/virtual_usb_manager/Android.bp
+++ b/host/commands/virtual_usb_manager/Android.bp
@@ -34,6 +34,7 @@
"cuttlefish_glog",
],
shared_libs: [
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
diff --git a/host/frontend/Android.bp b/host/frontend/Android.bp
index 8d78a9f..5a053e7 100644
--- a/host/frontend/Android.bp
+++ b/host/frontend/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
subdirs = [
+ "stream_audio",
"vnc_server",
"adb_connector",
]
diff --git a/host/frontend/stream_audio/Android.bp b/host/frontend/stream_audio/Android.bp
new file mode 100644
index 0000000..8f07536
--- /dev/null
+++ b/host/frontend/stream_audio/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2018 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.
+
+cc_binary_host {
+ name: "stream_audio",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "cuttlefish_tcp_socket",
+ "libbase",
+ "libcuttlefish_utils",
+ "libcuttlefish_fs",
+ "libopus",
+ "vsoc_lib",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ "libgflags",
+ "libopuscpp",
+ ],
+ cpp_std: "c++17",
+ enabled: false,
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/main.cpp b/host/frontend/stream_audio/main.cpp
new file mode 100644
index 0000000..f63e315
--- /dev/null
+++ b/host/frontend/stream_audio/main.cpp
@@ -0,0 +1,270 @@
+/*
+ *
+ * Copyright (C) 2018 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.
+ */
+
+// For each client that connects initially a header is sent with the following,
+// in this order, all as uint16_t in network-byte-order:
+// number of channels, frame rate
+//
+// Following, audio packets are sent as a uint32_t length (network byte order)
+// indicating the number of bytes
+// followed by the (opus) frame_size as a uint32_t
+// followed by <length> bytes.
+
+#include "common/libs/tcp_socket/tcp_socket.h"
+#include "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+
+#include <arpa/inet.h>
+
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+using vsoc::audio_data::AudioDataRegionView;
+
+DEFINE_int32(port, 0, "port on which to serve audio.");
+
+namespace {
+
+// Read audio frames from the AudioDataRegionView
+class AudioStreamer {
+ public:
+ cvd::Message MakeAudioDescriptionHeader() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+
+ const size_t num_channels = header_.frame_size / sizeof(opus_int16);
+ return cvd::CreateMessage(static_cast<std::uint16_t>(num_channels),
+ static_cast<std::uint16_t>(header_.frame_rate));
+ }
+
+ std::uint32_t frame_rate() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+ return header_.frame_rate;
+ }
+
+ std::uint32_t num_channels() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+ return header_.frame_size / sizeof(opus_int16);
+ }
+
+ // Returns the frame id and audio frame
+ std::tuple<std::int64_t, std::shared_ptr<const cvd::Message>> audio_buffer(
+ std::int64_t previous_frame_num) const {
+ std::unique_lock guard(buffer_lock_);
+ while (header_.frame_num <= previous_frame_num) {
+ buffer_cv_.wait(guard);
+ }
+
+ return {header_.frame_num, audio_buffer_};
+ }
+
+ void Update() {
+ auto audio_data_rv =
+ AudioDataRegionView::GetInstance(vsoc::GetDomain().c_str());
+ auto worker = audio_data_rv->StartWorker();
+ std::vector<char> new_buffer;
+
+ while (true) {
+ new_buffer.resize(new_buffer.capacity());
+
+ auto [new_header, payload_size, audio_data] =
+ NextAudioMessage(audio_data_rv, &new_buffer);
+
+ LOG(DEBUG) << "stream " << new_header.stream_number << ", frame "
+ << new_header.frame_num << ", rate " << new_header.frame_rate
+ << ", channel_mask " << new_header.channel_mask << ", format "
+ << new_header.format << ", payload_size " << payload_size
+ << '\n';
+
+ {
+ std::lock_guard guard(buffer_lock_);
+ CheckAudioConfigurationIsSame(new_header);
+ header_ = new_header;
+ audio_buffer_ = std::make_shared<const cvd::Message>(
+ audio_data, audio_data + payload_size);
+ }
+ buffer_cv_.notify_all();
+ }
+ }
+
+ private:
+ struct AudioMessage {
+ gce_audio_message header;
+ std::size_t payload_size;
+ const std::uint8_t* payload_data;
+ };
+
+ void ReadAudioMessage(AudioDataRegionView* audio_data_rv,
+ std::vector<char>* buffer) const {
+ while (true) {
+ auto read_size = audio_data_rv->data()->audio_queue.Read(
+ audio_data_rv, buffer->data(), buffer->size());
+ if (read_size == -ENOSPC) {
+ DoubleSize(buffer);
+ } else if (read_size < 0) {
+ LOG(ERROR) << "CircularPacketQueue::Read returned " << read_size;
+ } else {
+ buffer->resize(read_size);
+ return;
+ }
+ }
+ }
+
+ void DoubleSize(std::vector<char>* buffer) const {
+ if (buffer->empty()) {
+ buffer->resize(1);
+ } else {
+ buffer->resize(buffer->size() * 2);
+ }
+ }
+
+ gce_audio_message GetHeaderFromBuffer(const std::vector<char>& buffer) const {
+ gce_audio_message new_header{};
+ CHECK_GE(buffer.size(), sizeof new_header);
+
+ std::memcpy(&new_header, buffer.data(), sizeof new_header);
+ CHECK_GT(new_header.stream_number, 0u);
+ return new_header;
+ }
+
+ std::tuple<std::size_t, const std::uint8_t*> GetPayloadFromBuffer(
+ const std::vector<char>& buffer) const {
+ const auto payload_size = buffer.size() - sizeof(gce_audio_message);
+ const auto* audio_data =
+ reinterpret_cast<const std::uint8_t*>(buffer.data()) +
+ sizeof(gce_audio_message);
+ return {payload_size, audio_data};
+ }
+
+ AudioMessage NextAudioMessage(AudioDataRegionView* audio_data_rv,
+ std::vector<char>* buffer) const {
+ while (true) {
+ ReadAudioMessage(audio_data_rv, buffer);
+ auto header = GetHeaderFromBuffer(*buffer);
+ if (header.message_type == gce_audio_message::DATA_SAMPLES) {
+ auto [payload_size, payload_data] = GetPayloadFromBuffer(*buffer);
+ return {header, payload_size, payload_data};
+ }
+ }
+ }
+
+ void CheckAudioConfigurationIsSame(
+ const gce_audio_message& new_header) const {
+ if (audio_buffer_) {
+ CHECK_EQ(header_.frame_size, new_header.frame_size)
+ << "audio frame_size changed";
+ CHECK_EQ(header_.frame_rate, new_header.frame_rate)
+ << "audio frame_rate changed";
+ CHECK_EQ(header_.stream_number, new_header.stream_number)
+ << "audio stream_number changed";
+ }
+ }
+
+ std::shared_ptr<const cvd::Message> audio_buffer_{};
+ gce_audio_message header_{};
+ mutable std::mutex buffer_lock_;
+ mutable std::condition_variable buffer_cv_;
+};
+
+void HandleClient(AudioStreamer* audio_streamer,
+ cvd::ClientSocket client_socket) {
+ auto num_channels = audio_streamer->num_channels();
+ opus::Encoder enc(audio_streamer->frame_rate(),
+ audio_streamer->num_channels(), OPUS_APPLICATION_AUDIO);
+ CHECK(enc.valid()) << "Could not construct Encoder. Maybe bad frame_rate ("
+ << audio_streamer->frame_rate() <<") or num_channels ("
+ << audio_streamer->num_channels() << ")?";
+
+ auto header = audio_streamer->MakeAudioDescriptionHeader();
+ client_socket.SendNoSignal(header);
+ std::int64_t previous_frame_num = 0;
+
+ while (!client_socket.closed()) {
+ CHECK(enc.valid()) << "encoder in invalid state";
+ auto [frame_num, audio_data] =
+ audio_streamer->audio_buffer(previous_frame_num);
+ previous_frame_num = frame_num;
+
+ std::vector<opus_int16> pcm(audio_data->size() / sizeof(opus_int16));
+ std::memcpy(pcm.data(), audio_data->data(), audio_data->size());
+ // in opus terms "frame_size" is the number of unencoded samples per frame
+ const std::uint32_t frame_size = pcm.size() / num_channels;
+ auto encoded = enc.Encode(pcm, frame_size);
+ for (auto&& p : encoded) {
+ auto length_message =
+ cvd::CreateMessage(static_cast<std::uint32_t>(p.size()));
+ client_socket.SendNoSignal(length_message);
+ client_socket.SendNoSignal(cvd::CreateMessage(frame_size));
+ client_socket.SendNoSignal(p);
+ }
+ }
+}
+
+[[noreturn]] void AudioStreamerUpdateLoop(AudioStreamer* audio_streamer) {
+ while (true) {
+ audio_streamer->Update();
+ }
+}
+
+[[noreturn]] void MainLoop() {
+ AudioStreamer audio_streamer;
+ std::thread audio_streamer_update_thread;
+ auto server = cvd::ServerSocket(FLAGS_port);
+ while (true) {
+ LOG(INFO) << "waiting for client connection";
+ auto client = server.Accept();
+ LOG(INFO) << "client socket accepted";
+ if (!audio_streamer_update_thread.joinable()) {
+ audio_streamer_update_thread =
+ std::thread{AudioStreamerUpdateLoop, &audio_streamer};
+ }
+ std::thread(HandleClient, &audio_streamer, std::move(client)).detach();
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ gflags::SetUsageMessage(" ");
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_port <= 0) {
+ std::cerr << "--port must be specified.\n";
+ return 1;
+ }
+ MainLoop();
+}
diff --git a/host/frontend/stream_audio/opuscpp/Android.bp b/host/frontend/stream_audio/opuscpp/Android.bp
new file mode 100644
index 0000000..9ad7a52
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/Android.bp
@@ -0,0 +1,28 @@
+//
+// 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.
+
+cc_library_host_static {
+ name: "libopuscpp",
+ srcs: [
+ "opus_wrapper.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "libopus",
+ ],
+ cpp_std: "c++17",
+ enabled: false,
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.cc b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
new file mode 100644
index 0000000..538bee8
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+// https://github.com/google/opuscpp
+
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+
+std::string opus::ErrorToString(int error) {
+ switch (error) {
+ case OPUS_OK:
+ return "OK";
+ case OPUS_BAD_ARG:
+ return "One or more invalid/out of range arguments.";
+ case OPUS_BUFFER_TOO_SMALL:
+ return "The mode struct passed is invalid.";
+ case OPUS_INTERNAL_ERROR:
+ return "An internal error was detected.";
+ case OPUS_INVALID_PACKET:
+ return "The compressed data passed is corrupted.";
+ case OPUS_UNIMPLEMENTED:
+ return "Invalid/unsupported request number.";
+ case OPUS_INVALID_STATE:
+ return "An encoder or decoder structure is invalid or already freed.";
+ default:
+ return "Unknown error code: " + std::to_string(error);
+ }
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusEncoder* encoder) const
+ noexcept {
+ opus_encoder_destroy(encoder);
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusDecoder* decoder) const
+ noexcept {
+ opus_decoder_destroy(decoder);
+}
+
+opus::Encoder::Encoder(opus_int32 sample_rate, int num_channels,
+ int application, int expected_loss_percent)
+ : num_channels_{num_channels} {
+ int error{};
+ encoder_.reset(
+ opus_encoder_create(sample_rate, num_channels, application, &error));
+ valid_ = error == OPUS_OK;
+ if (!valid()) {
+ LOG(INFO) << "Could not construct encoder. Error: " << ErrorToString(error);
+ return;
+ }
+ if (expected_loss_percent > 0) {
+ LOG(INFO) << "Enabling FEC in the encoder.";
+ Ctl(OPUS_SET_INBAND_FEC(1));
+ Ctl(OPUS_SET_PACKET_LOSS_PERC(expected_loss_percent));
+ }
+}
+
+bool opus::Encoder::ResetState() {
+ valid_ = Ctl(OPUS_RESET_STATE) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetBitrate(int bitrate) {
+ valid_ = Ctl(OPUS_SET_BITRATE(bitrate)) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetVariableBitrate(int vbr) {
+ valid_ = Ctl(OPUS_SET_VBR(vbr)) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetComplexity(int complexity) {
+ valid_ = Ctl(OPUS_SET_COMPLEXITY(complexity)) == OPUS_OK;
+ return valid_;
+}
+
+int opus::Encoder::GetLookahead() {
+ opus_int32 skip{};
+ valid_ = Ctl(OPUS_GET_LOOKAHEAD(&skip)) == OPUS_OK;
+ return skip;
+}
+
+std::vector<std::vector<unsigned char>> opus::Encoder::Encode(
+ const std::vector<opus_int16>& pcm, int frame_size) {
+ constexpr auto sample_size = sizeof(pcm[0]);
+ const auto frame_length = frame_size * num_channels_ * sample_size;
+ auto data_length = pcm.size() * sample_size;
+ if (data_length % frame_length != 0u) {
+ LOG(WARNING) << "PCM samples contain an incomplete frame. Ignoring the "
+ "incomplete frame.";
+ data_length -= (data_length % frame_length);
+ }
+
+ std::vector<std::vector<unsigned char>> encoded;
+ for (std::size_t i{}; i < data_length; i += frame_length) {
+ encoded.push_back(EncodeFrame(pcm.begin() + (i / sample_size), frame_size));
+ }
+ return encoded;
+}
+
+std::vector<unsigned char> opus::Encoder::EncodeFrame(
+ const std::vector<opus_int16>::const_iterator& frame_start,
+ int frame_size) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(*frame_start));
+ std::vector<unsigned char> encoded(frame_length);
+ auto num_bytes = opus_encode(encoder_.get(), &*frame_start, frame_size,
+ encoded.data(), encoded.size());
+ if (num_bytes < 0) {
+ LOG(ERROR) << "Encode error: " << opus::ErrorToString(num_bytes);
+ return {};
+ }
+ encoded.resize(num_bytes);
+ return encoded;
+}
+
+opus::Decoder::Decoder(opus_uint32 sample_rate, int num_channels)
+ : num_channels_(num_channels) {
+ int error{};
+ decoder_.reset(opus_decoder_create(sample_rate, num_channels, &error));
+ valid_ = error == OPUS_OK;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+ const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+ bool decode_fec) {
+ std::vector<opus_int16> decoded;
+ for (const auto& enc : packets) {
+ auto just_decoded = Decode(enc, frame_size, decode_fec);
+ decoded.insert(std::end(decoded), std::begin(just_decoded),
+ std::end(just_decoded));
+ }
+ return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+ const std::vector<unsigned char>& packet, int frame_size, bool decode_fec) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+ std::vector<opus_int16> decoded(frame_length);
+ auto num_samples = opus_decode(decoder_.get(), packet.data(), packet.size(),
+ decoded.data(), frame_size, decode_fec);
+ if (num_samples < 0) {
+ LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+ return {};
+ }
+ decoded.resize(num_samples * num_channels_);
+ return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::DecodeDummy(int frame_size) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+ std::vector<opus_int16> decoded(frame_length);
+ auto num_samples =
+ opus_decode(decoder_.get(), nullptr, 0, decoded.data(), frame_size, true);
+ if (num_samples < 0) {
+ LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+ return {};
+ }
+ decoded.resize(num_samples * num_channels_);
+ return decoded;
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.h b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
new file mode 100644
index 0000000..07e932e
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
@@ -0,0 +1,133 @@
+// Copyright 2018 Google LLC
+//
+// 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
+//
+// https://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.
+
+// https://github.com/google/opuscpp
+
+#ifndef OPUSCPP_OPUS_WRAPPER_H_
+#define OPUSCPP_OPUS_WRAPPER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "opus.h"
+
+namespace opus {
+
+std::string ErrorToString(int error);
+
+namespace internal {
+// Deleter for OpusEncoders and OpusDecoders
+struct OpusDestroyer {
+ void operator()(OpusEncoder* encoder) const noexcept;
+ void operator()(OpusDecoder* decoder) const noexcept;
+};
+template <typename T>
+using opus_uptr = std::unique_ptr<T, OpusDestroyer>;
+} // namespace internal
+
+class Encoder {
+ public:
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gaa89264fd93c9da70362a0c9b96b9ca88
+ // Fs corresponds to sample_rate
+ //
+ // If expected_loss_percent is positive, FEC will be enabled
+ Encoder(opus_int32 sample_rate, int num_channels, int application,
+ int expected_loss_percent = 0);
+
+ // Resets internal state of encoder. This should be called between encoding
+ // different streams so that back-to-back decoding and one-at-a-time decoding
+ // give the same result. Returns true on success.
+ bool ResetState();
+
+ // Sets the desired bitrate. Rates from 500 to 512000 are meaningful as well
+ // as the special values OPUS_AUTO and OPUS_BITRATE_MAX. If this method
+ // is not called, the default value of OPUS_AUTO is used.
+ // Returns true on success.
+ bool SetBitrate(int bitrate);
+
+ // Enables or disables variable bitrate in the encoder. By default, variable
+ // bitrate is enabled. Returns true on success.
+ bool SetVariableBitrate(int vbr);
+
+ // Sets the computational complexity of the encoder, in the range of 0 to 10,
+ // inclusive, with 10 being the highest complexity. Returns true on success.
+ bool SetComplexity(int complexity);
+
+ // Gets the total samples of delay added by the entire codec. This value
+ // is the minimum amount of 'preskip' that has to be specified in an
+ // ogg-stream that encapsulates the encoded audio.
+ int GetLookahead();
+
+ // Takes audio data and encodes it. Returns a sequence of encoded packets.
+ // pcm.size() must be divisible by frame_size * (number of channels);
+ // pcm must not contain any incomplete packets.
+ // see documentation for pcm and frame_size at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gad2d6bf6a9ffb6674879d7605ed073e25
+ std::vector<std::vector<unsigned char>> Encode(
+ const std::vector<opus_int16>& pcm, int frame_size);
+
+ int valid() const { return valid_; }
+
+ private:
+ std::vector<unsigned char> EncodeFrame(
+ const std::vector<opus_int16>::const_iterator& frame_start,
+ int frame_size);
+
+ template <typename... Ts>
+ int Ctl(int request, Ts... args) const {
+ return opus_encoder_ctl(encoder_.get(), request, args...);
+ }
+
+ int num_channels_{};
+ bool valid_{};
+ internal::opus_uptr<OpusEncoder> encoder_;
+};
+
+class Decoder {
+ public:
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga753f6fe0b699c81cfd47d70c8e15a0bd
+ // Fs corresponds to sample_rate
+ Decoder(opus_uint32 sample_rate, int num_channels);
+
+ // Takes a sequence of encoded packets and decodes them. Returns the decoded
+ // audio.
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+ std::vector<opus_int16> Decode(
+ const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+ bool decode_fec);
+
+ int valid() const { return valid_; }
+
+ // Takes an encoded packet and decodes it. Returns the decoded audio
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+ std::vector<opus_int16> Decode(const std::vector<unsigned char>& packet,
+ int frame_size, bool decode_fec);
+
+ // Generates a dummy frame by passing nullptr to the underlying opus decode.
+ std::vector<opus_int16> DecodeDummy(int frame_size);
+
+ private:
+ int num_channels_{};
+ bool valid_{};
+ internal::opus_uptr<OpusDecoder> decoder_;
+};
+
+} // namespace opus
+
+#endif
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index 210f21d..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -30,6 +30,7 @@
"cuttlefish_glog",
],
shared_libs: [
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_tcp_socket",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
index 35cf22f..53cb389 100644
--- a/host/frontend/vnc_server/screen_connector.cpp
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -18,11 +18,11 @@
#include <atomic>
#include <condition_variable>
-#include <thread>
-#include <glog/logging.h>
#include <gflags/gflags.h>
+#include <common/vsoc/lib/screen_region_view.h>
+#include <host/libs/config/cuttlefish_config.h>
#include "host/frontend/vnc_server/vnc_utils.h"
DEFINE_int32(frame_server_fd, -1, "");
@@ -31,6 +31,23 @@
namespace vnc {
namespace {
+class VSoCScreenConnector : public ScreenConnector {
+ public:
+ int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+ if (!screen_view_) return -1;
+ return screen_view_->WaitForNewFrameSince(seq_num);
+ }
+
+ void* GetBuffer(int buffer_idx) override {
+ if (!screen_view_) return nullptr;
+ return screen_view_->GetBuffer(buffer_idx);
+ }
+
+ private:
+ vsoc::screen::ScreenRegionView* screen_view_ =
+ vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+};
+
// TODO(b/128852363): Substitute with one based on memory shared with the
// wayland mock
class SocketBasedScreenConnector : public ScreenConnector {
@@ -123,7 +140,12 @@
} // namespace
ScreenConnector* ScreenConnector::Get() {
- return new SocketBasedScreenConnector();
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (config->enable_ivserver()) {
+ return new VSoCScreenConnector();
+ } else {
+ return new SocketBasedScreenConnector();
+ }
}
} // namespace vnc
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index 96702e8..7a94454 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -20,6 +20,7 @@
#include "host/libs/config/cuttlefish_config.h"
using cvd::vnc::SimulatedHWComposer;
+using vsoc::screen::ScreenRegionView;
SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
:
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index fb91409..31c0328 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -16,19 +16,18 @@
#include "host/frontend/vnc_server/virtual_inputs.h"
#include <gflags/gflags.h>
-#include <glog/logging.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <cstdint>
#include <mutex>
-#include <thread>
#include "keysyms.h"
#include <common/libs/fs/shared_select.h>
#include <host/libs/config/cuttlefish_config.h>
using cvd::vnc::VirtualInputs;
+using vsoc::input_events::InputEventsRegionView;
DEFINE_int32(touch_fd, -1,
"A fd for a socket where to accept touch connections");
@@ -246,6 +245,37 @@
} // namespace
+class VSoCVirtualInputs : public VirtualInputs {
+ public:
+ VSoCVirtualInputs()
+ : input_events_region_view_{
+ vsoc::input_events::InputEventsRegionView::GetInstance(
+ vsoc::GetDomain().c_str())} {
+ if (!input_events_region_view_) {
+ LOG(FATAL) << "Failed to open Input events region view";
+ }
+ }
+
+ void GenerateKeyPressEvent(int code, bool down) override {
+ if (keymapping_.count(code)) {
+ input_events_region_view_->HandleKeyboardEvent(down, keymapping_[code]);
+ } else {
+ LOG(ERROR) << "Unknown keycode" << code;
+ }
+ }
+
+ void PressPowerButton(bool down) override {
+ input_events_region_view_->HandlePowerButtonEvent(down);
+ }
+
+ void HandlePointerEvent(bool touch_down, int x, int y) override {
+ input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
+ }
+
+ private:
+ vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
+};
+
class SocketVirtualInputs : public VirtualInputs {
public:
SocketVirtualInputs()
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index 7aca3eb..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -21,6 +21,8 @@
#include <map>
#include <mutex>
+#include "common/vsoc/lib/input_events_region_view.h"
+
namespace cvd {
namespace vnc {
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index b81bb88..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -42,17 +42,7 @@
using cvd::vnc::Stripe;
using cvd::vnc::StripePtrVec;
using cvd::vnc::VncClientConnection;
-
-struct ScreenRegionView {
- using Pixel = uint32_t;
- static constexpr int kSwiftShaderPadding = 4;
- static constexpr int kRedShift = 0;
- static constexpr int kGreenShift = 8;
- static constexpr int kBlueShift = 16;
- static constexpr int kRedBits = 8;
- static constexpr int kGreenBits = 8;
- static constexpr int kBlueBits = 8;
-};
+using vsoc::screen::ScreenRegionView;
DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index db88131..0953c4e 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -23,6 +23,7 @@
#include "common/libs/utils/size_utils.h"
#include "common/libs/tcp_socket/tcp_socket.h"
+#include "common/vsoc/lib/screen_region_view.h"
#include "host/libs/config/cuttlefish_config.h"
namespace cvd {
@@ -64,7 +65,7 @@
};
inline constexpr int BytesPerPixel() {
- return sizeof(uint32_t);
+ return sizeof(vsoc::screen::ScreenRegionView::Pixel);
}
// The width of the screen regardless of orientation. Does not change.
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 6f66fb3..17dcb6e 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -134,6 +134,10 @@
const char* kVncServerBinary = "vnc_server_binary";
const char* kVncServerPort = "vnc_server_port";
+const char* kEnableStreamAudio = "enable_stream_audio";
+const char* kStreamAudioBinary = "stream_audio_binary";
+const char* kStreamAudioPort = "stream_audio_port";
+
const char* kRestartSubprocesses = "restart_subprocesses";
const char* kRunAdbConnector = "run_adb_connector";
const char* kAdbConnectorBinary = "adb_connector_binary";
@@ -559,7 +563,9 @@
static AdbMode stringToAdbMode(std::string mode) {
std::transform(mode.begin(), mode.end(), mode.begin(), ::tolower);
- if (mode == "vsock_tunnel") {
+ if (mode == "tunnel") {
+ return AdbMode::Tunnel;
+ } else if (mode == "vsock_tunnel") {
return AdbMode::VsockTunnel;
} else if (mode == "vsock_half_tunnel") {
return AdbMode::VsockHalfTunnel;
@@ -606,10 +612,11 @@
std::string CuttlefishConfig::adb_device_name() const {
// TODO(schuffelen): Deal with duplication between here and launch.cc
+ bool tunnelMode = adb_mode().count(AdbMode::Tunnel) > 0;
bool vsockTunnel = adb_mode().count(AdbMode::VsockTunnel) > 0;
bool vsockHalfProxy = adb_mode().count(AdbMode::VsockHalfTunnel) > 0;
bool nativeVsock = adb_mode().count(AdbMode::NativeVsock) > 0;
- if (vsockTunnel || vsockHalfProxy || nativeVsock) {
+ if (tunnelMode || vsockTunnel || vsockHalfProxy || nativeVsock) {
return adb_ip_and_port();
} else if (adb_mode().count(AdbMode::Usb) > 0) {
return serial_number();
@@ -701,6 +708,31 @@
(*dictionary_)[kVncServerPort] = vnc_server_port;
}
+bool CuttlefishConfig::enable_stream_audio() const {
+ return (*dictionary_)[kEnableStreamAudio].asBool();
+}
+
+void CuttlefishConfig::set_enable_stream_audio(bool enable_stream_audio) {
+ (*dictionary_)[kEnableStreamAudio] = enable_stream_audio;
+}
+
+std::string CuttlefishConfig::stream_audio_binary() const {
+ return (*dictionary_)[kStreamAudioBinary].asString();
+}
+
+void CuttlefishConfig::set_stream_audio_binary(
+ const std::string& stream_audio_binary) {
+ (*dictionary_)[kStreamAudioBinary] = stream_audio_binary;
+}
+
+int CuttlefishConfig::stream_audio_port() const {
+ return (*dictionary_)[kStreamAudioPort].asInt();
+}
+
+void CuttlefishConfig::set_stream_audio_port(int stream_audio_port) {
+ (*dictionary_)[kStreamAudioPort] = stream_audio_port;
+}
+
bool CuttlefishConfig::restart_subprocesses() const {
return (*dictionary_)[kRestartSubprocesses].asBool();
}
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 4ec49e7..fc479ec 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -39,6 +39,7 @@
enum class AdbMode {
+ Tunnel,
VsockTunnel,
VsockHalfTunnel,
NativeVsock,
@@ -275,6 +276,15 @@
void set_vnc_server_binary(const std::string& vnc_server_binary);
std::string vnc_server_binary() const;
+ void set_enable_stream_audio(bool enable_stream_audio);
+ bool enable_stream_audio() const;
+
+ void set_stream_audio_port(int stream_audio_port);
+ int stream_audio_port() const;
+
+ void set_stream_audio_binary(const std::string& stream_audio_binary);
+ std::string stream_audio_binary() const;
+
void set_restart_subprocesses(bool restart_subprocesses);
bool restart_subprocesses() const;
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.cpp b/host/vsoc/lib/gralloc_buffer_region_view.cpp
new file mode 100644
index 0000000..db302cb
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 "host/vsoc/lib/gralloc_buffer_region_view.h"
+
+#include <memory>
+#include <mutex>
+#include "glog/logging.h"
+
+using vsoc::gralloc::GrallocBufferRegionView;
+
+uint8_t* GrallocBufferRegionView::OffsetToBufferPtr(uint32_t offset) {
+ if (offset <= control_->region_desc().offset_of_region_data ||
+ offset >= control_->region_size()) {
+ LOG(FATAL)
+ << "Attempted to access a gralloc buffer outside region data, offset: "
+ << offset;
+ return nullptr;
+ }
+ return region_offset_to_pointer<uint8_t>(offset);
+}
diff --git a/host/vsoc/lib/gralloc_buffer_region_view.h b/host/vsoc/lib/gralloc_buffer_region_view.h
new file mode 100644
index 0000000..b132a0e
--- /dev/null
+++ b/host/vsoc/lib/gralloc_buffer_region_view.h
@@ -0,0 +1,47 @@
+#pragma once
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/typed_region_view.h"
+#include "common/vsoc/shm/gralloc_layout.h"
+
+#include <string>
+
+namespace vsoc {
+namespace gralloc {
+
+// Allows access to the gralloc buffer region from host side. It needs to be a
+// different class than the one on guest side because of the required
+// interactions with the kernel on the guest.
+// Initially this class only returns a pointer to a buffer in memory given a
+// region offset, which is enough for now since it's only used by the hwcomposer
+// (which gets all other information from the guest side hwcomposer) and by the
+// VNC server (which uses only the frame buffer and gets the information it
+// needs from the framebuffer region).
+class GrallocBufferRegionView
+ : vsoc::TypedRegionView<
+ GrallocBufferRegionView,
+ vsoc::layout::gralloc::GrallocBufferLayout> {
+ public:
+ GrallocBufferRegionView() = default;
+ GrallocBufferRegionView(const GrallocBufferRegionView&) = delete;
+ GrallocBufferRegionView& operator=(const GrallocBufferRegionView&) = delete;
+
+ uint8_t* OffsetToBufferPtr(uint32_t offset);
+};
+
+} // namespace gralloc
+} // namespace vsoc
diff --git a/host/vsoc/lib/host_lock.cpp b/host/vsoc/lib/host_lock.cpp
new file mode 100644
index 0000000..895a429
--- /dev/null
+++ b/host/vsoc/lib/host_lock.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/shm/lock.h"
+
+#include "sys/types.h"
+
+#include "common/vsoc/lib/compat.h"
+#include "common/vsoc/lib/single_sided_signal.h"
+
+namespace vsoc {
+namespace layout {
+
+void HostLock::Lock() {
+ uint32_t tid = gettid();
+ uint32_t expected_value;
+ uint32_t* uaddr = reinterpret_cast<uint32_t*>(&lock_uint32_);
+
+ while (1) {
+ if (TryLock(tid, &expected_value)) {
+ return;
+ }
+ SingleSidedSignal::AwaitSignal(expected_value, uaddr);
+ }
+}
+
+void HostLock::Unlock() {
+ Sides sides_to_signal = UnlockCommon(gettid());
+ if (sides_to_signal != Sides::NoSides) {
+ SingleSidedSignal::Signal(&lock_uint32_);
+ }
+}
+
+bool HostLock::Recover() {
+ return RecoverSingleSided();
+}
+
+} // namespace layout
+} // namespace vsoc
diff --git a/host/vsoc/lib/host_region_e2e_test.cpp b/host/vsoc/lib/host_region_e2e_test.cpp
new file mode 100644
index 0000000..1366838
--- /dev/null
+++ b/host/vsoc/lib/host_region_e2e_test.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * End-to-end test to ensure that mapping of vsoc regions works on the host.
+ */
+
+#include <gtest/gtest.h>
+#include "common/vsoc/lib/e2e_test_region_view.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+// Here is a summary of the two regions interrupt and write test:
+// 1. Write our strings to the first region
+// 2. Ensure that our peer hasn't signalled the second region. That would
+// indicate that it didn't wait for our interrupt.
+// 3. Send the interrupt on the first region
+// 4. Wait for our peer's interrupt on the first region
+// 5. Confirm that we can see our peer's writes in the first region
+// 6. Initialize our strings in the second region
+// 7. Send an interrupt on the second region to our peer
+// 8. Wait for our peer's interrupt on the second region
+// 9. Confirm that we can see our peer's writes in the second region
+// 10. Repeat the process for signaling.
+// 11. Confirm that no interrupt is pending in the first region
+// 12. Confirm that no interrupt is pending in the second region
+
+template <typename View>
+void SetHostStrings(View* in) {
+ size_t num_data = in->string_size();
+ EXPECT_LE(static_cast<size_t>(2), num_data);
+ for (size_t i = 0; i < num_data; ++i) {
+ EXPECT_TRUE(!in->host_string(i)[0] ||
+ !strcmp(in->host_string(i), View::Layout::host_pattern));
+ in->set_host_string(i, View::Layout::host_pattern);
+ EXPECT_STREQ(in->host_string(i), View::Layout::host_pattern);
+ }
+}
+
+template <typename View>
+void CheckPeerStrings(View* in) {
+ size_t num_data = in->string_size();
+ EXPECT_LE(static_cast<size_t>(2), num_data);
+ for (size_t i = 0; i < num_data; ++i) {
+ EXPECT_STREQ(View::Layout::guest_pattern, in->guest_string(i));
+ }
+}
+
+TEST(RegionTest, PeerTests) {
+ auto primary =
+ vsoc::E2EPrimaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+ ASSERT_TRUE(!!primary);
+ auto secondary =
+ vsoc::E2ESecondaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+ ASSERT_TRUE(!!secondary);
+ LOG(INFO) << "Regions are open";
+ SetHostStrings(primary);
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+ EXPECT_TRUE(primary->MaybeInterruptPeer());
+ LOG(INFO) << "Waiting for first interrupt from peer";
+ primary->WaitForInterrupt();
+ LOG(INFO) << "First interrupt received";
+ CheckPeerStrings(primary);
+ SetHostStrings(secondary);
+ EXPECT_TRUE(secondary->MaybeInterruptPeer());
+ LOG(INFO) << "Waiting for second interrupt from peer";
+ secondary->WaitForInterrupt();
+ LOG(INFO) << "Second interrupt received";
+ CheckPeerStrings(secondary);
+
+ // Test signals
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+ LOG(INFO) << "Verified no early second signal";
+ primary->SendSignal(vsoc::layout::Sides::Peer,
+ &primary->data()->host_to_guest_signal);
+ LOG(INFO) << "Signal sent. Waiting for first signal from peer";
+ primary->WaitForInterrupt();
+ int count = 0; // counts the number of signals received.
+ primary->ProcessSignalsFromPeer(
+ [&primary, &count](uint32_t offset) {
+ ++count;
+ EXPECT_EQ(primary->guest_to_host_signal_offset(), offset);
+ });
+ EXPECT_EQ(1, count);
+ LOG(INFO) << "Signal received on primary region";
+ secondary->SendSignal(vsoc::layout::Sides::Peer,
+ &secondary->data()->host_to_guest_signal);
+ LOG(INFO) << "Signal sent. Waiting for second signal from peer";
+ secondary->WaitForInterrupt();
+ count = 0;
+ secondary->ProcessSignalsFromPeer(
+ [secondary, &count](uint32_t offset) {
+ ++count;
+ EXPECT_EQ(secondary->guest_to_host_signal_offset(), offset);
+ });
+ EXPECT_EQ(1, count);
+ LOG(INFO) << "Signal received on secondary region";
+
+ EXPECT_FALSE(primary->HasIncomingInterrupt());
+ EXPECT_FALSE(secondary->HasIncomingInterrupt());
+}
+
+TEST(RegionTest, MissingRegionCausesDeath) {
+ EXPECT_DEATH(
+ vsoc::E2EUnfindableRegionView::GetInstance(vsoc::GetDomain().c_str()),
+ ".*");
+}
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ int rval = RUN_ALL_TESTS();
+ if (!rval) {
+ auto region =
+ vsoc::E2EPrimaryRegionView::GetInstance(vsoc::GetDomain().c_str());
+ region->host_status(vsoc::layout::e2e_test::E2E_MEMORY_FILLED);
+ }
+ return rval;
+}
diff --git a/host/vsoc/lib/region_control.cpp b/host/vsoc/lib/region_control.cpp
new file mode 100644
index 0000000..491f600
--- /dev/null
+++ b/host/vsoc/lib/region_control.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+
+#define LOG_TAG "vsoc: region_host"
+
+#include <stdio.h>
+#include <string.h>
+#include <linux/futex.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/fs/shared_select.h"
+
+using cvd::SharedFD;
+
+namespace {
+
+class HostRegionControl : public vsoc::RegionControl {
+ public:
+ HostRegionControl(const char* region_name,
+ const SharedFD& incoming_interrupt_fd,
+ const SharedFD& outgoing_interrupt_fd,
+ const SharedFD& shared_memory_fd)
+ : region_name_{region_name},
+ incoming_interrupt_fd_{incoming_interrupt_fd},
+ outgoing_interrupt_fd_{outgoing_interrupt_fd},
+ shared_memory_fd_{shared_memory_fd} {}
+
+ int CreateFdScopedPermission(const char* /*managed_region_name*/,
+ uint32_t /*owner_offset*/,
+ uint32_t /*owned_val*/,
+ uint32_t /*begin_offset*/,
+ uint32_t /*end_offset*/) override {
+ return -1;
+ }
+
+ bool InitializeRegion();
+
+ virtual bool InterruptPeer() override {
+ uint64_t one = 1;
+ ssize_t rval = outgoing_interrupt_fd_->Write(&one, sizeof(one));
+ if (rval != sizeof(one)) {
+ LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+ return false;
+ }
+ return true;
+ }
+
+ // Wake the local signal table scanner. Primarily used during shutdown
+ virtual void InterruptSelf() override {
+ uint64_t one = 1;
+ ssize_t rval = incoming_interrupt_fd_->Write(&one, sizeof(one));
+ if (rval != sizeof(one)) {
+ LOG(FATAL) << __FUNCTION__ << ": rval (" << rval << ") != sizeof(one))";
+ }
+ }
+
+ virtual void WaitForInterrupt() override {
+ // Check then act isn't a problem here: the other side does
+ // the following things in exactly this order:
+ // 1. exchanges 1 with interrupt_signalled
+ // 2. if interrupt_signalled was 0 it increments the eventfd
+ // eventfd increments are persistent, so if interrupt_signalled was set
+ // back to 1 while we are going to sleep the sleep will return
+ // immediately.
+ uint64_t missed{};
+ cvd::SharedFDSet readset;
+ readset.Set(incoming_interrupt_fd_);
+ cvd::Select(&readset, NULL, NULL, NULL);
+ ssize_t rval = incoming_interrupt_fd_->Read(&missed, sizeof(missed));
+ if (rval != sizeof(missed)) {
+ LOG(FATAL) << __FUNCTION__ << ": rval (" << rval
+ << ") != sizeof(missed)), are there more than one threads "
+ "waiting for interrupts?";
+ }
+ if (!missed) {
+ LOG(FATAL) << __FUNCTION__ << ": woke with 0 interrupts";
+ }
+ }
+
+ virtual void* Map() override {
+ if (region_base_) {
+ return region_base_;
+ }
+ // Now actually map the region
+ region_base_ =
+ shared_memory_fd_->Mmap(0, region_size(), PROT_READ | PROT_WRITE,
+ MAP_SHARED, region_desc_.region_begin_offset);
+ if (region_base_ == MAP_FAILED) {
+ LOG(FATAL) << "mmap failed for offset "
+ << region_desc_.region_begin_offset << " ("
+ << shared_memory_fd_->StrError() << ")";
+ region_base_ = nullptr;
+ }
+ return region_base_;
+ }
+
+
+ virtual int SignalSelf(uint32_t offset) override {
+ return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
+ FUTEX_WAKE, -1, nullptr, nullptr, 0);
+ }
+
+ virtual int WaitForSignal(uint32_t offset, uint32_t expected_value) override {
+ return syscall(SYS_futex, region_offset_to_pointer<int32_t*>(offset),
+ FUTEX_WAIT, expected_value, nullptr, nullptr, 0);
+ }
+
+ protected:
+ const char* region_name_{};
+ cvd::SharedFD incoming_interrupt_fd_;
+ cvd::SharedFD outgoing_interrupt_fd_;
+ cvd::SharedFD shared_memory_fd_;
+};
+
+// Default path to the ivshmem_server socket. This can vary when we're
+// launching multiple CVDs.
+constexpr int kMaxSupportedProtocolVersion = 0;
+
+bool HostRegionControl::InitializeRegion() {
+ size_t region_name_len = strlen(region_name_);
+ if (region_name_len >= VSOC_DEVICE_NAME_SZ) {
+ LOG(FATAL) << "Region name length (" << region_name_len << ") not < "
+ << VSOC_DEVICE_NAME_SZ;
+ return false;
+ }
+ vsoc_shm_layout_descriptor layout;
+ ssize_t rval = shared_memory_fd_->Pread(&layout, sizeof(layout), 0);
+ if (rval != sizeof(layout)) {
+ LOG(FATAL) << "Unable to read layout, rval=" << rval << " ("
+ << shared_memory_fd_->StrError() << ")";
+ return false;
+ }
+ if (layout.major_version != CURRENT_VSOC_LAYOUT_MAJOR_VERSION) {
+ LOG(FATAL) << "Incompatible major version: saw " << layout.major_version
+ << " wanted " << CURRENT_VSOC_LAYOUT_MAJOR_VERSION;
+ }
+ std::vector<vsoc_device_region> descriptors;
+ descriptors.resize(layout.region_count);
+ ssize_t wanted = sizeof(vsoc_device_region) * layout.region_count;
+ rval = shared_memory_fd_->Pread(descriptors.data(), wanted,
+ layout.vsoc_region_desc_offset);
+ if (rval != wanted) {
+ LOG(FATAL) << "Unable to read region descriptors, rval=" << rval << " ("
+ << shared_memory_fd_->StrError() << ")";
+ return false;
+ }
+ for (const auto& desc : descriptors) {
+ if (!strcmp(region_name_, desc.device_name)) {
+ region_desc_ = desc;
+ return true;
+ }
+ }
+
+ std::ostringstream buf;
+ for (const auto& desc : descriptors) {
+ buf << " " << desc.device_name;
+ }
+ LOG(FATAL) << "Region name of " << region_name_
+ << " not found among:" << buf.str();
+ return false;
+}
+} // namespace
+
+std::shared_ptr<vsoc::RegionControl> vsoc::RegionControl::Open(
+ const char* region_name, const char* domain) {
+ AutoFreeBuffer msg;
+
+ SharedFD region_server =
+ SharedFD::SocketLocalClient(domain, false, SOCK_STREAM);
+ if (!region_server->IsOpen()) {
+ LOG(FATAL) << "Could not contact ivshmem_server ("
+ << region_server->StrError() << ")";
+ return nullptr;
+ }
+
+ // Check server protocol version.
+ uint32_t protocol_version;
+ ssize_t bytes = region_server->Recv(&protocol_version,
+ sizeof(protocol_version), MSG_NOSIGNAL);
+ if (bytes != sizeof(protocol_version)) {
+ LOG(FATAL) << "Failed to recv protocol version; res=" << bytes << " ("
+ << region_server->StrError() << ")";
+ return nullptr;
+ }
+
+ if (protocol_version > kMaxSupportedProtocolVersion) {
+ LOG(FATAL) << "Unsupported protocol version " << protocol_version
+ << "; max supported version is " << kMaxSupportedProtocolVersion;
+ return nullptr;
+ }
+
+ // Send requested region.
+ int16_t size = strlen(region_name);
+ bytes = region_server->Send(&size, sizeof(size), MSG_NOSIGNAL);
+ if (bytes != sizeof(size)) {
+ LOG(FATAL) << "Failed to send region name length; res=" << bytes << " ("
+ << region_server->StrError() << ")";
+ return nullptr;
+ }
+
+ bytes = region_server->Send(region_name, size, MSG_NOSIGNAL);
+ if (bytes != size) {
+ LOG(FATAL) << "Failed to send region name; res=" << bytes << " ("
+ << region_server->StrError() << ")";
+ return nullptr;
+ }
+
+ // Receive control sockets.
+ uint64_t control_data;
+ struct iovec iov {
+ &control_data, sizeof(control_data)
+ };
+ cvd::InbandMessageHeader hdr{};
+ hdr.msg_iov = &iov;
+ hdr.msg_iovlen = 1;
+ SharedFD fds[3];
+ bytes = region_server->RecvMsgAndFDs(hdr, 0, &fds);
+ if (bytes != sizeof(control_data)) {
+ LOG(FATAL) << "Failed to complete handshake; res=" << bytes << " ("
+ << region_server->StrError() << ")";
+ return nullptr;
+ }
+ HostRegionControl* rval =
+ new HostRegionControl(region_name, fds[0], fds[1], fds[2]);
+ if (!rval) {
+ return nullptr;
+ }
+ // Search for the region header
+ if (!rval->InitializeRegion()) {
+ // We already logged, so we can just bail out.
+ return nullptr;
+ }
+ return std::shared_ptr<RegionControl>(rval);
+}
diff --git a/host/vsoc/lib/region_view.cpp b/host/vsoc/lib/region_view.cpp
new file mode 100644
index 0000000..975b7cd
--- /dev/null
+++ b/host/vsoc/lib/region_view.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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 "common/vsoc/lib/region_view.h"
+#include "common/vsoc/lib/region_control.h"
+
+const vsoc_signal_table_layout& vsoc::RegionView::incoming_signal_table() {
+ return control_->region_desc().guest_to_host_signal_table;
+}
+
+// Returns a pointer to the table that will be used to post signals
+const vsoc_signal_table_layout& vsoc::RegionView::outgoing_signal_table() {
+ return control_->region_desc().host_to_guest_signal_table;
+}