Merge remote-tracking branch 'aosp/cuttlefish-testing' into master
Bug: 129429729
Test: TH
Change-Id: Iec596ffa78a0b07c7d1952eb501f703f93d462a0
diff --git a/Android.bp b/Android.bp
index be209a0..a604f8c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
header_libs: [
"cuttlefish_common_headers",
"cuttlefish_kernel_headers",
+ "cuttlefish_shared_config",
],
target: {
host: {
@@ -56,19 +57,6 @@
vendor: true,
}
-// ARM code should not touch the VSoC window on an x86 CPU.
-cc_defaults {
- name: "cuttlefish_native_isa",
- target: {
- android_arm: {
- enabled: false,
- },
- android_arm64: {
- enabled: false,
- },
- },
-}
-
cc_defaults {
name: "cuttlefish_guest_only",
defaults: ["cuttlefish_base"],
@@ -97,16 +85,13 @@
"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/ril_layout.cpp",
- "common/vsoc/lib/ril_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",
- "common/vsoc/lib/wifi_exchange_layout.cpp",
- "common/vsoc/lib/wifi_exchange_view.cpp",
],
header_libs: ["cuttlefish_glog"],
shared_libs: [
@@ -133,7 +118,7 @@
],
},
},
- defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+ defaults: ["cuttlefish_host_and_guest"],
}
cc_test_host {
diff --git a/Android.mk b/Android.mk
index c38d99b..fa659d7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -11,7 +11,7 @@
# 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.
-ifneq ($(filter vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
+ifneq ($(filter vsoc_arm vsoc_arm64 vsoc_x86 vsoc_x86_64, $(TARGET_DEVICE)),)
LOCAL_PATH:= $(call my-dir)
include $(call first-makefiles-under,$(LOCAL_PATH))
endif
diff --git a/OWNERS b/OWNERS
index 5c0d0bd..3f32871 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,7 @@
+astrachan@google.com
ghartman@google.com
jemoreira@google.com
-haining@google.com
malchev@google.com
schuffelen@google.com
+muntsinger@google.com
+rammuthiah@google.com
\ No newline at end of file
diff --git a/common/frontend/Android.bp b/common/frontend/Android.bp
index 8021915..a7a4de5 100644
--- a/common/frontend/Android.bp
+++ b/common/frontend/Android.bp
@@ -15,4 +15,5 @@
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
index 81ba9c4..74ef53a 100644
--- a/common/frontend/socket_forward_proxy/Android.bp
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -41,5 +41,5 @@
],
},
},
- defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"]
+ defaults: ["cuttlefish_host_and_guest"]
}
diff --git a/common/frontend/socket_vsock_proxy/Android.bp b/common/frontend/socket_vsock_proxy/Android.bp
new file mode 100644
index 0000000..0a2f572
--- /dev/null
+++ b/common/frontend/socket_vsock_proxy/Android.bp
@@ -0,0 +1,44 @@
+//
+// 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_vsock_proxy",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "cuttlefish_auto_resources",
+ "libbase",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ "libcuttlefish_strings",
+ "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_vsock_proxy/main.cpp b/common/frontend/socket_vsock_proxy/main.cpp
new file mode 100644
index 0000000..ac421a3
--- /dev/null
+++ b/common/frontend/socket_vsock_proxy/main.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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 <set>
+#include <thread>
+#include <glog/logging.h>
+#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
+
+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");
+DEFINE_uint32(vsock_guest_cid, 0, "Guest identifier");
+
+namespace {
+// Sends packets, Shutdown(SHUT_WR) on destruction
+class SocketSender {
+ public:
+ explicit SocketSender(cvd::SharedFD socket) : socket_{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_{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 SocketToVsock(SocketReceiver socket_receiver,
+ SocketSender vsock_sender) {
+ while (true) {
+ auto packet = Packet::MakeData();
+ socket_receiver.Recv(&packet);
+ if (packet.empty() || vsock_sender.SendAll(packet) < 0) {
+ break;
+ }
+ }
+ LOG(INFO) << "Socket to vsock exiting";
+}
+
+void VsockToSocket(SocketSender socket_sender,
+ SocketReceiver vsock_receiver) {
+ auto packet = Packet::MakeData();
+ while (true) {
+ vsock_receiver.Recv(&packet);
+ CHECK(packet.IsData());
+ if (packet.empty()) {
+ break;
+ }
+ if (socket_sender.SendAll(packet) < 0) {
+ break;
+ }
+ }
+ LOG(INFO) << "Vsock 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(cvd::SharedFD vsock,
+ cvd::SharedFD socket) {
+ auto socket_to_vsock =
+ std::thread(SocketToVsock, SocketReceiver{socket}, SocketSender{vsock});
+ VsockToSocket(SocketSender{socket}, SocketReceiver{vsock});
+ socket_to_vsock.join();
+}
+
+#ifdef CUTTLEFISH_HOST
+[[noreturn]] void host() {
+ LOG(INFO) << "starting server on " << FLAGS_tcp_port << " for vsock port "
+ << FLAGS_vsock_port;
+ auto server = cvd::SharedFD::SocketLocalServer(FLAGS_tcp_port, SOCK_STREAM);
+ CHECK(server->IsOpen()) << "Could not start server on " << FLAGS_tcp_port;
+ LOG(INFO) << "Accepting client connections";
+ int last_failure_reason = 0;
+ while (true) {
+ auto client_socket = cvd::SharedFD::Accept(*server);
+ CHECK(client_socket->IsOpen()) << "error creating client socket";
+ cvd::SharedFD vsock_socket = cvd::SharedFD::VsockClient(
+ FLAGS_vsock_guest_cid, FLAGS_vsock_port, SOCK_STREAM);
+ if (vsock_socket->IsOpen()) {
+ last_failure_reason = 0;
+ LOG(INFO) << "Connected to vsock:" << FLAGS_vsock_guest_cid << ":"
+ << FLAGS_vsock_port;
+ } else {
+ // Don't log if the previous connection failed with the same error
+ if (last_failure_reason != vsock_socket->GetErrno()) {
+ last_failure_reason = vsock_socket->GetErrno();
+ LOG(ERROR) << "Unable to connect to vsock server: "
+ << vsock_socket->StrError();
+ }
+ continue;
+ }
+ auto thread = std::thread(HandleConnection, std::move(vsock_socket),
+ std::move(client_socket));
+ thread.detach();
+ }
+}
+
+#else
+cvd::SharedFD OpenSocketConnection() {
+ while (true) {
+ auto sock = cvd::SharedFD::SocketLocalClient(FLAGS_tcp_port, SOCK_STREAM);
+ if (sock->IsOpen()) {
+ return sock;
+ }
+ LOG(WARNING) << "could not connect on port " << FLAGS_tcp_port
+ << ". sleeping for 1 second";
+ sleep(1);
+ }
+}
+
+bool socketErrorIsRecoverable(int error) {
+ std::set<int> unrecoverable{EACCES, EAFNOSUPPORT, EINVAL, EPROTONOSUPPORT};
+ return unrecoverable.find(error) == unrecoverable.end();
+}
+
+[[noreturn]] static void SleepForever() {
+ while (true) {
+ sleep(std::numeric_limits<unsigned int>::max());
+ }
+}
+
+[[noreturn]] void guest() {
+ LOG(INFO) << "Starting guest mainloop";
+ LOG(INFO) << "starting server on " << FLAGS_vsock_port;
+ cvd::SharedFD vsock;
+ do {
+ vsock = cvd::SharedFD::VsockServer(FLAGS_vsock_port, SOCK_STREAM);
+ if (!vsock->IsOpen() && !socketErrorIsRecoverable(vsock->GetErrno())) {
+ LOG(ERROR) << "Could not open vsock socket: " << vsock->StrError();
+ SleepForever();
+ }
+ } while (!vsock->IsOpen());
+ CHECK(vsock->IsOpen()) << "Could not start server on " << FLAGS_vsock_port;
+ while (true) {
+ LOG(INFO) << "waiting for vsock connection";
+ auto vsock_client = cvd::SharedFD::Accept(*vsock);
+ CHECK(vsock_client->IsOpen()) << "error creating vsock socket";
+ LOG(INFO) << "vsock socket accepted";
+ auto client = OpenSocketConnection();
+ CHECK(client->IsOpen()) << "error connecting to guest client";
+ auto thread = std::thread(HandleConnection, std::move(vsock_client),
+ std::move(client));
+ thread.detach();
+ }
+}
+
+#endif
+} // namespace
+
+int main(int argc, char* argv[]) {
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ CHECK(FLAGS_tcp_port != 0) << "Must specify -tcp_port flag";
+ CHECK(FLAGS_vsock_port != 0) << "Must specify -vsock_port flag";
+#ifdef CUTTLEFISH_HOST
+ CHECK(FLAGS_vsock_guest_cid != 0) << "Must specify -vsock_guest_cid flag";
+ host();
+#else
+ guest();
+#endif
+}
diff --git a/common/libs/constants/ril.h b/common/libs/constants/ril.h
new file mode 100644
index 0000000..b9c3e8a
--- /dev/null
+++ b/common/libs/constants/ril.h
@@ -0,0 +1,31 @@
+#pragma once
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(CUTTLEFISH_HOST)
+#define CF_PROPERTY_PREFIX "androidboot"
+#else
+#define CF_PROPERTY_PREFIX "ro.boot"
+#endif
+
+#define CUTTLEFISH_RIL_ADDR_PROPERTY CF_PROPERTY_PREFIX ".cuttlefish_ril_addr"
+#define CUTTLEFISH_RIL_GATEWAY_PROPERTY \
+ CF_PROPERTY_PREFIX ".cuttlefish_ril_gateway"
+#define CUTTLEFISH_RIL_DNS_PROPERTY CF_PROPERTY_PREFIX ".cuttlefish_ril_dns"
+#define CUTTLEFISH_RIL_BROADCAST_PROPERTY \
+ CF_PROPERTY_PREFIX ".cuttlefish_ril_broadcast"
+#define CUTTLEFISH_RIL_PREFIXLEN_PROPERTY \
+ CF_PROPERTY_PREFIX ".cuttlefish_ril_prefixlen"
diff --git a/common/libs/fs/shared_fd.cpp b/common/libs/fs/shared_fd.cpp
index 93d2027..6ca2826 100644
--- a/common/libs/fs/shared_fd.cpp
+++ b/common/libs/fs/shared_fd.cpp
@@ -253,8 +253,8 @@
new FileInstance(epoll_create1(flags), errno));
}
-inline bool SharedFD::SocketPair(int domain, int type, int protocol,
- SharedFD* fd0, SharedFD* fd1) {
+bool SharedFD::SocketPair(int domain, int type, int protocol,
+ SharedFD* fd0, SharedFD* fd1) {
int fds[2];
int rval = socketpair(domain, type, protocol, fds);
if (rval != -1) {
@@ -287,6 +287,10 @@
}
}
+SharedFD SharedFD::ErrorFD(int error) {
+ return SharedFD(std::shared_ptr<FileInstance>(new FileInstance(-1, error)));
+}
+
SharedFD SharedFD::SocketLocalClient(const char* name, bool abstract,
int in_type) {
struct sockaddr_un addr;
@@ -297,8 +301,7 @@
return rval;
}
if (rval->Connect(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
return rval;
}
@@ -314,8 +317,7 @@
}
if (rval->Connect(reinterpret_cast<const sockaddr*>(&addr),
sizeof addr) < 0) {
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
return rval;
}
@@ -333,19 +335,16 @@
int n = 1;
if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
if(rval->Bind(reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) < 0) {
LOG(ERROR) << "Bind failed " << rval->StrError();
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
if (type == SOCK_STREAM) {
if (rval->Listen(4) < 0) {
LOG(ERROR) << "Listen failed " << rval->StrError();
- return SharedFD(std::shared_ptr<FileInstance>(
- new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
}
return rval;
@@ -368,13 +367,11 @@
int n = 1;
if (rval->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1) {
LOG(ERROR) << "SetSockOpt failed " << rval->StrError();
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
if (rval->Bind(reinterpret_cast<sockaddr*>(&addr), addrlen) == -1) {
LOG(ERROR) << "Bind failed; name=" << name << ": " << rval->StrError();
- return SharedFD(
- std::shared_ptr<FileInstance>(new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
/* Only the bottom bits are really the socket type; there are flags too. */
@@ -385,8 +382,7 @@
// Follows the default from socket_local_server
if (rval->Listen(1) == -1) {
LOG(ERROR) << "Listen failed: " << rval->StrError();
- return SharedFD(std::shared_ptr<FileInstance>(
- new FileInstance(-1, rval->GetErrno())));
+ return SharedFD::ErrorFD(rval->GetErrno());
}
}
@@ -399,4 +395,43 @@
return rval;
}
+SharedFD SharedFD::VsockServer(unsigned int port, int type) {
+ auto vsock = cvd::SharedFD::Socket(AF_VSOCK, type, 0);
+ if (!vsock->IsOpen()) {
+ return vsock;
+ }
+ sockaddr_vm addr{};
+ addr.svm_family = AF_VSOCK;
+ addr.svm_port = port;
+ addr.svm_cid = VMADDR_CID_ANY;
+ auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
+ if (vsock->Bind(casted_addr, sizeof(addr)) == -1) {
+ LOG(ERROR) << "Bind failed (" << vsock->StrError() << ")";
+ return SharedFD::ErrorFD(vsock->GetErrno());
+ }
+ if (type == SOCK_STREAM) {
+ if (vsock->Listen(4) < 0) {
+ LOG(ERROR) << "Listen failed (" << vsock->StrError() << ")";
+ return SharedFD::ErrorFD(vsock->GetErrno());
+ }
+ }
+ return vsock;
+}
+
+SharedFD SharedFD::VsockClient(unsigned int cid, unsigned int port, int type) {
+ auto vsock = cvd::SharedFD::Socket(AF_VSOCK, type, 0);
+ if (!vsock->IsOpen()) {
+ return vsock;
+ }
+ sockaddr_vm addr{};
+ addr.svm_family = AF_VSOCK;
+ addr.svm_port = port;
+ addr.svm_cid = cid;
+ auto casted_addr = reinterpret_cast<sockaddr*>(&addr);
+ if (vsock->Connect(casted_addr, sizeof(addr)) == -1) {
+ return SharedFD::ErrorFD(vsock->GetErrno());
+ }
+ return vsock;
+}
+
} // namespace cvd
diff --git a/common/libs/fs/shared_fd.h b/common/libs/fs/shared_fd.h
index 33afee2..b811f4a 100644
--- a/common/libs/fs/shared_fd.h
+++ b/common/libs/fs/shared_fd.h
@@ -40,6 +40,7 @@
#include <unistd.h>
#include "common/libs/auto_resources/auto_resources.h"
+#include "vm_sockets.h"
/**
* Classes to to enable safe access to files.
@@ -155,6 +156,8 @@
static SharedFD SocketLocalServer(int port, int type);
static SharedFD SocketSeqPacketServer(const char* name, mode_t mode);
static SharedFD SocketSeqPacketClient(const char* name);
+ static SharedFD VsockServer(unsigned int port, int type);
+ static SharedFD VsockClient(unsigned int cid, unsigned int port, int type);
static SharedFD TimerFD(int clock, int flags);
bool operator==(const SharedFD& rhs) const { return value_ == rhs.value_; }
@@ -176,6 +179,8 @@
cvd::FileInstance& operator*() { return *value_; }
private:
+ static SharedFD ErrorFD(int error);
+
std::shared_ptr<FileInstance> value_;
};
@@ -531,7 +536,7 @@
/* Methods that need both a fully defined SharedFD and a fully defined
FileInstance. */
-SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) {}
+inline SharedFD::SharedFD() : value_(FileInstance::ClosedInstance()) {}
} // namespace cvd
diff --git a/common/libs/fs/shared_select.h b/common/libs/fs/shared_select.h
index 20bfb5a..d103f1d 100644
--- a/common/libs/fs/shared_select.h
+++ b/common/libs/fs/shared_select.h
@@ -48,7 +48,7 @@
}
void Clr(const SharedFD& in) {
- value_.insert(in);
+ value_.erase(in);
}
bool IsSet(const SharedFD& in) const {
diff --git a/common/libs/fs/vm_sockets.h b/common/libs/fs/vm_sockets.h
new file mode 100644
index 0000000..151f676
--- /dev/null
+++ b/common/libs/fs/vm_sockets.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** Copied and modified from bionic/libc/kernel/uapi/linux/vm_sockets.h
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _UAPI_VM_SOCKETS_H
+#define _UAPI_VM_SOCKETS_H
+#include <linux/socket.h>
+#define SO_VM_SOCKETS_BUFFER_SIZE 0
+#define SO_VM_SOCKETS_BUFFER_MIN_SIZE 1
+#define SO_VM_SOCKETS_BUFFER_MAX_SIZE 2
+#define SO_VM_SOCKETS_PEER_HOST_VM_ID 3
+#define SO_VM_SOCKETS_TRUSTED 5
+#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
+#define SO_VM_SOCKETS_NONBLOCK_TXRX 7
+#define VMADDR_CID_ANY - 1U
+#define VMADDR_PORT_ANY - 1U
+#define VMADDR_CID_HYPERVISOR 0
+#define VMADDR_CID_RESERVED 1
+#define VMADDR_CID_HOST 2
+#define VM_SOCKETS_INVALID_VERSION - 1U
+#define VM_SOCKETS_VERSION_EPOCH(_v) (((_v) & 0xFF000000) >> 24)
+#define VM_SOCKETS_VERSION_MAJOR(_v) (((_v) & 0x00FF0000) >> 16)
+#define VM_SOCKETS_VERSION_MINOR(_v) (((_v) & 0x0000FFFF))
+struct sockaddr_vm {
+ __kernel_sa_family_t svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) - sizeof(unsigned short) - sizeof(unsigned int) - sizeof(unsigned int)];
+};
+#define IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
+#endif
+#endif
diff --git a/common/libs/tcp_socket/tcp_socket.cpp b/common/libs/tcp_socket/tcp_socket.cpp
index dc244b5..64da7d7 100644
--- a/common/libs/tcp_socket/tcp_socket.cpp
+++ b/common/libs/tcp_socket/tcp_socket.cpp
@@ -28,9 +28,9 @@
using cvd::ServerSocket;
ClientSocket::ClientSocket(int port)
- : fd_(SharedFD::SocketLocalClient(port, SOCK_STREAM)) {}
+ : fd_(SharedFD::SocketLocalClient(port, SOCK_STREAM)) {}
-cvd::Message ClientSocket::RecvAny(size_t length) {
+cvd::Message ClientSocket::RecvAny(std::size_t length) {
Message buf(length);
auto read_count = fd_->Read(buf.data(), buf.size());
if (read_count < 0) {
@@ -45,7 +45,7 @@
return other_side_closed_;
}
-cvd::Message ClientSocket::Recv(size_t length) {
+cvd::Message ClientSocket::Recv(std::size_t length) {
Message buf(length);
ssize_t total_read = 0;
while (total_read < static_cast<ssize_t>(length)) {
@@ -66,12 +66,14 @@
return buf;
}
-ssize_t ClientSocket::Send(const uint8_t* data, std::size_t size) {
+ssize_t ClientSocket::SendNoSignal(const uint8_t* data, std::size_t size) {
std::lock_guard<std::mutex> lock(send_lock_);
ssize_t written{};
while (written < static_cast<ssize_t>(size)) {
- if (!fd_->IsOpen()) { LOG(ERROR) << "fd_ is closed"; }
- auto just_written = fd_->Write(data + written, size - written);
+ if (!fd_->IsOpen()) {
+ LOG(ERROR) << "fd_ is closed";
+ }
+ auto just_written = fd_->Send(data + written, size - written, MSG_NOSIGNAL);
if (just_written <= 0) {
LOG(INFO) << "Couldn't write to client: " << strerror(errno);
{
@@ -85,8 +87,8 @@
return written;
}
-ssize_t ClientSocket::Send(const Message& message) {
- return Send(&message[0], message.size());
+ssize_t ClientSocket::SendNoSignal(const Message& message) {
+ return SendNoSignal(&message[0], message.size());
}
ServerSocket::ServerSocket(int port)
@@ -103,3 +105,29 @@
}
return ClientSocket{client};
}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
+ msg->push_back(b);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
+ const std::uint16_t n = htons(s);
+ auto p = reinterpret_cast<const std::uint8_t*>(&n);
+ msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
+ const std::uint32_t n = htonl(w);
+ auto p = reinterpret_cast<const std::uint8_t*>(&n);
+ msg->insert(msg->end(), p, p + sizeof n);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::int32_t w) {
+ std::uint32_t u{};
+ std::memcpy(&u, &w, sizeof u);
+ AppendInNetworkByteOrder(msg, u);
+}
+
+void cvd::AppendInNetworkByteOrder(Message* msg, const std::string& str) {
+ msg->insert(msg->end(), str.begin(), str.end());
+}
diff --git a/common/libs/tcp_socket/tcp_socket.h b/common/libs/tcp_socket/tcp_socket.h
index 889d09b..e484f48 100644
--- a/common/libs/tcp_socket/tcp_socket.h
+++ b/common/libs/tcp_socket/tcp_socket.h
@@ -49,13 +49,14 @@
Message Recv(std::size_t length);
// RecvAny will receive whatever is available.
// An empty message returned indicates error or close.
- Message RecvAny(size_t length);
- ssize_t Send(const std::uint8_t* data, std::size_t size);
- ssize_t Send(const Message& message);
+ Message RecvAny(std::size_t length);
+ // Sends are called with MSG_NOSIGNAL to suppress SIGPIPE
+ ssize_t SendNoSignal(const std::uint8_t* data, std::size_t size);
+ ssize_t SendNoSignal(const Message& message);
template <std::size_t N>
- ssize_t Send(const std::uint8_t (&data)[N]) {
- return Send(data, N);
+ ssize_t SendNoSignal(const std::uint8_t (&data)[N]) {
+ return SendNoSignal(data, N);
}
bool closed() const;
@@ -83,4 +84,25 @@
cvd::SharedFD fd_;
};
+void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b);
+void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s);
+void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w);
+void AppendInNetworkByteOrder(Message* msg, const std::int32_t w);
+void AppendInNetworkByteOrder(Message* msg, const std::string& str);
+
+inline void AppendToMessage(Message*) {}
+
+template <typename T, typename... Ts>
+void AppendToMessage(Message* msg, T v, Ts... vals) {
+ AppendInNetworkByteOrder(msg, v);
+ AppendToMessage(msg, vals...);
+}
+
+template <typename... Ts>
+Message CreateMessage(Ts... vals) {
+ Message m;
+ AppendToMessage(&m, vals...);
+ return m;
+}
+
} // namespace cvd
diff --git a/common/libs/threads/Android.bp b/common/libs/threads/Android.bp
index dece9ae..7c89060 100644
--- a/common/libs/threads/Android.bp
+++ b/common/libs/threads/Android.bp
@@ -22,5 +22,6 @@
"cuttlefish_time",
"libbase",
],
+ cpp_std: "c++17",
defaults: ["cuttlefish_host_only"],
}
diff --git a/common/libs/threads/cuttlefish_thread_test.cpp b/common/libs/threads/cuttlefish_thread_test.cpp
index 513262e..e63d60c 100644
--- a/common/libs/threads/cuttlefish_thread_test.cpp
+++ b/common/libs/threads/cuttlefish_thread_test.cpp
@@ -52,10 +52,8 @@
void Run() {
{
- ScopedThread thread_a(
- MutexTestThunker<void*()>::call<&MutexTest::FastThread>, this);
- ScopedThread thread_b(
- MutexTestThunker<void*()>::call<&MutexTest::SlowThread>, this);
+ ScopedThread thread_a(cvd::thunk<void, &MutexTest::FastThread>, this);
+ ScopedThread thread_b(cvd::thunk<void, &MutexTest::SlowThread>, this);
}
LOG(INFO) << "MutexTest: completed at stage "
<< stage_
@@ -64,9 +62,6 @@
}
protected:
- template <typename F> struct MutexTestThunker :
- ThunkerBase<void, MutexTest, F>{};
-
void* FastThread() {
mutex_.Lock();
CHECK(busy_ == NULL);
@@ -111,11 +106,11 @@
void Run() {
{
ScopedThread thread_s(
- Thunker<void*()>::call<&NotifyOneTest::SignalThread>, this);
+ cvd::thunk<void, &NotifyOneTest::SignalThread>, this);
ScopedThread thread_w1(
- Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
+ cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
ScopedThread thread_w2(
- Thunker<void*()>::call<&NotifyOneTest::WaitThread>, this);
+ cvd::thunk<void, &NotifyOneTest::WaitThread>, this);
}
LOG(INFO) << "NotifyOneTest: completed, signalled "
<< signalled_
@@ -124,9 +119,6 @@
}
protected:
- template <typename F> struct Thunker :
- ThunkerBase<void, NotifyOneTest, F>{};
-
void* SignalThread() {
SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
mutex_.Lock();
@@ -164,20 +156,17 @@
void Run() {
{
ScopedThread thread_s(
- Thunker<void*()>::call<&NotifyAllTest::SignalThread>, this);
+ cvd::thunk<void, &NotifyAllTest::SignalThread>, this);
ScopedThread thread_w1(
- Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
+ cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
ScopedThread thread_w2(
- Thunker<void*()>::call<&NotifyAllTest::WaitThread>, this);
+ cvd::thunk<void, &NotifyAllTest::WaitThread>, this);
}
printf("NotifyAllTest: completed, signalled %d (%s)\n",
signalled_, (signalled_ == 2) ? "PASSED" : "FAILED");
}
protected:
- template <typename F> struct Thunker :
- ThunkerBase<void, NotifyAllTest, F>{};
-
void* SignalThread() {
SleepUntil(MonotonicTimePoint::Now() + Milliseconds(100));
mutex_.Lock();
@@ -211,18 +200,15 @@
start_ = MonotonicTimePoint::Now();
{
ScopedThread thread_s(
- Thunker<void*()>::call<&WaitUntilTest::SignalThread>, this);
+ cvd::thunk<void, &WaitUntilTest::SignalThread>, this);
ScopedThread thread_w2(
- Thunker<void*()>::call<&WaitUntilTest::WaitThread>, this);
+ cvd::thunk<void, &WaitUntilTest::WaitThread>, this);
}
printf("WaitUntilTest: completed, stage %d (%s)\n",
stage_, (stage_ == FINISHED) ? "PASSED" : "FAILED");
}
protected:
- template <typename F> struct Thunker :
- ThunkerBase<void, WaitUntilTest, F>{};
-
void* SignalThread() {
SleepUntil(start_ + Milliseconds(200));
mutex_.Lock();
diff --git a/common/libs/threads/thunkers.h b/common/libs/threads/thunkers.h
index e496839..bb97174 100644
--- a/common/libs/threads/thunkers.h
+++ b/common/libs/threads/thunkers.h
@@ -16,148 +16,39 @@
#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
#define CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
-template <typename HalType, typename Impl, typename F> struct ThunkerBase;
+namespace cvd {
+namespace internal {
-/* Handle varying number of arguments with a bunch of specializations.
- * The C++11 dream is:
- *
- * template <typename HalType, typename Impl, typename R, typename... Args>
- * struct ThunkerBase<HalType, Impl, R(Args...)> {
- * template <R (Impl::*MemFn)(Args...)>
- * static R call(HalType* in, Args... args) {
- * return (reinterpret_cast<Impl*>(in)->*MemFn)(args...);
- * }
- * };
- */
+template <typename HalType, typename F>
+struct ThunkerImpl;
-template <typename HalType, typename Impl, typename R>
-struct ThunkerBase<HalType, Impl, R()> {
- template <R (Impl::*MemFn)()>
- static R call(HalType* in) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)();
- }
-
- template <R (Impl::*MemFn)() const>
- static R call(const HalType* in) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)();
+template <typename HalType, typename Impl, typename R, typename... Args>
+struct ThunkerImpl<HalType, R (Impl::*)(Args...)> {
+ template <R (Impl::*MemFn)(Args...)>
+ static R call(HalType* in, Args... args) {
+ return (reinterpret_cast<Impl*>(in)->*MemFn)(args...);
}
};
-template <typename HalType, typename Impl, typename R, typename T1>
-struct ThunkerBase<HalType, Impl, R(T1)> {
- template <R (Impl::*MemFn)(T1)>
- static R call(HalType* in, T1 t1) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1);
- }
-
- template <R (Impl::*MemFn)(T1) const>
- static R call(const HalType* in, T1 t1) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1);
+template <typename HalType, typename Impl, typename R, typename... Args>
+struct ThunkerImpl<HalType, R (Impl::*)(Args...) const> {
+ template <R (Impl::*MemFn)(Args...) const>
+ static R call(const HalType* in, Args... args) {
+ return (reinterpret_cast<const Impl*>(in)->*MemFn)(args...);
}
};
-template <typename HalType, typename Impl, typename R, typename T1, typename T2>
-struct ThunkerBase<HalType, Impl, R(T1, T2)> {
- template <R (Impl::*MemFn)(T1, T2)>
- static R call(HalType* in, T1 t1, T2 t2) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2);
- }
-
- template <R (Impl::*MemFn)(T1, T2) const>
- static R call(const HalType* in, T1 t1, T2 t2) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2);
- }
+template <typename HalType, auto MemFunc>
+struct Thunker {
+ static constexpr auto call =
+ ThunkerImpl<HalType, decltype(MemFunc)>::template call<MemFunc>;
};
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3)> {
- template <R (Impl::*MemFn)(T1, T2, T3)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3);
- }
+} // namespace internal
- template <R (Impl::*MemFn)(T1, T2, T3) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3);
- }
-};
+template <typename HalType, auto MemFunc>
+constexpr auto thunk = internal::Thunker<HalType, MemFunc>::call;
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3, typename T4>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4)> {
- template <R (Impl::*MemFn)(T1, T2, T3, T4)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4);
- }
+} // namespace cvd
- template <R (Impl::*MemFn)(T1, T2, T3, T4) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4);
- }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3, typename T4, typename T5>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5)> {
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5);
- }
-
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5);
- }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3, typename T4, typename T5, typename T6>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6)> {
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6);
- }
-
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6);
- }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3, typename T4, typename T5, typename T6,
- typename T7>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6, T7)> {
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(t1, t2, t3, t4, t5, t6, t7);
- }
-
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6,
- T7 t7) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(
- t1, t2, t3, t4, t5, t6, t7);
- }
-};
-
-template <typename HalType, typename Impl, typename R, typename T1,
- typename T2, typename T3, typename T4, typename T5, typename T6,
- typename T7, typename T8>
-struct ThunkerBase<HalType, Impl, R(T1, T2, T3, T4, T5, T6, T7, T8)> {
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7, T8)>
- static R call(HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7,
- T8 t8) {
- return (reinterpret_cast<Impl*>(in)->*MemFn)(
- t1, t2, t3, t4, t5, t6, t7, t8);
- }
-
- template <R (Impl::*MemFn)(T1, T2, T3, T4, T5, T6, T7, T8) const>
- static R call(const HalType* in, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6,
- T7 t7, T8 t8) {
- return (reinterpret_cast<const Impl*>(in)->*MemFn)(
- t1, t2, t3, t4, t5, t6, t7, t8);
- }
-};
-#endif // CUTTLEFISH_COMMON_COMMON_LIBS_THREADS_THUNKERS_H_
+#endif
diff --git a/common/libs/utils/Android.bp b/common/libs/utils/Android.bp
index 4785fad..1551515 100644
--- a/common/libs/utils/Android.bp
+++ b/common/libs/utils/Android.bp
@@ -21,6 +21,7 @@
"size_utils.cpp",
"files.cpp",
"users.cpp",
+ "network.cpp",
],
header_libs: [
"cuttlefish_glog",
@@ -28,6 +29,15 @@
shared_libs: [
"libbase",
"libcuttlefish_fs",
+ "cuttlefish_auto_resources",
],
defaults: ["cuttlefish_host_and_guest"],
}
+
+cc_test {
+ name: "cuttlefish_simulated_buffer_test",
+ srcs: ["simulated_buffer_test.cpp"],
+ shared_libs: ["libcuttlefish_utils"],
+ gtest: true,
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/common/libs/utils/files.cpp b/common/libs/utils/files.cpp
index 654d971..432e8da 100644
--- a/common/libs/utils/files.cpp
+++ b/common/libs/utils/files.cpp
@@ -20,6 +20,7 @@
#include <array>
#include <climits>
+#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
@@ -27,6 +28,11 @@
namespace cvd {
+bool FileExists(const std::string& path) {
+ struct stat st;
+ return stat(path.c_str(), &st) == 0;
+}
+
bool FileHasContent(const std::string& path) {
return FileSize(path) > 0;
}
@@ -67,4 +73,9 @@
return st.st_size;
}
+bool RemoveFile(const std::string& file) {
+ LOG(INFO) << "Removing " << file;
+ return remove(file.c_str()) == 0;
+}
+
} // namespace cvd
diff --git a/common/libs/utils/files.h b/common/libs/utils/files.h
index ce689ac..d05501d 100644
--- a/common/libs/utils/files.h
+++ b/common/libs/utils/files.h
@@ -20,9 +20,11 @@
#include <string>
namespace cvd {
+bool FileExists(const std::string& path);
bool FileHasContent(const std::string& path);
bool DirectoryExists(const std::string& path);
off_t FileSize(const std::string& path);
+bool RemoveFile(const std::string& file);
// The returned value may contain .. or . if these are present in the path
// argument.
diff --git a/common/libs/utils/network.cpp b/common/libs/utils/network.cpp
new file mode 100644
index 0000000..f4943ed
--- /dev/null
+++ b/common/libs/utils/network.cpp
@@ -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.
+ */
+
+#include "common/libs/utils/network.h"
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <string.h>
+
+#include "common/libs/glog/logging.h"
+
+namespace cvd {
+namespace {
+// This should be the size of virtio_net_hdr_v1, from linux/virtio_net.h, but
+// the version of that header that ships with android in Pie does not include
+// that struct (it was added in Q).
+// This is what that struct looks like:
+// struct virtio_net_hdr_v1 {
+// u8 flags;
+// u8 gso_type;
+// u16 hdr_len;
+// u16 gso_size;
+// u16 csum_start;
+// u16 csum_offset;
+// u16 num_buffers;
+// };
+static constexpr int SIZE_OF_VIRTIO_NET_HDR_V1 = 12;
+} // namespace
+
+SharedFD OpenTapInterface(const std::string& interface_name) {
+ constexpr auto TUNTAP_DEV = "/dev/net/tun";
+
+ auto tap_fd = SharedFD::Open(TUNTAP_DEV, O_RDWR | O_NONBLOCK);
+ if (!tap_fd->IsOpen()) {
+ LOG(ERROR) << "Unable to open tun device: " << tap_fd->StrError();
+ return tap_fd;
+ }
+
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+ strncpy(ifr.ifr_name, interface_name.c_str(), IFNAMSIZ);
+
+ int err = tap_fd->Ioctl(TUNSETIFF, &ifr);
+ if (err < 0) {
+ LOG(ERROR) << "Unable to connect to " << interface_name
+ << " tap interface: " << tap_fd->StrError();
+ tap_fd->Close();
+ return cvd::SharedFD();
+ }
+
+ // The interface's configuration may have been modified or just not set
+ // correctly on creation. While qemu checks this and enforces the right
+ // configuration, crosvm does not, so it needs to be set before it's passed to
+ // it.
+ tap_fd->Ioctl(TUNSETOFFLOAD,
+ reinterpret_cast<void*>(TUN_F_CSUM | TUN_F_UFO | TUN_F_TSO4 |
+ TUN_F_TSO6));
+ int len = SIZE_OF_VIRTIO_NET_HDR_V1;
+ tap_fd->Ioctl(TUNSETVNETHDRSZ, &len);
+
+ return tap_fd;
+}
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/common/libs/utils/network.h
similarity index 62%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to common/libs/utils/network.h
index b6a037b..7b856e4 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/common/libs/utils/network.h
@@ -1,6 +1,5 @@
-#pragma once
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-#include "hwcomposer_common.h"
+#include <string>
+
+#include "common/libs/fs/shared_fd.h"
namespace cvd {
-
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
-} // namespace cvd
+// Creates, or connects to if it already exists, a tap network interface. The
+// user needs CAP_NET_ADMIN to create such interfaces or be the owner to connect
+// to one.
+SharedFD OpenTapInterface(const std::string& interface_name);
+}
\ No newline at end of file
diff --git a/guest/hals/audio/simulated_buffer.h b/common/libs/utils/simulated_buffer.h
similarity index 100%
rename from guest/hals/audio/simulated_buffer.h
rename to common/libs/utils/simulated_buffer.h
diff --git a/common/libs/utils/simulated_buffer_test.cpp b/common/libs/utils/simulated_buffer_test.cpp
new file mode 100644
index 0000000..c3fbe49
--- /dev/null
+++ b/common/libs/utils/simulated_buffer_test.cpp
@@ -0,0 +1,442 @@
+/*
+ * 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 "common/libs/utils/simulated_buffer.h"
+#include <gtest/gtest.h>
+
+using cvd::time::MonotonicTimePoint;
+using cvd::time::MonotonicTimePointFactory;
+using cvd::time::Seconds;
+using cvd::time::Milliseconds;
+using cvd::time::Nanoseconds;
+using cvd::time::kNanosecondsPerSecond;
+
+class MockTimepointFactory : public MonotonicTimePointFactory {
+ public:
+ virtual void FetchCurrentTime(MonotonicTimePoint* dest) const override {
+ *dest = system_time_;
+ }
+
+ void SetTime(const MonotonicTimePoint& in) {
+ system_time_ = in;
+ }
+
+ protected:
+ MonotonicTimePoint system_time_;
+};
+
+template <typename T> class MockSimulatedBuffer : public T {
+ public:
+ MockSimulatedBuffer(
+ int64_t sample_rate,
+ int64_t capacity,
+ MockTimepointFactory* factory) :
+ T(sample_rate, capacity, factory),
+ factory_(factory) { }
+
+ void FetchCurrentTime(MonotonicTimePoint* dest) const {
+ return factory_->FetchCurrentTime(dest);
+ }
+
+ void SleepUntilTime(const MonotonicTimePoint& tick) {
+ factory_->SetTime(tick);
+ }
+
+ protected:
+ // Save a redundant pointer to avoid downcasting
+ MockTimepointFactory* factory_;
+};
+
+static const int64_t kItemRate = 48000;
+static const int64_t kBufferCapacity = 4800;
+
+class SimulatedBufferTest : public ::testing::Test {
+ public:
+ MockTimepointFactory clock;
+ MockSimulatedBuffer<SimulatedBufferBase> buffer;
+
+ SimulatedBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(SimulatedBufferTest, TimeMocking) {
+ // Ensure that the mocked clock starts at the epoch.
+ MonotonicTimePoint epoch_time;
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(epoch_time, actual_time);
+
+ // Ensure that sleeping works
+ MonotonicTimePoint test_time = actual_time + Seconds(10);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+
+ // Try one more sleep to make sure that time moves forward
+ test_time += Seconds(5);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+}
+
+TEST_F(SimulatedBufferTest, ItemScaling) {
+ // Make certain that we start at item 0
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+
+ // Make certain that the expected number of items appear in 1 second
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ MonotonicTimePoint test_time = actual_time + Seconds(1);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(kItemRate, buffer.GetCurrentItemNum());
+
+ // Sleep an additional 10 seconds to make certain that the item numbers
+ // increment
+ test_time += Seconds(10);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(11 * kItemRate, buffer.GetCurrentItemNum());
+
+ // Make certain that partial seconds work
+ test_time += Milliseconds(1500);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(12.5 * kItemRate, buffer.GetCurrentItemNum());
+
+ // Make certain that we don't get new items when paused
+ buffer.SetPaused(true);
+ test_time += Seconds(10);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(12.5 * kItemRate, buffer.GetCurrentItemNum());
+
+ // Make certain that we start getting items when pausing stops
+ buffer.SetPaused(false);
+ test_time += Milliseconds(500);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(13 * kItemRate, buffer.GetCurrentItemNum());
+}
+
+TEST_F(SimulatedBufferTest, ItemSleeping) {
+ // See if sleeping on an time causes the right amount of time to pass
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+ MonotonicTimePoint base_time;
+ buffer.FetchCurrentTime(&base_time);
+
+ // Wait for 1500ms worth of samples
+ buffer.SleepUntilItem(kItemRate * 1500 / 1000);
+ EXPECT_EQ(kItemRate * 1500 / 1000, buffer.GetCurrentItemNum());
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(1500, Milliseconds(actual_time - base_time).count());
+
+ // Now wait again for more samples
+ buffer.SleepUntilItem(kItemRate * 2500 / 1000);
+ EXPECT_EQ(kItemRate * 2500 / 1000, buffer.GetCurrentItemNum());
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(2500, Milliseconds(actual_time - base_time).count());
+}
+
+class OutputBufferTest : public ::testing::Test {
+ public:
+ MockTimepointFactory clock;
+ MockSimulatedBuffer<SimulatedOutputBuffer> buffer;
+
+ OutputBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(OutputBufferTest, NonBlockingQueueing) {
+ int64_t half_buffer = kBufferCapacity / 2;
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+
+ // Filling half of the buffer should not block
+ MonotonicTimePoint test_time;
+ buffer.FetchCurrentTime(&test_time);
+ EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, false));
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(half_buffer, buffer.GetOutputBufferSize());
+
+ // Filling all but one entry of the buffer should not block
+ EXPECT_EQ(half_buffer - 1,
+ buffer.AddToOutputBuffer(half_buffer - 1, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity - 1, buffer.GetOutputBufferSize());
+
+ // Filling the entire buffer should not block
+ EXPECT_EQ(1, buffer.AddToOutputBuffer(half_buffer, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(actual_time, test_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // The buffer should reject additional data but not block
+ EXPECT_EQ(0, buffer.AddToOutputBuffer(half_buffer, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // One quarter of the buffer should drain in the expected time
+ Nanoseconds quarter_drain_time(
+ kBufferCapacity / 4 * kNanosecondsPerSecond / kItemRate);
+ test_time += quarter_drain_time;
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(actual_time, test_time);
+ EXPECT_EQ(kBufferCapacity * 3 / 4, buffer.GetOutputBufferSize());
+
+ // The buffer should now accept new data without blocking
+ EXPECT_EQ(kBufferCapacity / 4,
+ buffer.AddToOutputBuffer(half_buffer, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // Now that the buffer is full it should reject additional data but
+ // not block
+ EXPECT_EQ(0, buffer.AddToOutputBuffer(half_buffer, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // Wait for 3/4 of the buffer to drain
+ test_time += Nanoseconds(3 * quarter_drain_time.count());
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity / 4, buffer.GetOutputBufferSize());
+
+ // The entire buffer should drain on schedule
+ test_time += Nanoseconds(quarter_drain_time.count() - 1);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(1, buffer.GetOutputBufferSize());
+ test_time += Nanoseconds(1);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+ // It should be possible to fill the buffer in a single shot
+ EXPECT_EQ(kBufferCapacity,
+ buffer.AddToOutputBuffer(kBufferCapacity, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // The buffer shouldn't accept additional data but shouldn't block
+ EXPECT_EQ(0, buffer.AddToOutputBuffer(1, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // The buffer should underflow sanely
+ test_time += Nanoseconds(6 * quarter_drain_time.count());
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+ // The underflow shouldn't increase the buffer's capacity
+ EXPECT_EQ(kBufferCapacity,
+ buffer.AddToOutputBuffer(kBufferCapacity + 1, false));
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+}
+
+TEST_F(OutputBufferTest, BlockingQueueing) {
+ int64_t half_buffer = kBufferCapacity / 2;
+
+ // Check the initial setup
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+ MonotonicTimePoint test_time;
+ buffer.FetchCurrentTime(&test_time);
+
+ // Filling half the buffer works without blocking
+ EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(half_buffer, buffer.GetOutputBufferSize());
+
+ // Filling all but one entry of the buffer also works without blocking
+ EXPECT_EQ(half_buffer - 1,
+ buffer.AddToOutputBuffer(half_buffer - 1, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity - 1, buffer.GetOutputBufferSize());
+
+ // Putting the last sample into the buffer doesn't block
+ EXPECT_EQ(1, buffer.AddToOutputBuffer(1, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // Putting more data into the buffer causes blocking
+ EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+ Nanoseconds half_drain_time(
+ ((kBufferCapacity / 2) * kNanosecondsPerSecond + kItemRate - 1) /
+ kItemRate);
+ Nanoseconds quarter_drain_time(half_drain_time.count() / 2);
+ test_time += half_drain_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // The buffer drains as expected
+ test_time += quarter_drain_time;
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity * 3 / 4, buffer.GetOutputBufferSize());
+
+ // Overfilling the drained buffer also causes blocking
+ EXPECT_EQ(half_buffer, buffer.AddToOutputBuffer(half_buffer, true));
+ test_time += quarter_drain_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // The buffer drains on schedule
+ test_time += Nanoseconds(half_drain_time.count() * 2 - 1);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(1, buffer.GetOutputBufferSize());
+ test_time += Nanoseconds(1);
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetOutputBufferSize());
+
+ // It's possible to fill the entire output buffer in 1 shot without blocking
+ EXPECT_EQ(kBufferCapacity,
+ buffer.AddToOutputBuffer(kBufferCapacity, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+
+ // Adding a single extra sample causes some blocking
+ EXPECT_EQ(1, buffer.AddToOutputBuffer(1, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_LT(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity, buffer.GetOutputBufferSize());
+}
+
+class InputBufferTest : public ::testing::Test {
+ public:
+ MockTimepointFactory clock;
+ MockSimulatedBuffer<SimulatedInputBuffer> buffer;
+
+ InputBufferTest() : buffer(kItemRate, kBufferCapacity, &clock) { }
+};
+
+TEST_F(InputBufferTest, NonBlockingInput) {
+ Nanoseconds quarter_fill_time(kBufferCapacity / 4 * kNanosecondsPerSecond /
+ kItemRate);
+ // Verify that the buffer starts empty
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Wait for 1/4 of the buffer to fill
+ MonotonicTimePoint test_time = actual_time + quarter_fill_time;
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Verify that we can read the samples in two groups
+ EXPECT_EQ(kBufferCapacity / 8,
+ buffer.RemoveFromInputBuffer(kBufferCapacity / 8, false));
+ EXPECT_EQ(kBufferCapacity / 8,
+ buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+
+ // Verify that there are no samples left and that we did not block
+ EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+
+ // Verify that the buffer fills on schedule
+ test_time += Nanoseconds(4 * quarter_fill_time.count() - 1);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(kBufferCapacity - 1,
+ buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+ test_time += Nanoseconds(1);
+ buffer.SleepUntilTime(test_time);
+ EXPECT_EQ(1, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Verify that the buffer overflows as expected
+ test_time += Nanoseconds(5 * quarter_fill_time.count());
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity / 4, buffer.GetLostInputItems());
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ EXPECT_EQ(kBufferCapacity,
+ buffer.RemoveFromInputBuffer(2 * kBufferCapacity, false));
+ EXPECT_EQ(0, buffer.RemoveFromInputBuffer(kBufferCapacity, false));
+}
+
+TEST_F(InputBufferTest, BlockingInput) {
+ Nanoseconds quarter_fill_time(kBufferCapacity / 4 * kNanosecondsPerSecond /
+ kItemRate);
+ // Verify that the buffer starts empty
+ EXPECT_EQ(0, buffer.GetCurrentItemNum());
+ MonotonicTimePoint actual_time;
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Wait for 1/4 of the buffer to fill
+ MonotonicTimePoint test_time = actual_time + quarter_fill_time;
+ EXPECT_EQ(kBufferCapacity / 4,
+ buffer.RemoveFromInputBuffer(kBufferCapacity / 4, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Verify that the buffer fills on schedule
+ test_time += Nanoseconds(4 * quarter_fill_time.count());
+ EXPECT_EQ(kBufferCapacity,
+ buffer.RemoveFromInputBuffer(kBufferCapacity, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+
+ // Verify that the buffer overflows as expected
+ test_time += Nanoseconds(5 * quarter_fill_time.count());
+ buffer.SleepUntilTime(test_time);
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+ EXPECT_EQ(kBufferCapacity / 4, buffer.GetLostInputItems());
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+ EXPECT_EQ(kBufferCapacity,
+ buffer.RemoveFromInputBuffer(kBufferCapacity, true));
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+
+ // Verify that reads bigger than the buffer work as expected
+ test_time += Nanoseconds(8 * quarter_fill_time.count());
+ EXPECT_EQ(kBufferCapacity * 2,
+ buffer.RemoveFromInputBuffer(kBufferCapacity * 2, true));
+ EXPECT_EQ(0, buffer.GetLostInputItems());
+ buffer.FetchCurrentTime(&actual_time);
+ EXPECT_EQ(test_time, actual_time);
+}
diff --git a/common/libs/utils/subprocess.cpp b/common/libs/utils/subprocess.cpp
index 7f7b961..773a56c 100644
--- a/common/libs/utils/subprocess.cpp
+++ b/common/libs/utils/subprocess.cpp
@@ -16,17 +16,77 @@
#include "common/libs/utils/subprocess.h"
+#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
+#include <map>
+#include <set>
+
#include <glog/logging.h>
namespace {
-pid_t subprocess_impl(const char* const* command, const char* const* envp) {
+// If a redirected-to file descriptor was already closed, it's possible that
+// some inherited file descriptor duped to this file descriptor and the redirect
+// would override that. This function makes sure that doesn't happen.
+bool validate_redirects(
+ const std::map<cvd::Subprocess::StdIOChannel, int>& redirects,
+ const std::map<cvd::SharedFD, int>& inherited_fds) {
+ // Add the redirected IO channels to a set as integers. This allows converting
+ // the enum values into integers instead of the other way around.
+ std::set<int> int_redirects;
+ for (const auto& entry: redirects) {
+ int_redirects.insert(static_cast<int>(entry.first));
+ }
+ for (const auto& entry: inherited_fds) {
+ auto dupped_fd = entry.second;
+ if (int_redirects.count(dupped_fd)) {
+ LOG(ERROR) << "Requested redirect of fd(" << dupped_fd
+ << ") conflicts with inherited FD.";
+ return false;
+ }
+ }
+ return true;
+}
+
+void do_redirects(const std::map<cvd::Subprocess::StdIOChannel, int>& redirects) {
+ for (const auto& entry: redirects) {
+ auto std_channel = static_cast<int>(entry.first);
+ auto fd = entry.second;
+ TEMP_FAILURE_RETRY(dup2(fd, std_channel));
+ }
+}
+
+cvd::Subprocess subprocess_impl(
+ const char* const* command,
+ const char* const* envp,
+ const std::map<cvd::Subprocess::StdIOChannel, int>& redirects,
+ const std::map<cvd::SharedFD, int>& inherited_fds,
+ bool with_control_socket) {
+ // The parent socket will get closed on the child on the call to exec, the
+ // child socket will be closed on the parent when this function returns and no
+ // references to the fd are left
+ cvd::SharedFD parent_socket, child_socket;
+ if (with_control_socket) {
+ if (!cvd::SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &parent_socket,
+ &child_socket)) {
+ LOG(ERROR) << "Unable to create control socket pair: " << strerror(errno);
+ return cvd::Subprocess(-1, {});
+ }
+ // Remove FD_CLOEXEC from the child socket, ensure the parent has it
+ child_socket->Fcntl(F_SETFD, 0);
+ parent_socket->Fcntl(F_SETFD, FD_CLOEXEC);
+ }
+
+ if (!validate_redirects(redirects, inherited_fds)) {
+ return cvd::Subprocess(-1, {});
+ }
+
pid_t pid = fork();
if (!pid) {
+ do_redirects(redirects);
int rval;
// If envp is NULL, the current process's environment is used as the
// environment of the child process. To force an empty emvironment for
@@ -51,7 +111,7 @@
while (command[i]) {
LOG(INFO) << command[i++];
}
- return pid;
+ return cvd::Subprocess(pid, parent_socket);
}
std::vector<const char*> ToCharPointers(
@@ -66,6 +126,27 @@
} // namespace
namespace cvd {
+Subprocess::Subprocess(Subprocess&& subprocess)
+ : pid_(subprocess.pid_),
+ started_(subprocess.started_),
+ control_socket_(subprocess.control_socket_) {
+ // Make sure the moved object no longer controls this subprocess
+ subprocess.pid_ = -1;
+ subprocess.started_ = false;
+ subprocess.control_socket_ = SharedFD();
+}
+
+Subprocess& Subprocess::operator=(Subprocess&& other) {
+ pid_ = other.pid_;
+ started_ = other.started_;
+ control_socket_ = other.control_socket_;
+
+ other.pid_ = -1;
+ other.started_ = false;
+ other.control_socket_ = SharedFD();
+ return *this;
+}
+
int Subprocess::Wait() {
if (pid_ < 0) {
LOG(ERROR)
@@ -108,9 +189,14 @@
}
Command::~Command() {
+ // Close all inherited file descriptors
for(const auto& entry: inherited_fds_) {
close(entry.second);
}
+ // Close all redirected file descriptors
+ for (const auto& entry: redirects_) {
+ close(entry.second);
+ }
}
bool Command::BuildParameter(std::stringstream* stream, SharedFD shared_fd) {
@@ -128,13 +214,33 @@
return true;
}
-Subprocess Command::Start() const {
+bool Command::RedirectStdIO(cvd::Subprocess::StdIOChannel channel,
+ cvd::SharedFD shared_fd){
+ if (!shared_fd->IsOpen()) {
+ return false;
+ }
+ if (redirects_.count(channel)) {
+ LOG(ERROR) << "Attempted multiple redirections of fd: "
+ << static_cast<int>(channel);
+ return false;
+ }
+ auto dup_fd = shared_fd->UNMANAGED_Dup();
+ if (dup_fd < 0) {
+ return false;
+ }
+ redirects_[channel] = dup_fd;
+ return true;
+}
+
+Subprocess Command::Start(bool with_control_socket) const {
auto cmd = ToCharPointers(command_);
if (use_parent_env_) {
- return Subprocess(subprocess_impl(cmd.data(), NULL));
+ return subprocess_impl(cmd.data(), nullptr, redirects_, inherited_fds_,
+ with_control_socket);
} else {
auto envp = ToCharPointers(env_);
- return Subprocess(subprocess_impl(cmd.data(), envp.data()));
+ return subprocess_impl(cmd.data(), envp.data(), redirects_, inherited_fds_,
+ with_control_socket);
}
}
diff --git a/common/libs/utils/subprocess.h b/common/libs/utils/subprocess.h
index 814325b..a7485c2 100644
--- a/common/libs/utils/subprocess.h
+++ b/common/libs/utils/subprocess.h
@@ -29,9 +29,19 @@
// It's an error to wait twice for the same subprocess.
class Subprocess {
public:
- Subprocess(pid_t pid) : pid_(pid), started_(pid > 0) {}
- Subprocess(Subprocess&&) = default;
+ enum class StdIOChannel {
+ kStdIn = 0,
+ kStdOut = 1,
+ kStdErr = 2,
+ };
+
+ Subprocess(pid_t pid, SharedFD control)
+ : pid_(pid), started_(pid > 0), control_socket_(control) {}
+ // The default implementation won't do because we need to reset the pid of the
+ // moved object.
+ Subprocess(Subprocess&&);
~Subprocess() = default;
+ Subprocess& operator=(Subprocess&&);
// Waits for the subprocess to complete. Returns zero if completed
// successfully, non-zero otherwise.
int Wait();
@@ -41,6 +51,9 @@
// fork() succeeded or not, it says nothing about exec or successful
// completion of the command, that's what Wait is for.
bool Started() const {return started_;}
+ SharedFD control_socket() {
+ return control_socket_;
+ }
private:
// Copy is disabled to avoid waiting twice for the same pid (the first wait
@@ -49,7 +62,8 @@
Subprocess(const Subprocess&) = delete;
Subprocess& operator=(const Subprocess&) = delete;
pid_t pid_ = -1;
- bool started_= false;
+ bool started_ = false;
+ SharedFD control_socket_;
};
// An executable command. Multiple subprocesses can be started from the same
@@ -102,13 +116,25 @@
}
return false;
}
- // Starts execution of the command. This method can be called multiple times,
- // effectively staring multiple (possibly concurrent) instances.
- Subprocess Start() const;
+ // Redirects the standard IO of the command.
+ bool RedirectStdIO(Subprocess::StdIOChannel channel, cvd::SharedFD shared_fd);
+
+ // Starts execution of the command. This method can be called multiple times,
+ // effectively staring multiple (possibly concurrent) instances. If
+ // with_control_socket is true the returned Subprocess instance will have a
+ // sharedFD that enables communication with the child process.
+ Subprocess Start(bool with_control_socket = false) const;
+
+ std::string GetShortName() const {
+ // This is safe because the constructor guarantees the name of the binary to
+ // be at index 0 on the vector
+ return command_[0];
+ }
private:
std::vector<std::string> command_;
std::map<cvd::SharedFD, int> inherited_fds_{};
+ std::map<Subprocess::StdIOChannel, int> redirects_{};
bool use_parent_env_ = true;
std::vector<std::string> env_{};
};
diff --git a/common/vsoc/lib/e2e_test_region_layout.cpp b/common/vsoc/lib/e2e_test_region_layout.cpp
index 67a5435..9a787bd 100644
--- a/common/vsoc/lib/e2e_test_region_layout.cpp
+++ b/common/vsoc/lib/e2e_test_region_layout.cpp
@@ -38,10 +38,6 @@
const char* E2EUnfindableRegionLayout::region_name = "e2e_must_not_exist";
-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/wifi_exchange_layout.cpp b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
similarity index 73%
rename from common/vsoc/lib/wifi_exchange_layout.cpp
rename to common/vsoc/lib/managed_e2e_test_region_layout.cpp
index 12e9577..1cc794b 100644
--- a/common/vsoc/lib/wifi_exchange_layout.cpp
+++ b/common/vsoc/lib/managed_e2e_test_region_layout.cpp
@@ -13,12 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "common/vsoc/shm/wifi_exchange_layout.h"
+
+#include "common/vsoc/shm/managed_e2e_test_region_layout.h"
namespace vsoc {
namespace layout {
-namespace wifi {
-const char* WifiExchangeLayout::region_name = "wifi_exchange";
-} // namespace wifi
+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/ril_layout.cpp b/common/vsoc/lib/ril_layout.cpp
deleted file mode 100644
index 89f45f1..0000000
--- a/common/vsoc/lib/ril_layout.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * 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/ril_layout.h"
-
-namespace vsoc {
-namespace layout {
-namespace ril {
-
-const char* RilLayout::region_name = "ril";
-}
-} // namespace layout
-} // namespace vsoc
diff --git a/common/vsoc/lib/ril_region_view.cpp b/common/vsoc/lib/ril_region_view.cpp
deleted file mode 100644
index ad19ac3..0000000
--- a/common/vsoc/lib/ril_region_view.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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 <arpa/inet.h>
-
-#include <mutex>
-
-#include "common/vsoc/lib/ril_region_view.h"
-
-namespace vsoc {
-namespace ril {
-
-const char* RilRegionView::address_and_prefix_length() const {
- static char buffer[sizeof(data().ipaddr) + 3]{}; // <ipaddr>/dd
- if (buffer[0] == '\0') {
- snprintf(buffer, sizeof(buffer), "%s/%d", data().ipaddr, data().prefixlen);
- }
- return &buffer[0];
-}
-
-} // namespace ril
-} // namespace vsoc
diff --git a/common/vsoc/lib/ril_region_view.h b/common/vsoc/lib/ril_region_view.h
deleted file mode 100644
index 0f7fe84..0000000
--- a/common/vsoc/lib/ril_region_view.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#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/ril_layout.h"
-
-namespace vsoc {
-namespace ril {
-class RilRegionView
- : public vsoc::TypedRegionView<
- RilRegionView,
- vsoc::layout::ril::RilLayout> {
- public:
- // returns a string with '<ip>/<prefix_len>' like this: 192.168.99.2/30
- const char* address_and_prefix_length() const;
-};
-} // namespace ril
-} // namespace vsoc
diff --git a/common/vsoc/lib/vsoc_memory.cpp b/common/vsoc/lib/vsoc_memory.cpp
index 807b2d9..017ab13 100644
--- a/common/vsoc/lib/vsoc_memory.cpp
+++ b/common/vsoc/lib/vsoc_memory.cpp
@@ -29,10 +29,9 @@
#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/ril_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 "common/vsoc/shm/wifi_exchange_layout.h"
#include "uapi/vsoc_shm.h"
@@ -173,8 +172,6 @@
/* managed_by */ layout::gralloc::GrallocManagerLayout::region_name),
ValidateAndBuildLayout<layout::socket_forward::SocketForwardLayout>(7,
7),
- ValidateAndBuildLayout<layout::wifi::WifiExchangeLayout>(2, 2),
- ValidateAndBuildLayout<layout::ril::RilLayout>(2, 2),
ValidateAndBuildLayout<layout::e2e_test::E2EPrimaryTestRegionLayout>(1,
1),
ValidateAndBuildLayout<layout::e2e_test::E2ESecondaryTestRegionLayout>(
diff --git a/common/vsoc/lib/wifi_exchange_view.cpp b/common/vsoc/lib/wifi_exchange_view.cpp
deleted file mode 100644
index 23dcd09..0000000
--- a/common/vsoc/lib/wifi_exchange_view.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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/wifi_exchange_view.h"
-
-#include <algorithm>
-#include <string>
-
-#include <linux/if_ether.h>
-#include "common/vsoc/lib/circqueue_impl.h"
-
-namespace vsoc {
-namespace wifi {
-
-intptr_t WifiExchangeView::Send(const void* buffer, intptr_t length) {
-#ifdef CUTTLEFISH_HOST
- return data()->guest_ingress.Write(this, static_cast<const char*>(buffer),
- length);
-#else
- return data()->guest_egress.Write(this, static_cast<const char*>(buffer),
- length);
-#endif
-}
-
-intptr_t WifiExchangeView::Recv(void* buffer, intptr_t max_length) {
-#ifdef CUTTLEFISH_HOST
- return data()->guest_egress.Read(this, static_cast<char*>(buffer),
- max_length);
-#else
- return data()->guest_ingress.Read(this, static_cast<char*>(buffer),
- max_length);
-#endif
-}
-
-void WifiExchangeView::SetGuestMACAddress(
- const WifiExchangeView::MacAddress& mac_address) {
- std::copy(std::begin(mac_address),
- std::end(mac_address),
- std::begin(data()->guest_mac_address));
-}
-
-WifiExchangeView::MacAddress WifiExchangeView::GetGuestMACAddress() {
- WifiExchangeView::MacAddress ret;
- std::copy(std::begin(data()->guest_mac_address),
- std::end(data()->guest_mac_address),
- std::begin(ret));
- return ret;
-}
-
-void WifiExchangeView::SetHostMACAddress(
- const WifiExchangeView::MacAddress& mac_address) {
- std::copy(std::begin(mac_address),
- std::end(mac_address),
- std::begin(data()->host_mac_address));
-}
-
-WifiExchangeView::MacAddress WifiExchangeView::GetHostMACAddress() {
- WifiExchangeView::MacAddress ret;
- std::copy(std::begin(data()->host_mac_address),
- std::end(data()->host_mac_address),
- std::begin(ret));
- return ret;
-}
-
-// static
-bool WifiExchangeView::ParseMACAddress(const std::string& s,
- WifiExchangeView::MacAddress* mac) {
- char dummy;
- // This is likely to always be true, but better safe than sorry
- static_assert(std::tuple_size<WifiExchangeView::MacAddress>::value == 6,
- "Mac address size has changed");
- if (sscanf(s.c_str(),
- "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
- &(*mac)[0],
- &(*mac)[1],
- &(*mac)[2],
- &(*mac)[3],
- &(*mac)[4],
- &(*mac)[5],
- &dummy) != 6) {
- return false;
- }
- return true;
-}
-
-// static
-std::string WifiExchangeView::MacAddressToString(
- const WifiExchangeView::MacAddress& mac) {
- char buffer[3 * mac.size()];
- // This is likely to always be true, but better safe than sorry
- static_assert(std::tuple_size<WifiExchangeView::MacAddress>::value == 6,
- "Mac address size has changed");
- snprintf(buffer,
- sizeof(buffer),
- "%02x:%02x:%02x:%02x:%02x:%02x",
- mac[0],
- mac[1],
- mac[2],
- mac[3],
- mac[4],
- mac[5]);
- return std::string(buffer);
-}
-
-} // namespace wifi
-} // namespace vsoc
diff --git a/common/vsoc/lib/wifi_exchange_view.h b/common/vsoc/lib/wifi_exchange_view.h
deleted file mode 100644
index 5230a8e..0000000
--- a/common/vsoc/lib/wifi_exchange_view.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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 <array>
-#include <memory>
-
-#include "common/vsoc/lib/typed_region_view.h"
-#include "common/vsoc/shm/wifi_exchange_layout.h"
-#include "uapi/vsoc_shm.h"
-
-namespace vsoc {
-namespace wifi {
-
-class WifiExchangeView
- : public vsoc::TypedRegionView<
- WifiExchangeView,
- vsoc::layout::wifi::WifiExchangeLayout> {
- public:
- using MacAddress = std::array<
- uint8_t,
- sizeof(vsoc::layout::wifi::WifiExchangeLayout::guest_mac_address)>;
-
- // Send netlink packet to peer.
- // returns true, if operation was successful.
- intptr_t Send(const void* buffer, intptr_t length);
-
- // Receive netlink packet from peer.
- // Returns number of bytes read, or negative value, if failed.
- intptr_t Recv(void* buffer, intptr_t max_length);
-
- // Set guest MAC address.
- void SetGuestMACAddress(const MacAddress& mac_address);
- MacAddress GetGuestMACAddress();
-
- // Set host MAC address.
- void SetHostMACAddress(const MacAddress& mac_address);
- MacAddress GetHostMACAddress();
-
- void SetConfigReady();
- void WaitConfigReady();
-
- static bool ParseMACAddress(const std::string &s, MacAddress *mac);
- static std::string MacAddressToString(const MacAddress& mac);
-};
-
-} // namespace wifi
-} // namespace vsoc
diff --git a/common/vsoc/shm/e2e_test_region_layout.h b/common/vsoc/shm/e2e_test_region_layout.h
index 2c2670b..f3ae615 100644
--- a/common/vsoc/shm/e2e_test_region_layout.h
+++ b/common/vsoc/shm/e2e_test_region_layout.h
@@ -43,7 +43,7 @@
* 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 {
+enum E2ETestStage : uint32_t {
// No tests have passed
E2E_STAGE_NONE = 0,
// This side has finished writing its pattern to the region
@@ -163,23 +163,6 @@
};
ASSERT_SHM_COMPATIBLE(E2EUnfindableRegionLayout);
-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/lock.h b/common/vsoc/shm/lock.h
index cc86add..2049ba8 100644
--- a/common/vsoc/shm/lock.h
+++ b/common/vsoc/shm/lock.h
@@ -23,7 +23,13 @@
// 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>
@@ -62,7 +68,7 @@
if (lock_.compare_exchange_strong(expected, Sides::OurSide)) {
return;
}
- _mm_pause();
+ _pause();
}
}
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/ril_layout.h b/common/vsoc/shm/ril_layout.h
deleted file mode 100644
index 33348e2..0000000
--- a/common/vsoc/shm/ril_layout.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#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"
-
-// Memory layout for the ril hal region
-
-namespace vsoc {
-namespace layout {
-namespace ril {
-
-struct RilLayout : public RegionLayout {
- static constexpr size_t layout_size = 4 * 16 + 4;
- static const char* region_name;
-
- char ipaddr[16]; // xxx.xxx.xxx.xxx\0 = 16 bytes
- char gateway[16];
- char dns[16];
- char broadcast[16];
- uint32_t prefixlen;
-};
-ASSERT_SHM_COMPATIBLE(RilLayout);
-} // namespace ril
-} // namespace layout
-} // namespace vsoc
diff --git a/common/vsoc/shm/wifi_exchange_layout.h b/common/vsoc/shm/wifi_exchange_layout.h
deleted file mode 100644
index df4659e..0000000
--- a/common/vsoc/shm/wifi_exchange_layout.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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 "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 wifi {
-
-struct WifiExchangeLayout : public RegionLayout {
- static constexpr size_t layout_size = 2 * CircularPacketQueue<16, 8192>::layout_size + 12;
-
- // Traffic originating from host that proceeds towards guest.
- CircularPacketQueue<16, 8192> guest_ingress;
- // Traffic originating from guest that proceeds towards host.
- CircularPacketQueue<16, 8192> guest_egress;
-
- // Desired MAC address for guest device.
- uint8_t guest_mac_address[6];
- // MAC address of host device.
- uint8_t host_mac_address[6];
-
- static const char* region_name;
-};
-
-ASSERT_SHM_COMPATIBLE(WifiExchangeLayout);
-
-} // namespace wifi
-} // namespace layout
-} // namespace vsoc
diff --git a/guest/Android.bp b/guest/Android.bp
index 28eec2c..ba07c2e 100644
--- a/guest/Android.bp
+++ b/guest/Android.bp
@@ -16,4 +16,5 @@
subdirs = [
"commands",
"hals/health",
+ "hals/hwcomposer",
]
diff --git a/guest/commands/Android.bp b/guest/commands/Android.bp
index 8264fcc..5c7c3c2 100644
--- a/guest/commands/Android.bp
+++ b/guest/commands/Android.bp
@@ -14,4 +14,5 @@
// limitations under the License.
subdirs = [
+ "vsock_logcat",
]
diff --git a/guest/commands/rename_netiface/Android.bp b/guest/commands/rename_netiface/Android.bp
new file mode 100644
index 0000000..b93a465
--- /dev/null
+++ b/guest/commands/rename_netiface/Android.bp
@@ -0,0 +1,26 @@
+//
+// 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: "rename_netiface",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "cuttlefish_net",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/rename_netiface/Android.mk b/guest/commands/rename_netiface/Android.mk
deleted file mode 100644
index 67e97f4..0000000
--- a/guest/commands/rename_netiface/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := rename_netiface
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := cuttlefish_net
-LOCAL_C_INCLUDES := device/google/cuttlefish_common
-LOCAL_MULTILIB := first
-LOCAL_VENDOR_MODULE := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/guest/commands/setup_wifi/Android.bp b/guest/commands/setup_wifi/Android.bp
new file mode 100644
index 0000000..ec89852
--- /dev/null
+++ b/guest/commands/setup_wifi/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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: "setup_wifi",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "cuttlefish_net",
+ "cuttlefish_auto_resources",
+ "libbase",
+ "liblog",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/setup_wifi/Android.mk b/guest/commands/setup_wifi/Android.mk
deleted file mode 100644
index b920483..0000000
--- a/guest/commands/setup_wifi/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := setup_wifi
-LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := cuttlefish_net cuttlefish_auto_resources libbase liblog
-LOCAL_C_INCLUDES := device/google/cuttlefish_common
-LOCAL_MULTILIB := first
-LOCAL_VENDOR_MODULE := true
-
-include $(BUILD_EXECUTABLE)
diff --git a/guest/commands/setup_wifi/main.cpp b/guest/commands/setup_wifi/main.cpp
index cd60e99..e3840ff 100644
--- a/guest/commands/setup_wifi/main.cpp
+++ b/guest/commands/setup_wifi/main.cpp
@@ -93,7 +93,7 @@
return 0;
}
-int main(int argc, char** argv) {
+int main() {
int renamed_eth0 = RenameNetwork("eth0", "buried_eth0");
if (renamed_eth0 != 0) {
return renamed_eth0;
diff --git a/guest/commands/vsock_logcat/Android.bp b/guest/commands/vsock_logcat/Android.bp
new file mode 100644
index 0000000..52575d7
--- /dev/null
+++ b/guest/commands/vsock_logcat/Android.bp
@@ -0,0 +1,36 @@
+//
+// 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: "vsock_logcat",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libcuttlefish_fs",
+ "libcuttlefish_utils",
+ "liblog",
+ ],
+ static_libs: [
+ "libgflags",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ defaults: ["cuttlefish_guest_only"]
+}
diff --git a/guest/commands/vsock_logcat/main.cpp b/guest/commands/vsock_logcat/main.cpp
new file mode 100644
index 0000000..51d97e3
--- /dev/null
+++ b/guest/commands/vsock_logcat/main.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "vsock_logcat"
+
+#include <string.h>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <cutils/properties.h>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+
+DEFINE_uint32(port, property_get_int32("ro.boot.vsock_logcat_port", 0),
+ "VSOCK port to send logcat output to");
+DEFINE_uint32(cid, 2, "VSOCK CID to send logcat output to");
+DEFINE_string(pipe_name, "/dev/cf_logcat_pipe",
+ "The path for the named pipe logcat will write to");
+
+namespace {
+
+constexpr char kLogcatExitMsg[] = "\nDetected exit of logcat process\n\n";
+
+class ServiceStatus {
+ public:
+ static const char* kServiceStatusProperty;
+ static const char* kStatusStarted;
+ static const char* kStatusFailed;
+
+ ServiceStatus() {
+ // This can fail if the property isn't set (the first time it runs), so
+ // ignore the result.
+ property_get(kServiceStatusProperty, status_, kStatusStarted);
+ }
+
+ bool Set(const char* status) {
+ auto ret = property_set(kServiceStatusProperty, status);
+
+ if (ret == 0) {
+ strcpy(status_, status);
+ return true;
+ }
+ return false;
+ }
+
+ const char* Get() { return status_; }
+
+ private:
+ char status_[PROP_VALUE_MAX];
+};
+
+const char* ServiceStatus::kServiceStatusProperty = "vendor.vsock_logcat_status";
+const char* ServiceStatus::kStatusStarted = "started";
+const char* ServiceStatus::kStatusFailed = "failed";
+
+void LogFailed(const std::string& msg, ServiceStatus* status) {
+ // Only log if status is not failed, ensuring it logs once per fail.
+ if (strcmp(status->Get(), ServiceStatus::kStatusFailed) != 0) {
+ LOG(ERROR) << msg;
+ std::ofstream kmsg;
+ kmsg.open("/dev/kmsg");
+ kmsg << LOG_TAG << ": " << msg;
+ kmsg << "";
+ kmsg.close();
+ }
+ auto ret = status->Set(ServiceStatus::kStatusFailed);
+ if (!ret) {
+ LOG(ERROR) << "Unable to set value of property: "
+ << ServiceStatus::kServiceStatusProperty;
+ }
+}
+} // namespace
+
+int main(int argc, char** argv) {
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ CHECK(FLAGS_port != 0) << "Port flag is required";
+
+ ServiceStatus status;
+
+ auto log_fd = cvd::SharedFD::VsockClient(FLAGS_cid, FLAGS_port, SOCK_STREAM);
+ if (!log_fd->IsOpen()) {
+ std::ostringstream msg;
+ msg << "Unable to connect to vsock:" << FLAGS_cid << ":" << FLAGS_port
+ << ": " << log_fd->StrError();
+ LogFailed(msg.str(), &status);
+ return 1;
+ }
+ auto ret = status.Set(ServiceStatus::kStatusStarted);
+ if (!ret) {
+ LOG(ERROR) << "Unable to set value of property: "
+ << ServiceStatus::kServiceStatusProperty;
+ }
+
+ if (cvd::FileExists(FLAGS_pipe_name)) {
+ LOG(WARNING) << "The file " << FLAGS_pipe_name << " already exists. Deleting...";
+ cvd::RemoveFile(FLAGS_pipe_name);
+ }
+ auto pipe = mkfifo(FLAGS_pipe_name.c_str(), 0600);
+ if (pipe != 0) {
+ LOG(FATAL) << "unable to create pipe: " << strerror(errno);
+ }
+ property_set("vendor.ser.cf-logcat", FLAGS_pipe_name.c_str());
+ while (1) {
+ auto conn = cvd::SharedFD::Open(FLAGS_pipe_name.c_str(), O_RDONLY);
+ while (conn->IsOpen()) {
+ char buff[4096];
+ auto read = conn->Read(buff, sizeof(buff));
+ if (read) {
+ log_fd->Write(buff, read);
+ } else {
+ conn->Close();
+ }
+ }
+ log_fd->Write(kLogcatExitMsg, sizeof(kLogcatExitMsg) - 1);
+ }
+}
diff --git a/guest/hals/audio/Android.bp b/guest/hals/audio/Android.bp
new file mode 100644
index 0000000..93a7455
--- /dev/null
+++ b/guest/hals/audio/Android.bp
@@ -0,0 +1,23 @@
+// 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: "audio.primary.cutf",
+ relative_install_path: "hw",
+ defaults: ["cuttlefish_guest_only"],
+ vendor: true,
+ srcs: ["audio_hw.c"],
+ cflags: ["-Wno-unused-parameter"],
+ shared_libs: ["libcutils", "libhardware", "liblog", "libtinyalsa"],
+}
diff --git a/guest/hals/audio/audio_hw.c b/guest/hals/audio/audio_hw.c
new file mode 100644
index 0000000..058d620
--- /dev/null
+++ b/guest/hals/audio/audio_hw.c
@@ -0,0 +1,1634 @@
+/*
+ * Copyright (C) 2012 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.
+ *
+ * This code was forked from device/generic/goldfish/audio/audio_hw.c
+ *
+ * At the time of forking, the code was identical except that a fallback
+ * to a legacy HAL which does not use ALSA was removed, and the dependency
+ * on libdl was also removed.
+ */
+
+#define LOG_TAG "audio_hw_generic"
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <cutils/str_parms.h>
+
+#include <hardware/hardware.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+#include <tinyalsa/asoundlib.h>
+
+#define PCM_CARD 0
+#define PCM_DEVICE 0
+
+
+#define OUT_PERIOD_MS 15
+#define OUT_PERIOD_COUNT 4
+
+#define IN_PERIOD_MS 15
+#define IN_PERIOD_COUNT 4
+
+struct generic_audio_device {
+ struct audio_hw_device device; // Constant after init
+ pthread_mutex_t lock;
+ bool mic_mute; // Proteced by this->lock
+ struct mixer* mixer; // Proteced by this->lock
+};
+
+/* If not NULL, this is a pointer to the fallback module.
+ * This really is the original goldfish audio device /dev/eac which we will use
+ * if no alsa devices are detected.
+ */
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
+static int adev_get_microphones(const audio_hw_device_t *dev,
+ struct audio_microphone_characteristic_t *mic_array,
+ size_t *mic_count);
+
+
+typedef struct audio_vbuffer {
+ pthread_mutex_t lock;
+ uint8_t * data;
+ size_t frame_size;
+ size_t frame_count;
+ size_t head;
+ size_t tail;
+ size_t live;
+} audio_vbuffer_t;
+
+static int audio_vbuffer_init (audio_vbuffer_t * audio_vbuffer, size_t frame_count,
+ size_t frame_size) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ audio_vbuffer->frame_size = frame_size;
+ audio_vbuffer->frame_count = frame_count;
+ size_t bytes = frame_count * frame_size;
+ audio_vbuffer->data = calloc(bytes, 1);
+ if (!audio_vbuffer->data) {
+ return -ENOMEM;
+ }
+ audio_vbuffer->head = 0;
+ audio_vbuffer->tail = 0;
+ audio_vbuffer->live = 0;
+ pthread_mutex_init (&audio_vbuffer->lock, (const pthread_mutexattr_t *) NULL);
+ return 0;
+}
+
+static int audio_vbuffer_destroy (audio_vbuffer_t * audio_vbuffer) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ free(audio_vbuffer->data);
+ pthread_mutex_destroy(&audio_vbuffer->lock);
+ return 0;
+}
+
+static int audio_vbuffer_live (audio_vbuffer_t * audio_vbuffer) {
+ if (!audio_vbuffer) {
+ return -EINVAL;
+ }
+ pthread_mutex_lock (&audio_vbuffer->lock);
+ int live = audio_vbuffer->live;
+ pthread_mutex_unlock (&audio_vbuffer->lock);
+ return live;
+}
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+static size_t audio_vbuffer_write (audio_vbuffer_t * audio_vbuffer, const void * buffer, size_t frame_count) {
+ size_t frames_written = 0;
+ pthread_mutex_lock (&audio_vbuffer->lock);
+
+ while (frame_count != 0) {
+ int frames = 0;
+ if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) {
+ frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head);
+ } else if (audio_vbuffer->head < audio_vbuffer->tail) {
+ frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head));
+ } else {
+ // Full
+ break;
+ }
+ memcpy(&audio_vbuffer->data[audio_vbuffer->head*audio_vbuffer->frame_size],
+ &((uint8_t*)buffer)[frames_written*audio_vbuffer->frame_size],
+ frames*audio_vbuffer->frame_size);
+ audio_vbuffer->live += frames;
+ frames_written += frames;
+ frame_count -= frames;
+ audio_vbuffer->head = (audio_vbuffer->head + frames) % audio_vbuffer->frame_count;
+ }
+
+ pthread_mutex_unlock (&audio_vbuffer->lock);
+ return frames_written;
+}
+
+static size_t audio_vbuffer_read (audio_vbuffer_t * audio_vbuffer, void * buffer, size_t frame_count) {
+ size_t frames_read = 0;
+ pthread_mutex_lock (&audio_vbuffer->lock);
+
+ while (frame_count != 0) {
+ int frames = 0;
+ if (audio_vbuffer->live == audio_vbuffer->frame_count ||
+ audio_vbuffer->tail > audio_vbuffer->head) {
+ frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail);
+ } else if (audio_vbuffer->tail < audio_vbuffer->head) {
+ frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail);
+ } else {
+ break;
+ }
+ memcpy(&((uint8_t*)buffer)[frames_read*audio_vbuffer->frame_size],
+ &audio_vbuffer->data[audio_vbuffer->tail*audio_vbuffer->frame_size],
+ frames*audio_vbuffer->frame_size);
+ audio_vbuffer->live -= frames;
+ frames_read += frames;
+ frame_count -= frames;
+ audio_vbuffer->tail = (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count;
+ }
+
+ pthread_mutex_unlock (&audio_vbuffer->lock);
+ return frames_read;
+}
+
+struct generic_stream_out {
+ struct audio_stream_out stream; // Constant after init
+ pthread_mutex_t lock;
+ struct generic_audio_device *dev; // Constant after init
+ audio_devices_t device; // Protected by this->lock
+ struct audio_config req_config; // Constant after init
+ struct pcm_config pcm_config; // Constant after init
+ audio_vbuffer_t buffer; // Constant after init
+
+ // Time & Position Keeping
+ bool standby; // Protected by this->lock
+ uint64_t underrun_position; // Protected by this->lock
+ struct timespec underrun_time; // Protected by this->lock
+ uint64_t last_write_time_us; // Protected by this->lock
+ uint64_t frames_total_buffered; // Protected by this->lock
+ uint64_t frames_written; // Protected by this->lock
+ uint64_t frames_rendered; // Protected by this->lock
+
+ // Worker
+ pthread_t worker_thread; // Constant after init
+ pthread_cond_t worker_wake; // Protected by this->lock
+ bool worker_standby; // Protected by this->lock
+ bool worker_exit; // Protected by this->lock
+};
+
+struct generic_stream_in {
+ struct audio_stream_in stream; // Constant after init
+ pthread_mutex_t lock;
+ struct generic_audio_device *dev; // Constant after init
+ audio_devices_t device; // Protected by this->lock
+ struct audio_config req_config; // Constant after init
+ struct pcm *pcm; // Protected by this->lock
+ struct pcm_config pcm_config; // Constant after init
+ int16_t *stereo_to_mono_buf; // Protected by this->lock
+ size_t stereo_to_mono_buf_size; // Protected by this->lock
+ audio_vbuffer_t buffer; // Protected by this->lock
+
+ // Time & Position Keeping
+ bool standby; // Protected by this->lock
+ int64_t standby_position; // Protected by this->lock
+ struct timespec standby_exit_time;// Protected by this->lock
+ int64_t standby_frames_read; // Protected by this->lock
+
+ // Worker
+ pthread_t worker_thread; // Constant after init
+ pthread_cond_t worker_wake; // Protected by this->lock
+ bool worker_standby; // Protected by this->lock
+ bool worker_exit; // Protected by this->lock
+};
+
+static struct pcm_config pcm_config_out = {
+ .channels = 2,
+ .rate = 0,
+ .period_size = 0,
+ .period_count = OUT_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+};
+
+static struct pcm_config pcm_config_in = {
+ .channels = 2,
+ .rate = 0,
+ .period_size = 0,
+ .period_count = IN_PERIOD_COUNT,
+ .format = PCM_FORMAT_S16_LE,
+ .start_threshold = 0,
+ .stop_threshold = INT_MAX,
+};
+
+static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int audio_device_ref_count = 0;
+
+static uint32_t out_get_sample_rate(const struct audio_stream *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ return out->req_config.sample_rate;
+}
+
+static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ return -ENOSYS;
+}
+
+static size_t out_get_buffer_size(const struct audio_stream *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ int size = out->pcm_config.period_size *
+ audio_stream_out_frame_size(&out->stream);
+
+ return size;
+}
+
+static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ return out->req_config.channel_mask;
+}
+
+static audio_format_t out_get_format(const struct audio_stream *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+
+ return out->req_config.format;
+}
+
+static int out_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ return -ENOSYS;
+}
+
+static int out_dump(const struct audio_stream *stream, int fd)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ pthread_mutex_lock(&out->lock);
+ 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",
+ out_get_sample_rate(stream),
+ out_get_buffer_size(stream),
+ out_get_channels(stream),
+ out_get_format(stream),
+ out->device,
+ out->dev);
+ pthread_mutex_unlock(&out->lock);
+ return 0;
+}
+
+static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ struct str_parms *parms;
+ char value[32];
+ int ret = -ENOSYS;
+ int success;
+ long val;
+ char *end;
+
+ if (kvpairs == NULL || kvpairs[0] == 0) {
+ return 0;
+ }
+ pthread_mutex_lock(&out->lock);
+ if (out->standby) {
+ parms = str_parms_create_str(kvpairs);
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ if (success >= 0) {
+ errno = 0;
+ val = strtol(value, &end, 10);
+ if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) {
+ out->device = (int)val;
+ ret = 0;
+ }
+ }
+
+ // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
+ value, sizeof(value));
+ if (success >= 0) {
+ ret = 0;
+ }
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
+ value, sizeof(value));
+ if (success >= 0) {
+ ret = 0;
+ }
+
+ if (ret != 0) {
+ ALOGD("%s Unsupported parameter %s", __FUNCTION__, kvpairs);
+ }
+
+ str_parms_destroy(parms);
+ }
+ pthread_mutex_unlock(&out->lock);
+ return ret;
+}
+
+static char * out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ struct str_parms *query = str_parms_create_str(keys);
+ char *str = NULL;
+ char value[256];
+ struct str_parms *reply = str_parms_create();
+ int ret;
+ bool get = false;
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+ if (ret >= 0) {
+ pthread_mutex_lock(&out->lock);
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device);
+ pthread_mutex_unlock(&out->lock);
+ get = true;
+ }
+
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+ value[0] = 0;
+ strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+ str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
+ get = true;
+ }
+
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+ value[0] = 0;
+ strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+ str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
+ get = true;
+ }
+
+ if (get) {
+ str = strdup(str_parms_to_str(reply));
+ }
+ else {
+ ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys);
+ }
+
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ return str;
+}
+
+static uint32_t out_get_latency(const struct audio_stream_out *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ return (out->pcm_config.period_size * 1000) / out->pcm_config.rate;
+}
+
+static int out_set_volume(struct audio_stream_out *stream, float left,
+ float right)
+{
+ return -ENOSYS;
+}
+
+static void *out_write_worker(void * args)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)args;
+ struct pcm *pcm = NULL;
+ uint8_t *buffer = NULL;
+ int buffer_frames;
+ int buffer_size;
+ bool restart = false;
+ bool shutdown = false;
+ while (true) {
+ pthread_mutex_lock(&out->lock);
+ while (out->worker_standby || restart) {
+ restart = false;
+ if (pcm) {
+ pcm_close(pcm); // Frees pcm
+ pcm = NULL;
+ free(buffer);
+ buffer=NULL;
+ }
+ if (out->worker_exit) {
+ break;
+ }
+ pthread_cond_wait(&out->worker_wake, &out->lock);
+ }
+
+ if (out->worker_exit) {
+ if (!out->worker_standby) {
+ ALOGE("Out worker not in standby before exiting");
+ }
+ shutdown = true;
+ }
+
+ while (!shutdown && audio_vbuffer_live(&out->buffer) == 0) {
+ pthread_cond_wait(&out->worker_wake, &out->lock);
+ }
+
+ if (shutdown) {
+ pthread_mutex_unlock(&out->lock);
+ break;
+ }
+
+ if (!pcm) {
+ pcm = pcm_open(PCM_CARD, PCM_DEVICE,
+ PCM_OUT | PCM_MONOTONIC, &out->pcm_config);
+ if (!pcm_is_ready(pcm)) {
+ ALOGE("pcm_open(out) failed: %s: channels %d format %d rate %d",
+ pcm_get_error(pcm),
+ out->pcm_config.channels,
+ out->pcm_config.format,
+ out->pcm_config.rate
+ );
+ pthread_mutex_unlock(&out->lock);
+ break;
+ }
+ buffer_frames = out->pcm_config.period_size;
+ buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+ buffer = malloc(buffer_size);
+ if (!buffer) {
+ ALOGE("could not allocate write buffer");
+ pthread_mutex_unlock(&out->lock);
+ break;
+ }
+ }
+ int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames);
+ pthread_mutex_unlock(&out->lock);
+ int ret = pcm_write(pcm, buffer, pcm_frames_to_bytes(pcm, frames));
+ if (ret != 0) {
+ ALOGE("pcm_write failed %s", pcm_get_error(pcm));
+ restart = true;
+ }
+ }
+ if (buffer) {
+ free(buffer);
+ }
+
+ return NULL;
+}
+
+// Call with in->lock held
+static void get_current_output_position(struct generic_stream_out *out,
+ uint64_t * position,
+ struct timespec * timestamp) {
+ struct timespec curtime = { .tv_sec = 0, .tv_nsec = 0 };
+ clock_gettime(CLOCK_MONOTONIC, &curtime);
+ const int64_t now_us = (curtime.tv_sec * 1000000000LL + curtime.tv_nsec) / 1000;
+ if (timestamp) {
+ *timestamp = curtime;
+ }
+ int64_t position_since_underrun;
+ if (out->standby) {
+ position_since_underrun = 0;
+ } else {
+ const int64_t first_us = (out->underrun_time.tv_sec * 1000000000LL +
+ out->underrun_time.tv_nsec) / 1000;
+ position_since_underrun = (now_us - first_us) *
+ out_get_sample_rate(&out->stream.common) /
+ 1000000;
+ if (position_since_underrun < 0) {
+ position_since_underrun = 0;
+ }
+ }
+ *position = out->underrun_position + position_since_underrun;
+
+ // The device will reuse the same output stream leading to periods of
+ // underrun.
+ if (*position > out->frames_written) {
+ ALOGW("Not supplying enough data to HAL, expected position %" PRIu64 " , only wrote "
+ "%" PRIu64,
+ *position, out->frames_written);
+
+ *position = out->frames_written;
+ out->underrun_position = *position;
+ out->underrun_time = curtime;
+ out->frames_total_buffered = 0;
+ }
+}
+
+
+static ssize_t out_write(struct audio_stream_out *stream, const void *buffer,
+ size_t bytes)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ const size_t frames = bytes / audio_stream_out_frame_size(stream);
+
+ pthread_mutex_lock(&out->lock);
+
+ if (out->worker_standby) {
+ out->worker_standby = false;
+ }
+
+ uint64_t current_position;
+ struct timespec current_time;
+
+ get_current_output_position(out, ¤t_position, ¤t_time);
+ const uint64_t now_us = (current_time.tv_sec * 1000000000LL +
+ current_time.tv_nsec) / 1000;
+ if (out->standby) {
+ out->standby = false;
+ out->underrun_time = current_time;
+ out->frames_rendered = 0;
+ out->frames_total_buffered = 0;
+ }
+
+ size_t frames_written = audio_vbuffer_write(&out->buffer, buffer, frames);
+ pthread_cond_signal(&out->worker_wake);
+
+ /* Implementation just consumes bytes if we start getting backed up */
+ out->frames_written += frames;
+ out->frames_rendered += frames;
+ out->frames_total_buffered += frames;
+
+ // We simulate the audio device blocking when it's write buffers become
+ // full.
+
+ // At the beginning or after an underrun, try to fill up the vbuffer.
+ // This will be throttled by the PlaybackThread
+ int frames_sleep = out->frames_total_buffered < out->buffer.frame_count ? 0 : frames;
+
+ uint64_t sleep_time_us = frames_sleep * 1000000LL /
+ out_get_sample_rate(&stream->common);
+
+ // If the write calls are delayed, subtract time off of the sleep to
+ // compensate
+ uint64_t time_since_last_write_us = now_us - out->last_write_time_us;
+ if (time_since_last_write_us < sleep_time_us) {
+ sleep_time_us -= time_since_last_write_us;
+ } else {
+ sleep_time_us = 0;
+ }
+ out->last_write_time_us = now_us + sleep_time_us;
+
+ pthread_mutex_unlock(&out->lock);
+
+ if (sleep_time_us > 0) {
+ usleep(sleep_time_us);
+ }
+
+ if (frames_written < frames) {
+ ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames", frames_written, frames);
+ }
+
+ /* Always consume all bytes */
+ return bytes;
+}
+
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+ uint64_t *frames, struct timespec *timestamp)
+
+{
+ if (stream == NULL || frames == NULL || timestamp == NULL) {
+ return -EINVAL;
+ }
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+
+ pthread_mutex_lock(&out->lock);
+ get_current_output_position(out, frames, timestamp);
+ pthread_mutex_unlock(&out->lock);
+
+ return 0;
+}
+
+static int out_get_render_position(const struct audio_stream_out *stream,
+ uint32_t *dsp_frames)
+{
+ if (stream == NULL || dsp_frames == NULL) {
+ return -EINVAL;
+ }
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ pthread_mutex_lock(&out->lock);
+ *dsp_frames = out->frames_rendered;
+ pthread_mutex_unlock(&out->lock);
+ return 0;
+}
+
+// Must be called with out->lock held
+static void do_out_standby(struct generic_stream_out *out)
+{
+ int frames_sleep = 0;
+ uint64_t sleep_time_us = 0;
+ if (out->standby) {
+ return;
+ }
+ while (true) {
+ get_current_output_position(out, &out->underrun_position, NULL);
+ frames_sleep = out->frames_written - out->underrun_position;
+
+ if (frames_sleep == 0) {
+ break;
+ }
+
+ sleep_time_us = frames_sleep * 1000000LL /
+ out_get_sample_rate(&out->stream.common);
+
+ pthread_mutex_unlock(&out->lock);
+ usleep(sleep_time_us);
+ pthread_mutex_lock(&out->lock);
+ }
+ out->worker_standby = true;
+ out->standby = true;
+}
+
+static int out_standby(struct audio_stream *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ pthread_mutex_lock(&out->lock);
+ do_out_standby(out);
+ pthread_mutex_unlock(&out->lock);
+ return 0;
+}
+
+static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ // out_add_audio_effect is a no op
+ return 0;
+}
+
+static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ // out_remove_audio_effect is a no op
+ return 0;
+}
+
+static int out_get_next_write_timestamp(const struct audio_stream_out *stream,
+ int64_t *timestamp)
+{
+ return -ENOSYS;
+}
+
+static uint32_t in_get_sample_rate(const struct audio_stream *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ return in->req_config.sample_rate;
+}
+
+static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
+{
+ return -ENOSYS;
+}
+
+static int refine_output_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask)
+{
+ static const uint32_t sample_rates [] = {8000,11025,16000,22050,24000,32000,
+ 44100,48000};
+ static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
+ bool inval = false;
+ if (*format != AUDIO_FORMAT_PCM_16_BIT) {
+ *format = AUDIO_FORMAT_PCM_16_BIT;
+ inval = true;
+ }
+
+ int channel_count = popcount(*channel_mask);
+ if (channel_count != 1 && channel_count != 2) {
+ *channel_mask = AUDIO_CHANNEL_IN_STEREO;
+ inval = true;
+ }
+
+ int i;
+ for (i = 0; i < sample_rates_count; i++) {
+ if (*sample_rate < sample_rates[i]) {
+ *sample_rate = sample_rates[i];
+ inval=true;
+ break;
+ }
+ else if (*sample_rate == sample_rates[i]) {
+ break;
+ }
+ else if (i == sample_rates_count-1) {
+ // Cap it to the highest rate we support
+ *sample_rate = sample_rates[i];
+ inval=true;
+ }
+ }
+
+ if (inval) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask)
+{
+ static const uint32_t sample_rates [] = {8000, 11025, 16000, 22050, 44100, 48000};
+ static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t);
+ bool inval = false;
+ // Only PCM_16_bit is supported. If this is changed, stereo to mono drop
+ // must be fixed in in_read
+ if (*format != AUDIO_FORMAT_PCM_16_BIT) {
+ *format = AUDIO_FORMAT_PCM_16_BIT;
+ inval = true;
+ }
+
+ int channel_count = popcount(*channel_mask);
+ if (channel_count != 1 && channel_count != 2) {
+ *channel_mask = AUDIO_CHANNEL_IN_STEREO;
+ inval = true;
+ }
+
+ int i;
+ for (i = 0; i < sample_rates_count; i++) {
+ if (*sample_rate < sample_rates[i]) {
+ *sample_rate = sample_rates[i];
+ inval=true;
+ break;
+ }
+ else if (*sample_rate == sample_rates[i]) {
+ break;
+ }
+ else if (i == sample_rates_count-1) {
+ // Cap it to the highest rate we support
+ *sample_rate = sample_rates[i];
+ inval=true;
+ }
+ }
+
+ if (inval) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int check_input_parameters(uint32_t sample_rate, audio_format_t format,
+ audio_channel_mask_t channel_mask)
+{
+ return refine_input_parameters(&sample_rate, &format, &channel_mask);
+}
+
+static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format,
+ audio_channel_mask_t channel_mask)
+{
+ size_t size;
+ int channel_count = popcount(channel_mask);
+ if (check_input_parameters(sample_rate, format, channel_mask) != 0)
+ return 0;
+
+ size = sample_rate*IN_PERIOD_MS/1000;
+ // Audioflinger expects audio buffers to be multiple of 16 frames
+ size = ((size + 15) / 16) * 16;
+ size *= sizeof(short) * channel_count;
+
+ return size;
+}
+
+
+static size_t in_get_buffer_size(const struct audio_stream *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ int size = get_input_buffer_size(in->req_config.sample_rate,
+ in->req_config.format,
+ in->req_config.channel_mask);
+
+ return size;
+}
+
+static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ return in->req_config.channel_mask;
+}
+
+static audio_format_t in_get_format(const struct audio_stream *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ return in->req_config.format;
+}
+
+static int in_set_format(struct audio_stream *stream, audio_format_t format)
+{
+ return -ENOSYS;
+}
+
+static int in_dump(const struct audio_stream *stream, int fd)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+
+ pthread_mutex_lock(&in->lock);
+ dprintf(fd, "\tin_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",
+ in_get_sample_rate(stream),
+ in_get_buffer_size(stream),
+ in_get_channels(stream),
+ in_get_format(stream),
+ in->device,
+ in->dev);
+ pthread_mutex_unlock(&in->lock);
+ return 0;
+}
+
+static int in_set_parameters(struct audio_stream *stream, const char *kvpairs)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ struct str_parms *parms;
+ char value[32];
+ int ret = -ENOSYS;
+ int success;
+ long val;
+ char *end;
+
+ if (kvpairs == NULL || kvpairs[0] == 0) {
+ return 0;
+ }
+ pthread_mutex_lock(&in->lock);
+ if (in->standby) {
+ parms = str_parms_create_str(kvpairs);
+
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
+ value, sizeof(value));
+ if (success >= 0) {
+ errno = 0;
+ val = strtol(value, &end, 10);
+ if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
+ in->device = (int)val;
+ ret = 0;
+ }
+ }
+ // NO op for AUDIO_PARAMETER_DEVICE_CONNECT and AUDIO_PARAMETER_DEVICE_DISCONNECT
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_CONNECT,
+ value, sizeof(value));
+ if (success >= 0) {
+ ret = 0;
+ }
+ success = str_parms_get_str(parms, AUDIO_PARAMETER_DEVICE_DISCONNECT,
+ value, sizeof(value));
+ if (success >= 0) {
+ ret = 0;
+ }
+
+ if (ret != 0) {
+ ALOGD("%s: Unsupported parameter %s", __FUNCTION__, kvpairs);
+ }
+
+ str_parms_destroy(parms);
+ }
+ pthread_mutex_unlock(&in->lock);
+ return ret;
+}
+
+static char * in_get_parameters(const struct audio_stream *stream,
+ const char *keys)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ struct str_parms *query = str_parms_create_str(keys);
+ char *str = NULL;
+ char value[256];
+ struct str_parms *reply = str_parms_create();
+ int ret;
+ bool get = false;
+
+ ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
+ if (ret >= 0) {
+ str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device);
+ get = true;
+ }
+
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+ value[0] = 0;
+ strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+ str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
+ get = true;
+ }
+
+ if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) {
+ value[0] = 0;
+ strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
+ str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value);
+ get = true;
+ }
+
+ if (get) {
+ str = strdup(str_parms_to_str(reply));
+ }
+ else {
+ ALOGD("%s Unsupported paramter: %s", __FUNCTION__, keys);
+ }
+
+ str_parms_destroy(query);
+ str_parms_destroy(reply);
+ return str;
+}
+
+static int in_set_gain(struct audio_stream_in *stream, float gain)
+{
+ // in_set_gain is a no op
+ return 0;
+}
+
+// Call with in->lock held
+static void get_current_input_position(struct generic_stream_in *in,
+ int64_t * position,
+ struct timespec * timestamp) {
+ struct timespec t = { .tv_sec = 0, .tv_nsec = 0 };
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ const int64_t now_us = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000;
+ if (timestamp) {
+ *timestamp = t;
+ }
+ int64_t position_since_standby;
+ if (in->standby) {
+ position_since_standby = 0;
+ } else {
+ const int64_t first_us = (in->standby_exit_time.tv_sec * 1000000000LL +
+ in->standby_exit_time.tv_nsec) / 1000;
+ position_since_standby = (now_us - first_us) *
+ in_get_sample_rate(&in->stream.common) /
+ 1000000;
+ if (position_since_standby < 0) {
+ position_since_standby = 0;
+ }
+ }
+ *position = in->standby_position + position_since_standby;
+}
+
+// Must be called with in->lock held
+static void do_in_standby(struct generic_stream_in *in)
+{
+ if (in->standby) {
+ return;
+ }
+ in->worker_standby = true;
+ get_current_input_position(in, &in->standby_position, NULL);
+ in->standby = true;
+}
+
+static int in_standby(struct audio_stream *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ pthread_mutex_lock(&in->lock);
+ do_in_standby(in);
+ pthread_mutex_unlock(&in->lock);
+ return 0;
+}
+
+static void *in_read_worker(void * args)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)args;
+ struct pcm *pcm = NULL;
+ uint8_t *buffer = NULL;
+ size_t buffer_frames;
+ int buffer_size;
+
+ bool restart = false;
+ bool shutdown = false;
+ while (true) {
+ pthread_mutex_lock(&in->lock);
+ while (in->worker_standby || restart) {
+ restart = false;
+ if (pcm) {
+ pcm_close(pcm); // Frees pcm
+ pcm = NULL;
+ free(buffer);
+ buffer=NULL;
+ }
+ if (in->worker_exit) {
+ break;
+ }
+ pthread_cond_wait(&in->worker_wake, &in->lock);
+ }
+
+ if (in->worker_exit) {
+ if (!in->worker_standby) {
+ ALOGE("In worker not in standby before exiting");
+ }
+ shutdown = true;
+ }
+ if (shutdown) {
+ pthread_mutex_unlock(&in->lock);
+ break;
+ }
+ if (!pcm) {
+ pcm = pcm_open(PCM_CARD, PCM_DEVICE,
+ PCM_IN | PCM_MONOTONIC, &in->pcm_config);
+ if (!pcm_is_ready(pcm)) {
+ ALOGE("pcm_open(in) failed: %s: channels %d format %d rate %d",
+ pcm_get_error(pcm),
+ in->pcm_config.channels,
+ in->pcm_config.format,
+ in->pcm_config.rate
+ );
+ pthread_mutex_unlock(&in->lock);
+ break;
+ }
+ buffer_frames = in->pcm_config.period_size;
+ buffer_size = pcm_frames_to_bytes(pcm, buffer_frames);
+ buffer = malloc(buffer_size);
+ if (!buffer) {
+ ALOGE("could not allocate worker read buffer");
+ pthread_mutex_unlock(&in->lock);
+ break;
+ }
+ }
+ pthread_mutex_unlock(&in->lock);
+ int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames));
+ if (ret != 0) {
+ ALOGW("pcm_read failed %s", pcm_get_error(pcm));
+ restart = true;
+ continue;
+ }
+
+ pthread_mutex_lock(&in->lock);
+ size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames);
+ pthread_mutex_unlock(&in->lock);
+
+ if (frames_written != buffer_frames) {
+ ALOGW("in_read_worker only could write %zu / %zu frames", frames_written, buffer_frames);
+ }
+ }
+ if (buffer) {
+ free(buffer);
+ }
+ return NULL;
+}
+
+static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
+ size_t bytes)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ struct generic_audio_device *adev = in->dev;
+ const size_t frames = bytes / audio_stream_in_frame_size(stream);
+ bool mic_mute = false;
+ size_t read_bytes = 0;
+
+ adev_get_mic_mute(&adev->device, &mic_mute);
+ pthread_mutex_lock(&in->lock);
+
+ if (in->worker_standby) {
+ in->worker_standby = false;
+ }
+ pthread_cond_signal(&in->worker_wake);
+
+ int64_t current_position;
+ struct timespec current_time;
+
+ get_current_input_position(in, ¤t_position, ¤t_time);
+ if (in->standby) {
+ in->standby = false;
+ in->standby_exit_time = current_time;
+ in->standby_frames_read = 0;
+ }
+
+ const int64_t frames_available = current_position - in->standby_position - in->standby_frames_read;
+ assert(frames_available >= 0);
+
+ const size_t frames_wait = ((uint64_t)frames_available > frames) ? 0 : frames - frames_available;
+
+ int64_t sleep_time_us = frames_wait * 1000000LL /
+ in_get_sample_rate(&stream->common);
+
+ pthread_mutex_unlock(&in->lock);
+
+ if (sleep_time_us > 0) {
+ usleep(sleep_time_us);
+ }
+
+ pthread_mutex_lock(&in->lock);
+ int read_frames = 0;
+ if (in->standby) {
+ ALOGW("Input put to sleep while read in progress");
+ goto exit;
+ }
+ in->standby_frames_read += frames;
+
+ if (popcount(in->req_config.channel_mask) == 1 &&
+ in->pcm_config.channels == 2) {
+ // Need to resample to mono
+ if (in->stereo_to_mono_buf_size < bytes*2) {
+ in->stereo_to_mono_buf = realloc(in->stereo_to_mono_buf,
+ bytes*2);
+ if (!in->stereo_to_mono_buf) {
+ ALOGE("Failed to allocate stereo_to_mono_buff");
+ goto exit;
+ }
+ }
+
+ read_frames = audio_vbuffer_read(&in->buffer, in->stereo_to_mono_buf, frames);
+
+ // Currently only pcm 16 is supported.
+ uint16_t *src = (uint16_t *)in->stereo_to_mono_buf;
+ uint16_t *dst = (uint16_t *)buffer;
+ size_t i;
+ // Resample stereo 16 to mono 16 by dropping one channel.
+ // The stereo stream is interleaved L-R-L-R
+ for (i = 0; i < frames; i++) {
+ *dst = *src;
+ src += 2;
+ dst += 1;
+ }
+ } else {
+ read_frames = audio_vbuffer_read(&in->buffer, buffer, frames);
+ }
+
+exit:
+ read_bytes = read_frames*audio_stream_in_frame_size(stream);
+
+ if (mic_mute) {
+ read_bytes = 0;
+ }
+
+ if (read_bytes < bytes) {
+ memset (&((uint8_t *)buffer)[read_bytes], 0, bytes-read_bytes);
+ }
+
+ pthread_mutex_unlock(&in->lock);
+
+ return bytes;
+}
+
+static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream)
+{
+ return 0;
+}
+
+static int in_get_capture_position(const struct audio_stream_in *stream,
+ int64_t *frames, int64_t *time)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ pthread_mutex_lock(&in->lock);
+ struct timespec current_time;
+ get_current_input_position(in, frames, ¤t_time);
+ *time = (current_time.tv_sec * 1000000000LL + current_time.tv_nsec);
+ pthread_mutex_unlock(&in->lock);
+ return 0;
+}
+
+static int in_get_active_microphones(const struct audio_stream_in *stream,
+ struct audio_microphone_characteristic_t *mic_array,
+ size_t *mic_count)
+{
+ return adev_get_microphones(NULL, mic_array, mic_count);
+}
+
+static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ // in_add_audio_effect is a no op
+ return 0;
+}
+
+static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
+{
+ // in_add_audio_effect is a no op
+ return 0;
+}
+
+static int adev_open_output_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ audio_output_flags_t flags,
+ struct audio_config *config,
+ struct audio_stream_out **stream_out,
+ const char *address __unused)
+{
+ struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+ struct generic_stream_out *out;
+ int ret = 0;
+
+ if (refine_output_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
+ ALOGE("Error opening output stream format %d, channel_mask %04x, sample_rate %u",
+ config->format, config->channel_mask, config->sample_rate);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out));
+
+ if (!out)
+ return -ENOMEM;
+
+ out->stream.common.get_sample_rate = out_get_sample_rate;
+ out->stream.common.set_sample_rate = out_set_sample_rate;
+ out->stream.common.get_buffer_size = out_get_buffer_size;
+ out->stream.common.get_channels = out_get_channels;
+ out->stream.common.get_format = out_get_format;
+ out->stream.common.set_format = out_set_format;
+ out->stream.common.standby = out_standby;
+ out->stream.common.dump = out_dump;
+ out->stream.common.set_parameters = out_set_parameters;
+ out->stream.common.get_parameters = out_get_parameters;
+ out->stream.common.add_audio_effect = out_add_audio_effect;
+ out->stream.common.remove_audio_effect = out_remove_audio_effect;
+ out->stream.get_latency = out_get_latency;
+ out->stream.set_volume = out_set_volume;
+ out->stream.write = out_write;
+ out->stream.get_render_position = out_get_render_position;
+ out->stream.get_presentation_position = out_get_presentation_position;
+ out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
+
+ pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
+ out->dev = adev;
+ out->device = devices;
+ memcpy(&out->req_config, config, sizeof(struct audio_config));
+ memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config));
+ out->pcm_config.rate = config->sample_rate;
+ out->pcm_config.period_size = out->pcm_config.rate*OUT_PERIOD_MS/1000;
+
+ out->standby = true;
+ out->underrun_position = 0;
+ out->underrun_time.tv_sec = 0;
+ out->underrun_time.tv_nsec = 0;
+ out->last_write_time_us = 0;
+ out->frames_total_buffered = 0;
+ out->frames_written = 0;
+ out->frames_rendered = 0;
+
+ ret = audio_vbuffer_init(&out->buffer,
+ out->pcm_config.period_size*out->pcm_config.period_count,
+ out->pcm_config.channels *
+ pcm_format_to_bits(out->pcm_config.format) >> 3);
+ if (ret == 0) {
+ pthread_cond_init(&out->worker_wake, NULL);
+ out->worker_standby = true;
+ out->worker_exit = false;
+ pthread_create(&out->worker_thread, NULL, out_write_worker, out);
+
+ }
+ *stream_out = &out->stream;
+
+
+error:
+
+ return ret;
+}
+
+static void adev_close_output_stream(struct audio_hw_device *dev,
+ struct audio_stream_out *stream)
+{
+ struct generic_stream_out *out = (struct generic_stream_out *)stream;
+ pthread_mutex_lock(&out->lock);
+ do_out_standby(out);
+
+ out->worker_exit = true;
+ pthread_cond_signal(&out->worker_wake);
+ pthread_mutex_unlock(&out->lock);
+
+ pthread_join(out->worker_thread, NULL);
+ pthread_mutex_destroy(&out->lock);
+ audio_vbuffer_destroy(&out->buffer);
+ free(stream);
+}
+
+static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
+{
+ return 0;
+}
+
+static char * adev_get_parameters(const struct audio_hw_device *dev,
+ const char *keys)
+{
+ return strdup("");
+}
+
+static int adev_init_check(const struct audio_hw_device *dev)
+{
+ return 0;
+}
+
+static int adev_set_voice_volume(struct audio_hw_device *dev, float volume)
+{
+ // adev_set_voice_volume is a no op (simulates phones)
+ return 0;
+}
+
+static int adev_set_master_volume(struct audio_hw_device *dev, float volume)
+{
+ return -ENOSYS;
+}
+
+static int adev_get_master_volume(struct audio_hw_device *dev, float *volume)
+{
+ return -ENOSYS;
+}
+
+static int adev_set_master_mute(struct audio_hw_device *dev, bool muted)
+{
+ return -ENOSYS;
+}
+
+static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted)
+{
+ return -ENOSYS;
+}
+
+static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode)
+{
+ // adev_set_mode is a no op (simulates phones)
+ return 0;
+}
+
+static int adev_set_mic_mute(struct audio_hw_device *dev, bool state)
+{
+ struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+ pthread_mutex_lock(&adev->lock);
+ adev->mic_mute = state;
+ pthread_mutex_unlock(&adev->lock);
+ return 0;
+}
+
+static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state)
+{
+ struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+ pthread_mutex_lock(&adev->lock);
+ *state = adev->mic_mute;
+ pthread_mutex_unlock(&adev->lock);
+ return 0;
+}
+
+
+static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
+ const struct audio_config *config)
+{
+ return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask);
+}
+
+
+static void adev_close_input_stream(struct audio_hw_device *dev,
+ struct audio_stream_in *stream)
+{
+ struct generic_stream_in *in = (struct generic_stream_in *)stream;
+ pthread_mutex_lock(&in->lock);
+ do_in_standby(in);
+
+ in->worker_exit = true;
+ pthread_cond_signal(&in->worker_wake);
+ pthread_mutex_unlock(&in->lock);
+ pthread_join(in->worker_thread, NULL);
+
+ if (in->stereo_to_mono_buf != NULL) {
+ free(in->stereo_to_mono_buf);
+ in->stereo_to_mono_buf_size = 0;
+ }
+
+ pthread_mutex_destroy(&in->lock);
+ audio_vbuffer_destroy(&in->buffer);
+ free(stream);
+}
+
+
+static int adev_open_input_stream(struct audio_hw_device *dev,
+ audio_io_handle_t handle,
+ audio_devices_t devices,
+ struct audio_config *config,
+ struct audio_stream_in **stream_in,
+ audio_input_flags_t flags __unused,
+ const char *address __unused,
+ audio_source_t source __unused)
+{
+ struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+ struct generic_stream_in *in;
+ int ret = 0;
+ if (refine_input_parameters(&config->sample_rate, &config->format, &config->channel_mask)) {
+ ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u",
+ config->format, config->channel_mask, config->sample_rate);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in));
+ if (!in) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate; // no op
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format; // no op
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect; // no op
+ in->stream.common.remove_audio_effect = in_remove_audio_effect; // no op
+ in->stream.set_gain = in_set_gain; // no op
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost; // no op
+ in->stream.get_capture_position = in_get_capture_position;
+ in->stream.get_active_microphones = in_get_active_microphones;
+
+ pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL);
+ in->dev = adev;
+ in->device = devices;
+ memcpy(&in->req_config, config, sizeof(struct audio_config));
+ memcpy(&in->pcm_config, &pcm_config_in, sizeof(struct pcm_config));
+ in->pcm_config.rate = config->sample_rate;
+ in->pcm_config.period_size = in->pcm_config.rate*IN_PERIOD_MS/1000;
+
+ in->stereo_to_mono_buf = NULL;
+ in->stereo_to_mono_buf_size = 0;
+
+ in->standby = true;
+ in->standby_position = 0;
+ in->standby_exit_time.tv_sec = 0;
+ in->standby_exit_time.tv_nsec = 0;
+ in->standby_frames_read = 0;
+
+ ret = audio_vbuffer_init(&in->buffer,
+ in->pcm_config.period_size*in->pcm_config.period_count,
+ in->pcm_config.channels *
+ pcm_format_to_bits(in->pcm_config.format) >> 3);
+ if (ret == 0) {
+ pthread_cond_init(&in->worker_wake, NULL);
+ in->worker_standby = true;
+ in->worker_exit = false;
+ pthread_create(&in->worker_thread, NULL, in_read_worker, in);
+ }
+
+ *stream_in = &in->stream;
+
+error:
+ return ret;
+}
+
+
+static int adev_dump(const audio_hw_device_t *dev, int fd)
+{
+ return 0;
+}
+
+static int adev_get_microphones(const audio_hw_device_t *dev,
+ struct audio_microphone_characteristic_t *mic_array,
+ size_t *mic_count)
+{
+ if (mic_count == NULL) {
+ return -ENOSYS;
+ }
+
+ if (*mic_count == 0) {
+ *mic_count = 1;
+ return 0;
+ }
+
+ if (mic_array == NULL) {
+ return -ENOSYS;
+ }
+
+ strncpy(mic_array->device_id, "mic_goldfish", AUDIO_MICROPHONE_ID_MAX_LEN - 1);
+ mic_array->device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ strncpy(mic_array->address, AUDIO_BOTTOM_MICROPHONE_ADDRESS,
+ AUDIO_DEVICE_MAX_ADDRESS_LEN - 1);
+ memset(mic_array->channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED,
+ sizeof(mic_array->channel_mapping));
+ mic_array->location = AUDIO_MICROPHONE_LOCATION_UNKNOWN;
+ mic_array->group = 0;
+ mic_array->index_in_the_group = 0;
+ mic_array->sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+ mic_array->max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+ mic_array->min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN;
+ mic_array->directionality = AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN;
+ mic_array->num_frequency_responses = 0;
+ mic_array->geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic_array->geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic_array->geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic_array->orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic_array->orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic_array->orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+
+ *mic_count = 1;
+ return 0;
+}
+
+static int adev_close(hw_device_t *dev)
+{
+ struct generic_audio_device *adev = (struct generic_audio_device *)dev;
+ int ret = 0;
+ if (!adev)
+ return 0;
+
+ pthread_mutex_lock(&adev_init_lock);
+
+ if (audio_device_ref_count == 0) {
+ ALOGE("adev_close called when ref_count 0");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if ((--audio_device_ref_count) == 0) {
+ if (adev->mixer) {
+ mixer_close(adev->mixer);
+ }
+ free(adev);
+ }
+
+error:
+ pthread_mutex_unlock(&adev_init_lock);
+ return ret;
+}
+
+static int adev_open(const hw_module_t* module, const char* name,
+ hw_device_t** device)
+{
+ static struct generic_audio_device *adev;
+
+ if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
+ return -EINVAL;
+
+ pthread_mutex_lock(&adev_init_lock);
+ if (audio_device_ref_count != 0) {
+ *device = &adev->device.common;
+ audio_device_ref_count++;
+ ALOGV("%s: returning existing instance of adev", __func__);
+ ALOGV("%s: exit", __func__);
+ goto unlock;
+ }
+ adev = calloc(1, sizeof(struct generic_audio_device));
+
+ pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL);
+
+ adev->device.common.tag = HARDWARE_DEVICE_TAG;
+ adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
+ adev->device.common.module = (struct hw_module_t *) module;
+ adev->device.common.close = adev_close;
+
+ adev->device.init_check = adev_init_check; // no op
+ adev->device.set_voice_volume = adev_set_voice_volume; // no op
+ adev->device.set_master_volume = adev_set_master_volume; // no op
+ adev->device.get_master_volume = adev_get_master_volume; // no op
+ adev->device.set_master_mute = adev_set_master_mute; // no op
+ adev->device.get_master_mute = adev_get_master_mute; // no op
+ adev->device.set_mode = adev_set_mode; // no op
+ adev->device.set_mic_mute = adev_set_mic_mute;
+ adev->device.get_mic_mute = adev_get_mic_mute;
+ adev->device.set_parameters = adev_set_parameters; // no op
+ adev->device.get_parameters = adev_get_parameters; // no op
+ adev->device.get_input_buffer_size = adev_get_input_buffer_size;
+ adev->device.open_output_stream = adev_open_output_stream;
+ adev->device.close_output_stream = adev_close_output_stream;
+ adev->device.open_input_stream = adev_open_input_stream;
+ adev->device.close_input_stream = adev_close_input_stream;
+ adev->device.dump = adev_dump;
+ adev->device.get_microphones = adev_get_microphones;
+
+ *device = &adev->device.common;
+
+ adev->mixer = mixer_open(PCM_CARD);
+ struct mixer_ctl *ctl;
+
+ // Set default mixer ctls
+ // Enable channels and set volume
+ for (int i = 0; i < (int)mixer_get_num_ctls(adev->mixer); i++) {
+ ctl = mixer_get_ctl(adev->mixer, i);
+ ALOGD("mixer %d name %s", i, mixer_ctl_get_name(ctl));
+ if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Volume") ||
+ !strcmp(mixer_ctl_get_name(ctl), "Capture Volume")) {
+ for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
+ ALOGD("set ctl %d to %d", z, 100);
+ mixer_ctl_set_percent(ctl, z, 100);
+ }
+ continue;
+ }
+ if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Switch") ||
+ !strcmp(mixer_ctl_get_name(ctl), "Capture Switch")) {
+ for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) {
+ ALOGD("set ctl %d to %d", z, 1);
+ mixer_ctl_set_value(ctl, z, 1);
+ }
+ continue;
+ }
+ }
+
+ audio_device_ref_count++;
+
+unlock:
+ pthread_mutex_unlock(&adev_init_lock);
+ return 0;
+}
+
+static struct hw_module_methods_t hal_module_methods = {
+ .open = adev_open,
+};
+
+struct 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 = "Generic audio HW HAL",
+ .author = "The Android Open Source Project",
+ .methods = &hal_module_methods,
+ },
+};
diff --git a/guest/hals/audio/Android.mk b/guest/hals/audio/legacy/Android.mk
similarity index 95%
rename from guest/hals/audio/Android.mk
rename to guest/hals/audio/legacy/Android.mk
index 3af6e0f..8d4ef0a 100644
--- a/guest/hals/audio/Android.mk
+++ b/guest/hals/audio/legacy/Android.mk
@@ -55,11 +55,11 @@
$(VSOC_STLPORT_STATIC_LIBS)
LOCAL_CFLAGS := \
- -Wall -Werror \
+ -Wall -Werror -std=c++17 \
$(VSOC_VERSION_CFLAGS)
-LOCAL_MODULE := audio.primary.vsoc
+LOCAL_MODULE := audio.primary.cutf_ivsh
LOCAL_VENDOR_MODULE := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/audio/audio_hal.cpp b/guest/hals/audio/legacy/audio_hal.cpp
similarity index 92%
rename from guest/hals/audio/audio_hal.cpp
rename to guest/hals/audio/legacy/audio_hal.cpp
index 74180b5..8b528a6 100644
--- a/guest/hals/audio/audio_hal.cpp
+++ b/guest/hals/audio/legacy/audio_hal.cpp
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "guest/hals/audio/vsoc_audio.h"
+#include "guest/hals/audio/legacy/vsoc_audio.h"
-#include "guest/hals/audio/audio_hal.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
#include "guest/libs/platform_support/api_level_fixes.h"
static hw_module_methods_t hal_module_methods = {
diff --git a/guest/hals/audio/audio_hal.h b/guest/hals/audio/legacy/audio_hal.h
similarity index 100%
rename from guest/hals/audio/audio_hal.h
rename to guest/hals/audio/legacy/audio_hal.h
diff --git a/guest/hals/audio/policy/Android.mk b/guest/hals/audio/legacy/policy/Android.mk
similarity index 100%
rename from guest/hals/audio/policy/Android.mk
rename to guest/hals/audio/legacy/policy/Android.mk
diff --git a/guest/hals/audio/policy/vsoc_audio_policy_hal.cpp b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
similarity index 98%
rename from guest/hals/audio/policy/vsoc_audio_policy_hal.cpp
rename to guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
index c0ca8f9..53c3127 100644
--- a/guest/hals/audio/policy/vsoc_audio_policy_hal.cpp
+++ b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.cpp
@@ -23,7 +23,7 @@
#include <system/audio_policy.h>
#include <hardware/audio_policy.h>
-#include "guest/hals/audio/policy/vsoc_audio_policy_hal.h"
+#include "guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h"
namespace cvd {
diff --git a/guest/hals/audio/policy/vsoc_audio_policy_hal.h b/guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
similarity index 100%
rename from guest/hals/audio/policy/vsoc_audio_policy_hal.h
rename to guest/hals/audio/legacy/policy/vsoc_audio_policy_hal.h
diff --git a/guest/hals/audio/vsoc_audio.cpp b/guest/hals/audio/legacy/vsoc_audio.cpp
similarity index 84%
rename from guest/hals/audio/vsoc_audio.cpp
rename to guest/hals/audio/legacy/vsoc_audio.cpp
index 38352ff..a59c04d 100644
--- a/guest/hals/audio/vsoc_audio.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "guest/hals/audio/audio_hal.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
#include <inttypes.h>
#include <stdio.h>
@@ -30,9 +30,9 @@
#include "common/libs/threads/cuttlefish_thread.h"
#include "common/libs/threads/thunkers.h"
#include "common/vsoc/lib/circqueue_impl.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.h"
-#include "guest/hals/audio/vsoc_audio_output_stream.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/platform_support/api_level_fixes.h"
#include "guest/libs/remoter/remoter_framework_pkt.h"
@@ -41,15 +41,6 @@
namespace cvd {
-namespace {
-template <typename F> struct HWDeviceThunker :
- ThunkerBase<hw_device_t, GceAudio, F>{};
-template <typename F> struct AudioThunker :
- ThunkerBase<audio_hw_device, GceAudio, F>{};
-template <typename F> struct AudioThreadThunker :
- ThunkerBase<void, GceAudio, F>{};
-}
-
GceAudio::~GceAudio() { }
int GceAudio::Close() {
@@ -299,7 +290,7 @@
rval->common.tag = HARDWARE_DEVICE_TAG;
rval->common.version = version_;
rval->common.module = const_cast<hw_module_t *>(module);
- rval->common.close = HWDeviceThunker<int()>::call<&GceAudio::Close>;
+ 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.
@@ -308,51 +299,45 @@
// Skipping the assignment is ok: the memset in the constructor already
// put a NULL here.
rval->get_supported_devices =
- AudioThunker<uint32_t()>::call<&GceAudio::GetSupportedDevices>;
+ cvd::thunk<audio_hw_device, &GceAudio::GetSupportedDevices>;
#endif
- rval->init_check = AudioThunker<int()>::call<&GceAudio::InitCheck>;
+ rval->init_check = cvd::thunk<audio_hw_device, &GceAudio::InitCheck>;
rval->set_voice_volume =
- AudioThunker<int(float)>::call<&GceAudio::SetVoiceVolume>;
+ cvd::thunk<audio_hw_device, &GceAudio::SetVoiceVolume>;
rval->set_master_volume =
- AudioThunker<int(float)>::call<&GceAudio::SetMasterVolume>;
+ cvd::thunk<audio_hw_device, &GceAudio::SetMasterVolume>;
rval->get_master_volume =
- AudioThunker<int(float*)>::call<&GceAudio::GetMasterVolume>;
+ cvd::thunk<audio_hw_device, &GceAudio::GetMasterVolume>;
#if defined(AUDIO_DEVICE_API_VERSION_2_0)
rval->set_master_mute =
- AudioThunker<int(bool)>::call<&GceAudio::SetMasterMute>;
+ cvd::thunk<audio_hw_device, &GceAudio::SetMasterMute>;
rval->get_master_mute =
- AudioThunker<int(bool*)>::call<&GceAudio::GetMasterMute>;
+ cvd::thunk<audio_hw_device, &GceAudio::GetMasterMute>;
#endif
- rval->set_mode = AudioThunker<int(audio_mode_t)>::call<&GceAudio::SetMode>;
- rval->set_mic_mute = AudioThunker<int(bool)>::call<&GceAudio::SetMicMute>;
- rval->get_mic_mute =
- AudioThunker<int(bool*)>::call<&GceAudio::GetMicMute>;
+ 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 =
- AudioThunker<int(const char*)>::call<&GceAudio::SetParameters>;
- rval->get_parameters =
- AudioThunker<char*(const char*)>::call<&GceAudio::GetParameters>;
+ 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 =
- AudioThunker<size_t(const audio_config*)>::call<
- &GceAudio::GetInputBufferSize>;
+ cvd::thunk<audio_hw_device, &GceAudio::GetInputBufferSize>;
rval->open_input_stream =
- AudioThunker<GceAudio::OpenInputStreamHAL_t>::call<
- &GceAudio::OpenInputStreamCurrentHAL>;
+ cvd::thunk<audio_hw_device, &GceAudio::OpenInputStreamCurrentHAL>;
rval->close_input_stream =
- AudioThunker<void(audio_stream_in*)>::call<&GceAudio::CloseInputStream>;
+ cvd::thunk<audio_hw_device, &GceAudio::CloseInputStream>;
rval->open_output_stream =
- AudioThunker<GceAudio::OpenOutputStreamHAL_t>::call<
- &GceAudio::OpenOutputStreamCurrentHAL>;
+ cvd::thunk<audio_hw_device, &GceAudio::OpenOutputStreamCurrentHAL>;
rval->close_output_stream =
- AudioThunker<void(audio_stream_out*)>::call<&GceAudio::CloseOutputStream>;
+ cvd::thunk<audio_hw_device, &GceAudio::CloseOutputStream>;
- rval->dump = AudioThunker<int(int)>::call<&GceAudio::Dump>;
+ rval->dump = cvd::thunk<audio_hw_device, &GceAudio::Dump>;
*device = &rval->common;
return 0;
diff --git a/guest/hals/audio/vsoc_audio.h b/guest/hals/audio/legacy/vsoc_audio.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio.h
rename to guest/hals/audio/legacy/vsoc_audio.h
index 6ec9eee..a5f551a 100644
--- a/guest/hals/audio/vsoc_audio.h
+++ b/guest/hals/audio/legacy/vsoc_audio.h
@@ -23,8 +23,8 @@
#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/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
+#include "guest/hals/audio/legacy/vsoc_audio_input_stream.h"
#include "guest/libs/platform_support/api_level_fixes.h"
namespace cvd {
diff --git a/guest/hals/audio/vsoc_audio_input_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
similarity index 76%
rename from guest/hals/audio/vsoc_audio_input_stream.cpp
rename to guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
index 4c91237..1ffd29b 100644
--- a/guest/hals/audio/vsoc_audio_input_stream.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.cpp
@@ -25,20 +25,13 @@
#include "common/libs/auto_resources/auto_resources.h"
#include "common/libs/threads/thunkers.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_input_stream.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"
#include "guest/libs/platform_support/api_level_fixes.h"
namespace cvd {
-namespace {
-template <typename F> struct Thunker :
- ThunkerBase<audio_stream, GceAudioInputStream, F>{};
-template <typename F> struct InThunker :
- ThunkerBase<audio_stream_in, GceAudioInputStream, F>{};
-}
-
#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);
@@ -63,36 +56,30 @@
gain_(0.0),
device_(devices) {
common.get_sample_rate =
- Thunker<uint32_t()>::call<&GceAudioInputStream::GetSampleRate>;
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetSampleRate>;
common.set_sample_rate =
- Thunker<int(uint32_t)>::call<&GceAudioInputStream::SetSampleRate>;
+ cvd::thunk<audio_stream, &GceAudioInputStream::SetSampleRate>;
common.get_buffer_size =
- Thunker<size_t()>::call<&GceAudioInputStream::GetBufferSize>;
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetBufferSize>;
common.get_channels =
- Thunker<audio_channel_mask_t()>::call<&GceAudioInputStream::GetChannels>;
- common.get_device =
- Thunker<audio_devices_t()>::call<&GceAudioInputStream::GetDevice>;
- common.set_device =
- Thunker<int(audio_devices_t)>::call<&GceAudioInputStream::SetDevice>;
- common.get_format =
- Thunker<audio_format_t()>::call<&GceAudioInputStream::GetFormat>;
- common.set_format =
- Thunker<int(audio_format_t)>::call<&GceAudioInputStream::SetFormat>;
- common.standby =
- Thunker<int()>::call<&GceAudioInputStream::Standby>;
- common.dump =
- Thunker<int(int)>::call<&GceAudioInputStream::Dump>;
+ 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 =
- Thunker<char*(const char *)>::call<&GceAudioInputStream::GetParameters>;
+ cvd::thunk<audio_stream, &GceAudioInputStream::GetParameters>;
common.add_audio_effect =
- Thunker<int(effect_handle_t)>::call<&GceAudioInputStream::AddAudioEffect>;
- common.remove_audio_effect = Thunker<int(effect_handle_t)>::call<
+ cvd::thunk<audio_stream, &GceAudioInputStream::AddAudioEffect>;
+ common.remove_audio_effect = cvd::thunk<audio_stream,
&GceAudioInputStream::RemoveAudioEffect>;
- set_gain = InThunker<int(float)>::call<&GceAudioInputStream::SetGain>;
- read = InThunker<ssize_t(void*, size_t)>::call<
+ set_gain = cvd::thunk<audio_stream_in, &GceAudioInputStream::SetGain>;
+ read = cvd::thunk<audio_stream_in,
&GceAudioInputStream::Read>;
- get_input_frames_lost = InThunker<uint32_t()>::call<
+ get_input_frames_lost = cvd::thunk<audio_stream_in,
&GceAudioInputStream::GetInputFramesLost>;
frame_size_ = GceAudioFrameSize(this);
buffer_model_.reset(
diff --git a/guest/hals/audio/vsoc_audio_input_stream.h b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio_input_stream.h
rename to guest/hals/audio/legacy/vsoc_audio_input_stream.h
index 40d6b19..222194c 100644
--- a/guest/hals/audio/vsoc_audio_input_stream.h
+++ b/guest/hals/audio/legacy/vsoc_audio_input_stream.h
@@ -17,9 +17,9 @@
#include <memory>
+#include "common/libs/utils/simulated_buffer.h"
#include "common/vsoc/lib/vsoc_audio_message.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/simulated_buffer.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
namespace cvd {
diff --git a/guest/hals/audio/vsoc_audio_output_stream.cpp b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
similarity index 83%
rename from guest/hals/audio/vsoc_audio_output_stream.cpp
rename to guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
index b78351e..255b6fd 100644
--- a/guest/hals/audio/vsoc_audio_output_stream.cpp
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.cpp
@@ -26,9 +26,9 @@
#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/audio_hal.h"
-#include "guest/hals/audio/vsoc_audio.h"
-#include "guest/hals/audio/vsoc_audio_output_stream.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/platform_support/api_level_fixes.h"
#include "guest/libs/remoter/remoter_framework_pkt.h"
@@ -53,14 +53,6 @@
const size_t GceAudioOutputStream::kOutBufferSize;
const size_t GceAudioOutputStream::kOutLatency;
-namespace {
-template <typename F> struct Thunker :
- ThunkerBase<audio_stream, GceAudioOutputStream, F>{};
-
-template <typename F> struct OutThunker :
- ThunkerBase<audio_stream_out, GceAudioOutputStream, F>{};
-}
-
GceAudioOutputStream::GceAudioOutputStream(GceAudio* dev) :
audio_stream_out(),
dev_(dev),
@@ -285,50 +277,38 @@
0;
#endif
out->common.get_sample_rate =
- Thunker<uint32_t()>::call<&GceAudioOutputStream::GetSampleRate>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetSampleRate>;
out->common.set_sample_rate =
- Thunker<int(uint32_t)>::call<&GceAudioOutputStream::SetSampleRate>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::SetSampleRate>;
out->common.get_buffer_size =
- Thunker<size_t()>::call<&GceAudioOutputStream::GetBufferSize>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetBufferSize>;
out->common.get_channels =
- Thunker<audio_channel_mask_t()>::call<
- &GceAudioOutputStream::GetChannels>;
- out->common.get_format = Thunker<audio_format_t()>::call<
- &GceAudioOutputStream::GetFormat>;
- out->common.set_format = Thunker<int(audio_format_t)>::call<
- &GceAudioOutputStream::SetFormat>;
- out->common.standby = Thunker<int()>::call<&GceAudioOutputStream::Standby>;
- out->common.dump = Thunker<int(int)>::call<&GceAudioOutputStream::Dump>;
- out->common.get_device = Thunker<audio_devices_t()>::call<
- &GceAudioOutputStream::GetDevice>;
- out->common.set_device = Thunker<int(audio_devices_t)>::call<
- &GceAudioOutputStream::SetDevice>;
+ 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 =
- Thunker<int(const char*)>::call<
- &GceAudioOutputStream::SetParameters>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::SetParameters>;
out->common.get_parameters =
- Thunker<char*(const char *)>::call<
- &GceAudioOutputStream::GetParameters>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::GetParameters>;
out->common.add_audio_effect =
- Thunker<int(effect_handle_t)>::call<
- &GceAudioOutputStream::AddAudioEffect>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::AddAudioEffect>;
out->common.remove_audio_effect =
- Thunker<int(effect_handle_t)>::call<
- &GceAudioOutputStream::RemoveAudioEffect>;
+ cvd::thunk<audio_stream, &GceAudioOutputStream::RemoveAudioEffect>;
+
out->get_latency =
- OutThunker<uint32_t()>::call<
- &GceAudioOutputStream::GetLatency>;
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetLatency>;
out->set_volume =
- OutThunker<int(float, float)>::call<&GceAudioOutputStream::SetVolume>;
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::SetVolume>;
out->write =
- OutThunker<ssize_t(const void*, size_t)>::call<
- &GceAudioOutputStream::Write>;
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::Write>;
out->get_render_position =
- OutThunker<int(uint32_t*)>::call<
- &GceAudioOutputStream::GetRenderPosition>;
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetRenderPosition>;
out->get_next_write_timestamp =
- OutThunker<int(int64_t*)>::call<
- &GceAudioOutputStream::GetNextWriteTimestamp>;
+ cvd::thunk<audio_stream_out, &GceAudioOutputStream::GetNextWriteTimestamp>;
out->device_ = devices;
out->frame_size_ = GceAudioFrameSize(out.get());
diff --git a/guest/hals/audio/vsoc_audio_output_stream.h b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
similarity index 98%
rename from guest/hals/audio/vsoc_audio_output_stream.h
rename to guest/hals/audio/legacy/vsoc_audio_output_stream.h
index 14ea2f4..70ced6e 100644
--- a/guest/hals/audio/vsoc_audio_output_stream.h
+++ b/guest/hals/audio/legacy/vsoc_audio_output_stream.h
@@ -17,9 +17,9 @@
#include <memory>
+#include "common/libs/utils/simulated_buffer.h"
#include "common/vsoc/lib/vsoc_audio_message.h"
-#include "guest/hals/audio/audio_hal.h"
-#include "guest/hals/audio/simulated_buffer.h"
+#include "guest/hals/audio/legacy/audio_hal.h"
namespace cvd {
diff --git a/guest/hals/camera/Android.mk b/guest/hals/camera/Android.mk
index ceaded9..0b2a87f 100644
--- a/guest/hals/camera/Android.mk
+++ b/guest/hals/camera/Android.mk
@@ -124,7 +124,7 @@
$(if $(enable_emulated_camera2),$(emulated_camera2_src),) \
$(if $(enable_emulated_camera3),$(emulated_camera3_src),)
-LOCAL_MODULE := camera.vsoc
+LOCAL_MODULE := camera.cutf
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
@@ -175,7 +175,7 @@
LOCAL_C_INCLUDES += ${jpeg_c_includes}
LOCAL_SRC_FILES := ${jpeg_src}
-LOCAL_MODULE := camera.vsoc.jpeg
+LOCAL_MODULE := camera.cutf.jpeg
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
diff --git a/guest/hals/camera/JpegCompressor.cpp b/guest/hals/camera/JpegCompressor.cpp
index 2dae838..99164c9 100644
--- a/guest/hals/camera/JpegCompressor.cpp
+++ b/guest/hals/camera/JpegCompressor.cpp
@@ -46,10 +46,10 @@
NV21JpegCompressor::NV21JpegCompressor() {
if (mDl == NULL) {
- mDl = dlopen("/vendor/lib/hw/camera.vsoc.jpeg.so", RTLD_NOW);
+ mDl = dlopen("/vendor/lib/hw/camera.cutf.jpeg.so", RTLD_NOW);
}
if (mDl == NULL) {
- mDl = dlopen("/system/lib/hw/camera.vsoc.jpeg.so", RTLD_NOW);
+ mDl = dlopen("/system/lib/hw/camera.cutf.jpeg.so", RTLD_NOW);
}
assert(mDl != NULL);
diff --git a/guest/hals/camera/media_codecs.xml b/guest/hals/camera/media_codecs.xml
deleted file mode 100644
index 87d11f2..0000000
--- a/guest/hals/camera/media_codecs.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!-- Copyright (C) 2012 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.
--->
-
-<!--
-<!DOCTYPE MediaCodecs [
-<!ELEMENT Include EMPTY>
-<!ATTLIST Include href CDATA #REQUIRED>
-<!ELEMENT MediaCodecs (Decoders|Encoders|Include)*>
-<!ELEMENT Decoders (MediaCodec|Include)*>
-<!ELEMENT Encoders (MediaCodec|Include)*>
-<!ELEMENT MediaCodec (Type|Quirk|Include)*>
-<!ATTLIST MediaCodec name CDATA #REQUIRED>
-<!ATTLIST MediaCodec type CDATA>
-<!ELEMENT Type EMPTY>
-<!ATTLIST Type name CDATA #REQUIRED>
-<!ELEMENT Quirk EMPTY>
-<!ATTLIST Quirk name CDATA #REQUIRED>
-]>
-
-There's a simple and a complex syntax to declare the availability of a
-media codec:
-
-A codec that properly follows the OpenMax spec and therefore doesn't have any
-quirks and that only supports a single content type can be declared like so:
-
- <MediaCodec name="OMX.foo.bar" type="something/interesting" />
-
-If a codec has quirks OR supports multiple content types, the following syntax
-can be used:
-
- <MediaCodec name="OMX.foo.bar" >
- <Type name="something/interesting" />
- <Type name="something/else" />
- ...
- <Quirk name="requires-allocate-on-input-ports" />
- <Quirk name="requires-allocate-on-output-ports" />
- <Quirk name="output-buffers-are-unreadable" />
- </MediaCodec>
-
-Only the three quirks included above are recognized at this point:
-
-"requires-allocate-on-input-ports"
- must be advertised if the component does not properly support specification
- of input buffers using the OMX_UseBuffer(...) API but instead requires
- OMX_AllocateBuffer to be used.
-
-"requires-allocate-on-output-ports"
- must be advertised if the component does not properly support specification
- of output buffers using the OMX_UseBuffer(...) API but instead requires
- OMX_AllocateBuffer to be used.
-
-"output-buffers-are-unreadable"
- must be advertised if the emitted output buffers of a decoder component
- are not readable, i.e. use a custom format even though abusing one of
- the official OMX colorspace constants.
- Clients of such decoders will not be able to access the decoded data,
- naturally making the component much less useful. The only use for
- a component with this quirk is to render the output to the screen.
- Audio decoders MUST NOT advertise this quirk.
- Video decoders that advertise this quirk must be accompanied by a
- corresponding color space converter for thumbnail extraction,
- matching surfaceflinger support that can render the custom format to
- a texture and possibly other code, so just DON'T USE THIS QUIRK.
-
--->
-
-<MediaCodecs>
- <Include href="media_codecs_google_audio.xml" />
- <Include href="media_codecs_google_telephony.xml" />
- <Include href="media_codecs_google_video.xml" />
-</MediaCodecs>
diff --git a/guest/hals/camera/media_codecs_performance.xml b/guest/hals/camera/media_codecs_performance.xml
deleted file mode 100644
index f3eeb12..0000000
--- a/guest/hals/camera/media_codecs_performance.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!-- Copyright 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.
--->
-
-<!-- The 'range' values below are based on tests run 2016-04-13 on Ubuntu 14.04
- x86-64, Xeon 2.8 GHz x 10
-
- The range values are set to (meas / sqrt(tolerance)) and
- (meas * sqrt(tolerance)).
- These values maximize the 'success' window for the tests performed in
- cts/libs/deviceutil/src/android/cts/util/MediaUtils.java.
- That file defines 'tolerance' as sqrt(12.1).
-
- Where multiple results were obtained, the geometric mean was used.
-
- OMX.google.h264.encoder video/avc 320x 240 measured 1294.2
- OMX.google.h264.decoder video/avc 320x 240 measured 7204.1, 9151.4
-
- OMX.google.h263.encoder video/3gpp 176x 144 measured 2127.0
- OMX.google.h263.decoder video/3gpp 176x 144 measured 7574.0
-
- OMX.google.mpeg4.encoder video/mp4v-es 176x 144 measured 2783.8
- OMX.google.mpeg4.decoder video/mp4v-es 176x 144 measured 6954.2
-
- OMX.google.vp8.encoder video/x-vnd.on2.vp8 1280x 720 measured 195.0
- OMX.google.vp8.encoder video/x-vnd.on2.vp8 1920x1080 measured 93.3, 91.1
- OMX.google.vp8.decoder video/x-vnd.on2.vp8 1280x 720 measured 1196.1, 1211.3
- OMX.google.vp8.decoder video/x-vnd.on2.vp8 1920x1080 measured 483.7, 497.6
--->
-
-<MediaCodecs>
- <Encoders>
- <MediaCodec name="OMX.google.h264.encoder" type="video/avc" update="true">
- <Limit name="measured-frame-rate-320x240" range="694-2414" />
- </MediaCodec>
- <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp" update="true">
- <Limit name="measured-frame-rate-176x144" range="1140-3967" />
- </MediaCodec>
- <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es" update="true">
- <Limit name="measured-frame-rate-176x144" range="1493-5192" />
- </MediaCodec>
- <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8" update="true">
- <Limit name="measured-frame-rate-1280x720" range="105-364" />
- <Limit name="measured-frame-rate-1920x1080" range="49-172" />
- </MediaCodec>
- </Encoders>
- <Decoders>
- <MediaCodec name="OMX.google.h264.decoder" type="video/avc" update="true">
- <Limit name="measured-frame-rate-320x240" range="4353-15114" />
- </MediaCodec>
- <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" update="true">
- <Limit name="measured-frame-rate-176x144" range="4061-14126" />
- </MediaCodec>
- <MediaCodec name="OMX.google.hevc.decoder" type="video/hevc" update="true">
- <Limit name="measured-frame-rate-352x288" range="1000-4000" />
- <Limit name="measured-frame-rate-720x480" range="500-2000" />
- <Limit name="measured-frame-rate-1280x720" range="100-1000" />
- <Limit name="measured-frame-rate-1920x1080" range="50-700" />
- </MediaCodec>
- <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" update="true">
- <Limit name="measured-frame-rate-176x144" range="3729-12970" />
- </MediaCodec>
- <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" update="true">
- <Limit name="measured-frame-rate-1280x720" range="645-2245" />
- <Limit name="measured-frame-rate-1920x1080" range="263-915" />
- </MediaCodec>
- <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" update="true">
- <Limit name="measured-frame-rate-320x240" range="645-2245" />
- <Limit name="measured-frame-rate-640x360" range="500-3000" />
- <Limit name="measured-frame-rate-1280x720" range="350-1500" />
- <Limit name="measured-frame-rate-1920x1080" range="150-1000" />
- </MediaCodec>
- </Decoders>
-</MediaCodecs>
diff --git a/guest/hals/camera/media_profiles.xml b/guest/hals/camera/media_profiles.xml
deleted file mode 100644
index cd99857..0000000
--- a/guest/hals/camera/media_profiles.xml
+++ /dev/null
@@ -1,368 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-<!DOCTYPE MediaSettings [
-<!ELEMENT MediaSettings (CamcorderProfiles,
- EncoderOutputFileFormat+,
- VideoEncoderCap+,
- AudioEncoderCap+,
- VideoDecoderCap,
- AudioDecoderCap)>
-<!ELEMENT CamcorderProfiles (EncoderProfile+, ImageEncoding+, ImageDecoding, Camera)>
-<!ELEMENT EncoderProfile (Video, Audio)>
-<!ATTLIST EncoderProfile quality (high|low) #REQUIRED>
-<!ATTLIST EncoderProfile fileFormat (mp4|3gp) #REQUIRED>
-<!ATTLIST EncoderProfile duration (30|60) #REQUIRED>
-<!ATTLIST EncoderProfile cameraId (0|1) #REQUIRED>
-<!ELEMENT Video EMPTY>
-<!ATTLIST Video codec (h264|h263|m4v) #REQUIRED>
-<!ATTLIST Video bitRate CDATA #REQUIRED>
-<!ATTLIST Video width CDATA #REQUIRED>
-<!ATTLIST Video height CDATA #REQUIRED>
-<!ATTLIST Video frameRate CDATA #REQUIRED>
-<!ELEMENT Audio EMPTY>
-<!ATTLIST Audio codec (amrnb|amrwb|aac) #REQUIRED>
-<!ATTLIST Audio bitRate CDATA #REQUIRED>
-<!ATTLIST Audio sampleRate CDATA #REQUIRED>
-<!ATTLIST Audio channels (1|2) #REQUIRED>
-<!ELEMENT ImageEncoding EMPTY>
-<!ATTLIST ImageEncoding quality (90|80|70|60|50|40) #REQUIRED>
-<!ELEMENT ImageDecoding EMPTY>
-<!ATTLIST ImageDecoding memCap CDATA #REQUIRED>
-<!ELEMENT Camera EMPTY>
-<!ELEMENT EncoderOutputFileFormat EMPTY>
-<!ATTLIST EncoderOutputFileFormat name (mp4|3gp) #REQUIRED>
-<!ELEMENT VideoEncoderCap EMPTY>
-<!ATTLIST VideoEncoderCap name (h264|h263|m4v|wmv) #REQUIRED>
-<!ATTLIST VideoEncoderCap enabled (true|false) #REQUIRED>
-<!ATTLIST VideoEncoderCap minBitRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxBitRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameWidth CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameWidth CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameHeight CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameHeight CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap minFrameRate CDATA #REQUIRED>
-<!ATTLIST VideoEncoderCap maxFrameRate CDATA #REQUIRED>
-<!ELEMENT AudioEncoderCap EMPTY>
-<!ATTLIST AudioEncoderCap name (amrnb|amrwb|aac|wma) #REQUIRED>
-<!ATTLIST AudioEncoderCap enabled (true|false) #REQUIRED>
-<!ATTLIST AudioEncoderCap minBitRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap maxBitRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap minSampleRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap maxSampleRate CDATA #REQUIRED>
-<!ATTLIST AudioEncoderCap minChannels (1|2) #REQUIRED>
-<!ATTLIST AudioEncoderCap maxChannels (1|2) #REQUIRED>
-<!ELEMENT VideoDecoderCap EMPTY>
-<!ATTLIST VideoDecoderCap name (wmv) #REQUIRED>
-<!ATTLIST VideoDecoderCap enabled (true|false) #REQUIRED>
-<!ELEMENT AudioDecoderCap EMPTY>
-<!ATTLIST AudioDecoderCap name (wma) #REQUIRED>
-<!ATTLIST AudioDecoderCap enabled (true|false) #REQUIRED>
-]>
-<!--
- This file is used to declare the multimedia profiles and capabilities
- on an android-powered device.
--->
-<MediaSettings>
- <!-- Each camcorder profile defines a set of predefined configuration parameters -->
- <CamcorderProfiles cameraId="0">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="1">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="2">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="3">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="4">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="5">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <CamcorderProfiles cameraId="6">
-
- <EncoderProfile quality="qvga" fileFormat="mp4" duration="60">
- <Video codec="m4v"
- bitRate="128000"
- width="320"
- height="240"
- frameRate="15" />
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <EncoderProfile quality="timelapseqcif" fileFormat="mp4" duration="30">
- <Video codec="h264"
- bitRate="192000"
- width="176"
- height="144"
- frameRate="30" />
- <!-- audio setting is ignored -->
- <Audio codec="amrnb"
- bitRate="12200"
- sampleRate="8000"
- channels="1" />
- </EncoderProfile>
-
- <ImageEncoding quality="95" />
- <ImageEncoding quality="80" />
- <ImageEncoding quality="70" />
- <ImageDecoding memCap="20000000" />
-
- </CamcorderProfiles>
-
- <EncoderOutputFileFormat name="3gp" />
- <EncoderOutputFileFormat name="mp4" />
-
- <!--
- If a codec is not enabled, it is invisible to the applications
- In other words, the applications won't be able to use the codec
- or query the capabilities of the codec at all if it is disabled
- -->
- <VideoEncoderCap name="h264" enabled="true"
- minBitRate="64000" maxBitRate="192000"
- minFrameWidth="176" maxFrameWidth="320"
- minFrameHeight="144" maxFrameHeight="240"
- minFrameRate="15" maxFrameRate="30" />
-
- <VideoEncoderCap name="h263" enabled="true"
- minBitRate="64000" maxBitRate="192000"
- minFrameWidth="176" maxFrameWidth="320"
- minFrameHeight="144" maxFrameHeight="240"
- minFrameRate="15" maxFrameRate="30" />
-
- <VideoEncoderCap name="m4v" enabled="true"
- minBitRate="64000" maxBitRate="192000"
- minFrameWidth="176" maxFrameWidth="320"
- minFrameHeight="144" maxFrameHeight="240"
- minFrameRate="15" maxFrameRate="30" />
-
- <AudioEncoderCap name="aac" enabled="true"
- minBitRate="8000" maxBitRate="96000"
- minSampleRate="8000" maxSampleRate="48000"
- minChannels="1" maxChannels="1" />
-
- <AudioEncoderCap name="amrwb" enabled="true"
- minBitRate="6600" maxBitRate="23050"
- minSampleRate="16000" maxSampleRate="16000"
- minChannels="1" maxChannels="1" />
-
- <AudioEncoderCap name="amrnb" enabled="true"
- minBitRate="5525" maxBitRate="12200"
- minSampleRate="8000" maxSampleRate="8000"
- minChannels="1" maxChannels="1" />
-
- <!--
- FIXME:
- We do not check decoder capabilities at present
- At present, we only check whether windows media is visible
- for TEST applications. For other applications, we do
- not perform any checks at all.
- -->
- <VideoDecoderCap name="wmv" enabled="false"/>
- <AudioDecoderCap name="wma" enabled="false"/>
-</MediaSettings>
diff --git a/guest/hals/gatekeeper/Android.mk b/guest/hals/gatekeeper/Android.mk
index 03f684a..85d7346 100644
--- a/guest/hals/gatekeeper/Android.mk
+++ b/guest/hals/gatekeeper/Android.mk
@@ -24,7 +24,7 @@
endif
LOCAL_VENDOR_MODULE := true
-LOCAL_MODULE := gatekeeper.vsoc
+LOCAL_MODULE := gatekeeper.cutf
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_CFLAGS := -Wall -Wextra -Werror -Wunused
diff --git a/guest/hals/gps/Android.mk b/guest/hals/gps/Android.mk
index 1829c2f..9443980 100644
--- a/guest/hals/gps/Android.mk
+++ b/guest/hals/gps/Android.mk
@@ -28,7 +28,7 @@
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := gps_vsoc.cpp gps_thread.cpp
-LOCAL_MODULE := gps.vsoc
+LOCAL_MODULE := gps.cutf
LOCAL_C_INCLUDES := device/google/cuttlefish_common
LOCAL_HEADER_LIBRARIES := \
diff --git a/guest/hals/gralloc/Android.mk b/guest/hals/gralloc/Android.mk
index 5fe2d54..68ffffa 100644
--- a/guest/hals/gralloc/Android.mk
+++ b/guest/hals/gralloc/Android.mk
@@ -16,7 +16,7 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := gralloc.vsoc-future
+LOCAL_MODULE := gralloc.cutf_ivsh-future
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
diff --git a/guest/hals/gralloc/legacy/Android.mk b/guest/hals/gralloc/legacy/Android.mk
index fe01635..57dae50 100644
--- a/guest/hals/gralloc/legacy/Android.mk
+++ b/guest/hals/gralloc/legacy/Android.mk
@@ -29,7 +29,7 @@
$(VSOC_VERSION_CFLAGS)
include $(CLEAR_VARS)
-LOCAL_MODULE := gralloc.vsoc
+LOCAL_MODULE := gralloc.cutf
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
diff --git a/guest/hals/health/storage/Android.bp b/guest/hals/health/storage/Android.bp
index e81fea0..5c62bdf 100644
--- a/guest/hals/health/storage/Android.bp
+++ b/guest/hals/health/storage/Android.bp
@@ -17,7 +17,7 @@
cc_binary {
name: "android.hardware.health.storage@1.0-service.cuttlefish",
vendor: true,
- defaults: ["hidl_defaults"],
+ defaults: ["hidl_defaults", "cuttlefish_health_storage"],
relative_install_path: "hw",
init_rc: ["android.hardware.health.storage@1.0-service.cuttlefish.rc"],
srcs: [
@@ -37,8 +37,4 @@
"libhidltransport",
"libutils",
],
-
- vintf_fragments: [
- "manifest_android.hardware.health.storage@1.0.cuttlefish.xml",
- ],
}
diff --git a/guest/hals/hwcomposer/common/Android.bp b/guest/hals/hwcomposer/common/Android.bp
new file mode 100644
index 0000000..5e16041
--- /dev/null
+++ b/guest/hals/hwcomposer/common/Android.bp
@@ -0,0 +1,22 @@
+// 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_static {
+ name: "hwcomposer_common",
+ defaults: ["cuttlefish_guest_only"],
+ vendor: true,
+ srcs: ["hwcomposer.cpp"],
+ shared_libs: ["libhardware", "liblog"],
+}
diff --git a/guest/hals/hwcomposer/common/hwcomposer.cpp b/guest/hals/hwcomposer/common/hwcomposer.cpp
new file mode 100644
index 0000000..f18534b
--- /dev/null
+++ b/guest/hals/hwcomposer/common/hwcomposer.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.cf_x86"
+#define HWC_REMOVE_DEPRECATED_VERSIONS 1
+
+#include "hwcomposer.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <string>
+
+#include <log/log.h>
+
+namespace cvd {
+
+void* hwc_vsync_thread(void* data) {
+ struct hwc_composer_device_data_t* pdev =
+ (struct hwc_composer_device_data_t*)data;
+ 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 timestamp.
+ 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;
+}
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/common/hwcomposer.h
similarity index 67%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to guest/hals/hwcomposer/common/hwcomposer.h
index b6a037b..8c57e71 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/common/hwcomposer.h
@@ -1,6 +1,6 @@
#pragma once
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +15,15 @@
* limitations under the License.
*/
-#include "hwcomposer_common.h"
+#include <hardware/hwcomposer.h>
namespace cvd {
+ struct hwc_composer_device_data_t {
+ const hwc_procs_t* procs;
+ pthread_t vsync_thread;
+ int64_t vsync_base_timestamp;
+ int32_t vsync_period_ns;
+ };
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
+ void* hwc_vsync_thread(void* data);
} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/Android.bp b/guest/hals/hwcomposer/cutf_cvm/Android.bp
new file mode 100644
index 0000000..9e3962b
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/Android.bp
@@ -0,0 +1,44 @@
+// 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_cvm",
+ relative_install_path: "hw",
+ defaults: ["cuttlefish_guest_only"],
+ vendor: true,
+ srcs: [
+ "hwcomposer.cpp",
+ "geometry_utils.cpp",
+ "vsoc_composer.cpp",
+ "base_composer.cpp",
+ ],
+ include_dirs: [
+ "device/google/cuttlefish_common",
+ ],
+ export_include_dirs: ["."],
+ static_libs: ["libyuv_static", "hwcomposer_common"],
+ shared_libs: [
+ "liblog",
+ "libhardware",
+ "libbase",
+ "libcutils",
+ "libutils",
+ "libsync",
+ "libhardware",
+ "libjpeg",
+ "libcuttlefish_utils",
+ "libcuttlefish_fs",
+ ],
+}
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
new file mode 100644
index 0000000..23be7e1
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 "base_composer.h"
+
+#include <string.h>
+
+#include <cutils/properties.h>
+#include <hardware/gralloc.h>
+#include <log/log.h>
+
+#include <common/libs/utils/size_utils.h>
+#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+
+namespace cvd {
+
+BaseComposer::BaseComposer(int64_t vsync_base_timestamp)
+ : vsync_base_timestamp_(vsync_base_timestamp) {
+ vsync_period_ns_ = 1000000000 / frame_buffer_.refresh_rate();
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
+ reinterpret_cast<const hw_module_t**>(&gralloc_module_));
+}
+
+BaseComposer::~BaseComposer() {}
+
+void BaseComposer::Dump(char* buff __unused, int buff_len __unused) {}
+
+int BaseComposer::PostFrameBufferTarget(buffer_handle_t buffer_handle) {
+ int fb_index = frame_buffer_.NextScreenBuffer();
+ void* frame_buffer = frame_buffer_.GetBuffer(fb_index);
+ const private_handle_t* p_handle =
+ reinterpret_cast<const private_handle_t*>(buffer_handle);
+ void* buffer;
+ int retval = gralloc_module_->lock(gralloc_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, frame_buffer_.buffer_size());
+ frame_buffer_.Broadcast(fb_index);
+ return 0;
+} // namespace cvd
+
+int BaseComposer::PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ // find unsupported overlays
+ for (size_t i = 0; i < num_layers; i++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[i].compositionType)) {
+ continue;
+ }
+ layers[i].compositionType = HWC_FRAMEBUFFER;
+ }
+ return 0;
+}
+
+int BaseComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
+ for (size_t idx = 0; idx < num_layers; idx++) {
+ if (IS_TARGET_FRAMEBUFFER(layers[idx].compositionType)) {
+ return PostFrameBufferTarget(layers[idx].handle);
+ }
+ }
+ return -1;
+}
+
+FrameBuffer::FrameBuffer()
+ : screen_server_(cvd::SharedFD::VsockClient(
+ 2, property_get_int32("ro.boot.vsock_frames_port", 5580),
+ SOCK_STREAM)),
+ broadcast_thread_([this]() { BroadcastLoop(); }) {
+ if (screen_server_->IsOpen()) {
+ // TODO(b/128842613): Get this info from the configuration server
+ int32_t screen_params[4];
+ auto res = screen_server_->Read(screen_params, sizeof(screen_params));
+ if (res == sizeof(screen_params)) {
+ x_res_ = screen_params[0];
+ y_res_ = screen_params[1];
+ dpi_ = screen_params[2];
+ refresh_rate_ = screen_params[3];
+ } else {
+ LOG(ERROR) << "Unable to get screen configuration parameters from screen "
+ << "server (" << res << "): " << screen_server_->StrError();
+ }
+ } else {
+ LOG(ERROR) << "Unable to connect to screen server: "
+ << screen_server_->StrError();
+ }
+ // This needs to happen no matter what, otherwise there won't be a buffer for
+ // the set calls to compose on.
+ inner_buffer_ = std::vector<char>(FrameBuffer::buffer_size() * 8);
+}
+
+FrameBuffer::~FrameBuffer() {
+ running_ = false;
+ broadcast_thread_.join();
+}
+
+int FrameBuffer::NextScreenBuffer() {
+ int num_buffers = inner_buffer_.size() / buffer_size();
+ last_frame_buffer_ =
+ num_buffers > 0 ? (last_frame_buffer_ + 1) % num_buffers : -1;
+ return last_frame_buffer_;
+}
+
+void FrameBuffer::BroadcastLoop() {
+ if (!screen_server_->IsOpen()) {
+ LOG(ERROR) << "Broadcaster thread exiting due to no connection to screen"
+ << " server. Compositions will occur, but frames won't be sent"
+ << " anywhere";
+ return;
+ }
+ int32_t current_seq = 0;
+ int32_t current_offset;
+ while (running_) {
+ {
+ std::unique_lock<std::mutex> lock(mutex_);
+ while (current_seq == current_seq_) {
+ cond_var_.wait(lock);
+ }
+ current_offset = current_offset_;
+ current_seq = current_seq_;
+ }
+ int32_t size = buffer_size();
+ screen_server_->Write(&size, sizeof(size));
+ auto buff = static_cast<char*>(GetBuffer(current_offset));
+ while (size > 0) {
+ auto written = screen_server_->Write(buff, size);
+ size -= written;
+ buff += written;
+ }
+ }
+}
+
+void FrameBuffer::Broadcast(int32_t offset) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ current_offset_ = offset;
+ current_seq_++;
+ cond_var_.notify_all();
+}
+void* FrameBuffer::GetBuffer(int fb_index) {
+ return &inner_buffer_[buffer_size() * fb_index];
+}
+size_t FrameBuffer::buffer_size() {
+ return (line_length() * y_res()) + 4;
+}
+int32_t FrameBuffer::x_res() { return x_res_; }
+int32_t FrameBuffer::y_res() { return y_res_; }
+int32_t FrameBuffer::line_length() {
+ return cvd::AlignToPowerOf2(x_res() * bytes_per_pixel(), 4);
+}
+int32_t FrameBuffer::bytes_per_pixel() { return 4; }
+int32_t FrameBuffer::dpi() { return dpi_; }
+int32_t FrameBuffer::refresh_rate() { return refresh_rate_; }
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/cutf_cvm/base_composer.h b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
new file mode 100644
index 0000000..5a23176
--- /dev/null
+++ b/guest/hals/hwcomposer/cutf_cvm/base_composer.h
@@ -0,0 +1,98 @@
+#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 <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <hardware/gralloc.h>
+#include <common/libs/fs/shared_fd.h>
+#include "hwcomposer.h"
+
+namespace cvd {
+
+class FrameBuffer{
+ public:
+ FrameBuffer();
+ ~FrameBuffer();
+
+ void Broadcast(int32_t offset);
+ int NextScreenBuffer();
+ void* GetBuffer(int fb_index);
+ size_t buffer_size();
+ int32_t x_res();
+ int32_t y_res();
+ int32_t line_length();
+ int32_t bytes_per_pixel();
+ int32_t dpi();
+ int32_t refresh_rate();
+ private:
+ void BroadcastLoop();
+
+ std::vector<char> inner_buffer_;
+ int last_frame_buffer_ = 0;
+ cvd::SharedFD screen_server_;
+ std::thread broadcast_thread_;
+ int32_t current_offset_ = 0;
+ int32_t current_seq_ = 0;
+ std::mutex mutex_;
+ std::condition_variable cond_var_;
+ bool running_ = true;
+ int32_t x_res_{720};
+ int32_t y_res_{1280};
+ int32_t dpi_{160};
+ int32_t refresh_rate_{60};
+};
+
+class BaseComposer {
+ public:
+ BaseComposer(int64_t vsync_base_timestamp);
+ ~BaseComposer();
+
+ // Sets the composition type of each layer and returns the number of layers
+ // to be composited by the hwcomposer.
+ int PrepareLayers(size_t num_layers, vsoc_hwc_layer* layers);
+ // Returns 0 if successful.
+ int SetLayers(size_t num_layers, vsoc_hwc_layer* layers);
+ void Dump(char* buff, int buff_len);
+
+ int32_t x_res() {
+ return frame_buffer_.x_res();
+ }
+ int32_t y_res() {
+ return frame_buffer_.y_res();
+ }
+ int32_t dpi() {
+ return frame_buffer_.dpi();
+ }
+ int32_t refresh_rate() {
+ return frame_buffer_.refresh_rate();
+ }
+
+ protected:
+ const gralloc_module_t* gralloc_module_;
+ int64_t vsync_base_timestamp_;
+ int32_t vsync_period_ns_;
+ FrameBuffer frame_buffer_;
+
+ private:
+ // Returns buffer offset or negative on error.
+ int PostFrameBufferTarget(buffer_handle_t handle);
+};
+
+} // namespace cvd
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
similarity index 100%
copy from guest/hals/hwcomposer/legacy/geometry_utils.cpp
copy to guest/hals/hwcomposer/cutf_cvm/geometry_utils.cpp
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
similarity index 95%
rename from guest/hals/hwcomposer/legacy/geometry_utils.h
rename to guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
index b6a037b..937283f 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/cutf_cvm/geometry_utils.h
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
similarity index 81%
copy from guest/hals/hwcomposer/legacy/hwcomposer.cpp
copy to guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
index 9711656..3db7f80 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.cpp
@@ -53,36 +53,26 @@
#include <utils/String8.h>
#include <utils/Vector.h>
-#include "common/vsoc/lib/screen_region_view.h"
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
#include <sync/sync.h>
#include "base_composer.h"
#include "geometry_utils.h"
-#include "hwcomposer_common.h"
-#include "stats_keeper.h"
+#include "hwcomposer.h"
#include "vsoc_composer.h"
-using vsoc::screen::ScreenRegionView;
-
#ifdef USE_OLD_HWCOMPOSER
typedef cvd::BaseComposer InnerComposerType;
#else
typedef cvd::VSoCComposer InnerComposerType;
#endif
-#ifdef GATHER_STATS
-typedef cvd::StatsKeepingComposer<InnerComposerType> ComposerType;
-#else
typedef InnerComposerType ComposerType;
-#endif
struct vsoc_hwc_composer_device_1_t {
vsoc_hwc_device base;
- const hwc_procs_t* procs;
- pthread_t vsync_thread;
- int64_t vsync_base_timestamp;
- int32_t vsync_period_ns;
+ cvd::hwc_composer_device_data_t vsync_data;
ComposerType* composer;
};
@@ -309,7 +299,7 @@
const hwc_procs_t* procs) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)dev;
- pdev->procs = procs;
+ pdev->vsync_data.procs = procs;
}
static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
@@ -322,7 +312,7 @@
value[0] = 0;
break;
case HWC_VSYNC_PERIOD:
- value[0] = pdev->vsync_period_ns;
+ value[0] = pdev->vsync_data.vsync_period_ns;
break;
default:
// unsupported query
@@ -345,65 +335,6 @@
return -EINVAL;
}
-static void* hwc_vsync_thread(void* data) {
- struct vsoc_hwc_composer_device_1_t* pdev =
- (struct vsoc_hwc_composer_device_1_t*)data;
- 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 timestamp.
- 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;
-}
-
static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
return 0;
@@ -430,22 +361,21 @@
#if VSOC_PLATFORM_SDK_AFTER(J)
static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
const uint32_t attribute) {
- auto screen_view = ScreenRegionView::GetInstance();
switch (attribute) {
case HWC_DISPLAY_VSYNC_PERIOD:
- return pdev->vsync_period_ns;
+ return pdev->vsync_data.vsync_period_ns;
case HWC_DISPLAY_WIDTH:
- return screen_view->x_res();
+ return pdev->composer->x_res();
case HWC_DISPLAY_HEIGHT:
- return screen_view->y_res();
+ return pdev->composer->y_res();
case HWC_DISPLAY_DPI_X:
- ALOGI("Reporting DPI_X of %d", screen_view->dpi());
+ ALOGI("Reporting DPI_X of %d", pdev->composer->dpi());
// The number of pixels per thousand inches
- return screen_view->dpi() * 1000;
+ return pdev->composer->dpi() * 1000;
case HWC_DISPLAY_DPI_Y:
- ALOGI("Reporting DPI_Y of %d", screen_view->dpi());
+ ALOGI("Reporting DPI_Y of %d", pdev->composer->dpi());
// The number of pixels per thousand inches
- return screen_view->dpi() * 1000;
+ return pdev->composer->dpi() * 1000;
default:
ALOGE("unknown display attribute %u", attribute);
return -EINVAL;
@@ -476,8 +406,8 @@
struct vsoc_hwc_composer_device_1_t* dev =
(struct vsoc_hwc_composer_device_1_t*)device;
ALOGE("vsoc_hwc_close");
- pthread_kill(dev->vsync_thread, SIGTERM);
- pthread_join(dev->vsync_thread, NULL);
+ pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+ pthread_join(dev->vsync_data.vsync_thread, NULL);
delete dev->composer;
delete dev;
return 0;
@@ -497,14 +427,12 @@
return -ENOMEM;
}
- int refreshRate = ScreenRegionView::GetInstance()->refresh_rate_hz();
- dev->vsync_period_ns = 1000000000 / refreshRate;
struct timespec rt;
if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
strerror(errno));
}
- dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+ dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
dev->base.common.tag = HARDWARE_DEVICE_TAG;
dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
@@ -525,10 +453,10 @@
dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
#endif
- dev->composer =
- new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
-
- int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+ dev->composer = new ComposerType(dev->vsync_data.vsync_base_timestamp);
+ dev->vsync_data.vsync_period_ns = 1000000000 / dev->composer->refresh_rate();
+ int ret = pthread_create(&dev->vsync_data.vsync_thread,
+ NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
if (ret) {
ALOGE("failed to start vsync thread: %s", strerror(ret));
ret = -ret;
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
similarity index 100%
copy from guest/hals/hwcomposer/legacy/hwcomposer_common.h
copy to guest/hals/hwcomposer/cutf_cvm/hwcomposer.h
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
similarity index 94%
copy from guest/hals/hwcomposer/legacy/vsoc_composer.cpp
copy to guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
index 179e930..5b55e84 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.cpp
@@ -27,12 +27,9 @@
#include <libyuv.h>
#include <system/graphics.h>
-#include "common/vsoc/lib/screen_region_view.h"
+#include <common/libs/utils/size_utils.h>
#include "geometry_utils.h"
-#include "hwcomposer_common.h"
-
-using vsoc::screen::ScreenRegionView;
namespace cvd {
@@ -177,9 +174,9 @@
uint8_t* src_y = src.buffer;
int stride_y = stride_in_pixels;
uint8_t* src_v = src_y + stride_y * src.height;
- int stride_v = ScreenRegionView::align(stride_y / 2, 16);
+ int stride_v = cvd::AlignToPowerOf2(stride_y / 2, 4);
uint8_t* src_u = src_v + stride_v * src.height / 2;
- int stride_u = ScreenRegionView::align(stride_y / 2, 16);
+ int stride_u = cvd::AlignToPowerOf2(stride_y / 2, 4);
// Adjust for crop
src_y += src.crop_y * stride_y + src.crop_x;
@@ -317,7 +314,7 @@
uint8_t* src_buffer;
uint8_t* dst_buffer = reinterpret_cast<uint8_t*>(
- ScreenRegionView::GetInstance()->GetBuffer(buffer_idx));
+ frame_buffer_.GetBuffer(buffer_idx));
int retval = gralloc_module_->lock(
gralloc_module_, src_layer->handle, GRALLOC_USAGE_SW_READ_OFTEN, 0, 0,
src_priv_handle->x_res, src_priv_handle->y_res,
@@ -345,10 +342,9 @@
src_layer->sourceCrop.bottom - src_layer->sourceCrop.top;
src_layer_spec.format = src_priv_handle->format;
- auto screen_view = ScreenRegionView::GetInstance();
- BufferSpec dst_layer_spec(dst_buffer, screen_view->buffer_size(),
- screen_view->x_res(), screen_view->y_res(),
- screen_view->line_length());
+ BufferSpec dst_layer_spec(dst_buffer, frame_buffer_.buffer_size(),
+ frame_buffer_.x_res(), frame_buffer_.y_res(),
+ frame_buffer_.line_length());
dst_layer_spec.crop_x = src_layer->displayFrame.left;
dst_layer_spec.crop_y = src_layer->displayFrame.top;
dst_layer_spec.crop_width =
@@ -377,12 +373,12 @@
int y_res = src_layer->displayFrame.bottom - src_layer->displayFrame.top;
size_t output_frame_size =
x_res *
- ScreenRegionView::align(y_res * screen_view->bytes_per_pixel(), 16);
+ cvd::AlignToPowerOf2(y_res * frame_buffer_.bytes_per_pixel(), 4);
while (needed_tmp_buffers > 0) {
BufferSpec tmp(RotateTmpBuffer(needed_tmp_buffers), output_frame_size,
x_res, y_res,
- ScreenRegionView::align(
- x_res * screen_view->bytes_per_pixel(), 16));
+ cvd::AlignToPowerOf2(
+ x_res * frame_buffer_.bytes_per_pixel(), 4));
dest_buffer_stack.push_back(tmp);
needed_tmp_buffers--;
}
@@ -403,8 +399,8 @@
// Make width and height match the crop sizes on the source
int src_width = src_layer_spec.crop_width;
int src_height = src_layer_spec.crop_height;
- int dst_stride = ScreenRegionView::align(
- src_width * screen_view->bytes_per_pixel(), 16);
+ int dst_stride = cvd::AlignToPowerOf2(
+ src_width * frame_buffer_.bytes_per_pixel(), 4);
size_t needed_size = dst_stride * src_height;
dst_buffer_spec.width = src_width;
dst_buffer_spec.height = src_height;
@@ -444,7 +440,7 @@
// TODO (jemoreira): Aligment (To align here may cause the needed size to
// be bigger than the buffer, so care should be taken)
dst_buffer_spec.stride =
- dst_buffer_spec.width * screen_view->bytes_per_pixel();
+ dst_buffer_spec.width * frame_buffer_.bytes_per_pixel();
}
retval = DoScaling(src_layer_spec, dst_buffer_spec, needs_vflip);
needs_vflip = false;
@@ -504,11 +500,10 @@
/* static */ const int VSoCComposer::kNumTmpBufferPieces = 2;
-VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp,
- int32_t vsync_period_ns)
- : BaseComposer(vsync_base_timestamp, vsync_period_ns),
+VSoCComposer::VSoCComposer(int64_t vsync_base_timestamp)
+ : BaseComposer(vsync_base_timestamp),
tmp_buffer_(kNumTmpBufferPieces *
- ScreenRegionView::GetInstance()->buffer_size()) {}
+ frame_buffer_.buffer_size()) {}
VSoCComposer::~VSoCComposer() {}
@@ -556,7 +551,7 @@
int VSoCComposer::SetLayers(size_t num_layers, vsoc_hwc_layer* layers) {
int targetFbs = 0;
- int buffer_idx = NextScreenBuffer();
+ int buffer_idx = frame_buffer_.NextScreenBuffer();
// The framebuffer target layer should be composed if at least one layers was
// marked HWC_FRAMEBUFFER or if it's the only layer in the composition
@@ -596,7 +591,7 @@
if (targetFbs != 1) {
ALOGW("Saw %zu layers, posted=%d", num_layers, targetFbs);
}
- Broadcast(buffer_idx);
+ frame_buffer_.Broadcast(buffer_idx);
return 0;
}
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.h b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
similarity index 92%
copy from guest/hals/hwcomposer/legacy/vsoc_composer.h
copy to guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
index 48f290f..7007931 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.h
+++ b/guest/hals/hwcomposer/cutf_cvm/vsoc_composer.h
@@ -22,13 +22,13 @@
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
#include "base_composer.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
class VSoCComposer : public BaseComposer {
public:
- VSoCComposer(int64_t vsync_base_timestamp, int32_t vsync_period_ns);
+ VSoCComposer(int64_t vsync_base_timestamp);
~VSoCComposer();
// override
diff --git a/guest/hals/hwcomposer/Android.mk b/guest/hals/hwcomposer/vsoc-future/Android.mk
similarity index 96%
rename from guest/hals/hwcomposer/Android.mk
rename to guest/hals/hwcomposer/vsoc-future/Android.mk
index 1a8c44f..bcc104e 100644
--- a/guest/hals/hwcomposer/Android.mk
+++ b/guest/hals/hwcomposer/vsoc-future/Android.mk
@@ -16,7 +16,7 @@
include $(CLEAR_VARS)
-LOCAL_MODULE := hwcomposer.vsoc-future
+LOCAL_MODULE := hwcomposer.cutf_ivsh-future
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := \
diff --git a/guest/hals/hwcomposer/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
similarity index 100%
rename from guest/hals/hwcomposer/hwcomposer.cpp
rename to guest/hals/hwcomposer/vsoc-future/hwcomposer.cpp
diff --git a/guest/hals/hwcomposer/legacy/Android.mk b/guest/hals/hwcomposer/vsoc/Android.mk
similarity index 93%
rename from guest/hals/hwcomposer/legacy/Android.mk
rename to guest/hals/hwcomposer/vsoc/Android.mk
index f2fe5c3..2d22f2d 100644
--- a/guest/hals/hwcomposer/legacy/Android.mk
+++ b/guest/hals/hwcomposer/vsoc/Android.mk
@@ -21,7 +21,7 @@
include $(CLEAR_VARS)
include $(LOCAL_PATH)/hwcomposer.mk
LOCAL_CFLAGS += -DUSE_OLD_HWCOMPOSER -Wall -Werror
-LOCAL_MODULE := hwcomposer.vsoc-deprecated
+LOCAL_MODULE := hwcomposer.cutf_ivsh-deprecated
# See b/67109557
ifeq (true, $(TARGET_TRANSLATE_2ND_ARCH))
@@ -33,7 +33,7 @@
# New hwcomposer, performs software composition
include $(CLEAR_VARS)
include $(LOCAL_PATH)/hwcomposer.mk
-LOCAL_MODULE := hwcomposer.vsoc
+LOCAL_MODULE := hwcomposer.cutf_ivsh
LOCAL_VENDOR_MODULE := true
# See b/67109557
diff --git a/guest/hals/hwcomposer/legacy/base_composer.cpp b/guest/hals/hwcomposer/vsoc/base_composer.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/base_composer.cpp
rename to guest/hals/hwcomposer/vsoc/base_composer.cpp
diff --git a/guest/hals/hwcomposer/legacy/base_composer.h b/guest/hals/hwcomposer/vsoc/base_composer.h
similarity index 98%
rename from guest/hals/hwcomposer/legacy/base_composer.h
rename to guest/hals/hwcomposer/vsoc/base_composer.h
index 9e8d10c..f3467ef 100644
--- a/guest/hals/hwcomposer/legacy/base_composer.h
+++ b/guest/hals/hwcomposer/vsoc/base_composer.h
@@ -18,7 +18,7 @@
#include <functional>
#include <hardware/gralloc.h>
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.cpp b/guest/hals/hwcomposer/vsoc/geometry_utils.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/geometry_utils.cpp
rename to guest/hals/hwcomposer/vsoc/geometry_utils.cpp
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/guest/hals/hwcomposer/vsoc/geometry_utils.h
similarity index 95%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to guest/hals/hwcomposer/vsoc/geometry_utils.h
index b6a037b..937283f 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/guest/hals/hwcomposer/vsoc/geometry_utils.h
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.cpp b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
similarity index 85%
rename from guest/hals/hwcomposer/legacy/hwcomposer.cpp
rename to guest/hals/hwcomposer/vsoc/hwcomposer.cpp
index 9711656..16b63ba 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.cpp
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.cpp
@@ -55,11 +55,12 @@
#include "common/vsoc/lib/screen_region_view.h"
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
+#include "guest/hals/hwcomposer/common/hwcomposer.h"
#include <sync/sync.h>
#include "base_composer.h"
#include "geometry_utils.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
#include "stats_keeper.h"
#include "vsoc_composer.h"
@@ -79,10 +80,7 @@
struct vsoc_hwc_composer_device_1_t {
vsoc_hwc_device base;
- const hwc_procs_t* procs;
- pthread_t vsync_thread;
- int64_t vsync_base_timestamp;
- int32_t vsync_period_ns;
+ cvd::hwc_composer_device_data_t vsync_data;
ComposerType* composer;
};
@@ -309,7 +307,7 @@
const hwc_procs_t* procs) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)dev;
- pdev->procs = procs;
+ pdev->vsync_data.procs = procs;
}
static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
@@ -322,7 +320,7 @@
value[0] = 0;
break;
case HWC_VSYNC_PERIOD:
- value[0] = pdev->vsync_period_ns;
+ value[0] = pdev->vsync_data.vsync_period_ns;
break;
default:
// unsupported query
@@ -345,65 +343,6 @@
return -EINVAL;
}
-static void* hwc_vsync_thread(void* data) {
- struct vsoc_hwc_composer_device_1_t* pdev =
- (struct vsoc_hwc_composer_device_1_t*)data;
- 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 timestamp.
- 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;
-}
-
static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
return 0;
@@ -433,7 +372,7 @@
auto screen_view = ScreenRegionView::GetInstance();
switch (attribute) {
case HWC_DISPLAY_VSYNC_PERIOD:
- return pdev->vsync_period_ns;
+ return pdev->vsync_data.vsync_period_ns;
case HWC_DISPLAY_WIDTH:
return screen_view->x_res();
case HWC_DISPLAY_HEIGHT:
@@ -476,8 +415,8 @@
struct vsoc_hwc_composer_device_1_t* dev =
(struct vsoc_hwc_composer_device_1_t*)device;
ALOGE("vsoc_hwc_close");
- pthread_kill(dev->vsync_thread, SIGTERM);
- pthread_join(dev->vsync_thread, NULL);
+ pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
+ pthread_join(dev->vsync_data.vsync_thread, NULL);
delete dev->composer;
delete dev;
return 0;
@@ -498,13 +437,13 @@
}
int refreshRate = ScreenRegionView::GetInstance()->refresh_rate_hz();
- dev->vsync_period_ns = 1000000000 / refreshRate;
+ dev->vsync_data.vsync_period_ns = 1000000000 / refreshRate;
struct timespec rt;
if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
strerror(errno));
}
- dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
+ dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
dev->base.common.tag = HARDWARE_DEVICE_TAG;
dev->base.common.version = VSOC_HWC_DEVICE_API_VERSION;
@@ -526,9 +465,9 @@
dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
#endif
dev->composer =
- new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
+ new ComposerType(dev->vsync_data.vsync_base_timestamp, dev->vsync_data.vsync_period_ns);
- int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
+ int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL, cvd::hwc_vsync_thread, &dev->vsync_data);
if (ret) {
ALOGE("failed to start vsync thread: %s", strerror(ret));
ret = -ret;
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer_common.h b/guest/hals/hwcomposer/vsoc/hwcomposer.h
similarity index 100%
rename from guest/hals/hwcomposer/legacy/hwcomposer_common.h
rename to guest/hals/hwcomposer/vsoc/hwcomposer.h
diff --git a/guest/hals/hwcomposer/legacy/hwcomposer.mk b/guest/hals/hwcomposer/vsoc/hwcomposer.mk
similarity index 95%
rename from guest/hals/hwcomposer/legacy/hwcomposer.mk
rename to guest/hals/hwcomposer/vsoc/hwcomposer.mk
index 1414dca..cb27c95 100644
--- a/guest/hals/hwcomposer/legacy/hwcomposer.mk
+++ b/guest/hals/hwcomposer/vsoc/hwcomposer.mk
@@ -34,14 +34,15 @@
$(VSOC_STLPORT_LIBS)
LOCAL_STATIC_LIBRARIES := \
- libyuv_static
+ libyuv_static \
+ hwcomposer_common \
LOCAL_SRC_FILES := \
geometry_utils.cpp \
hwcomposer.cpp \
vsoc_composer.cpp \
stats_keeper.cpp \
- base_composer.cpp
+ base_composer.cpp \
LOCAL_CFLAGS += \
-DGATHER_STATS \
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.cpp b/guest/hals/hwcomposer/vsoc/stats_keeper.cpp
similarity index 100%
rename from guest/hals/hwcomposer/legacy/stats_keeper.cpp
rename to guest/hals/hwcomposer/vsoc/stats_keeper.cpp
diff --git a/guest/hals/hwcomposer/legacy/stats_keeper.h b/guest/hals/hwcomposer/vsoc/stats_keeper.h
similarity index 99%
rename from guest/hals/hwcomposer/legacy/stats_keeper.h
rename to guest/hals/hwcomposer/vsoc/stats_keeper.h
index c813920..e1dd330 100644
--- a/guest/hals/hwcomposer/legacy/stats_keeper.h
+++ b/guest/hals/hwcomposer/vsoc/stats_keeper.h
@@ -24,7 +24,7 @@
#include "common/libs/time/monotonic_time.h"
#include "common/vsoc/lib/screen_region_view.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp b/guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
similarity index 99%
rename from guest/hals/hwcomposer/legacy/vsoc_composer.cpp
rename to guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
index 179e930..33b08e1 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.cpp
+++ b/guest/hals/hwcomposer/vsoc/vsoc_composer.cpp
@@ -30,7 +30,6 @@
#include "common/vsoc/lib/screen_region_view.h"
#include "geometry_utils.h"
-#include "hwcomposer_common.h"
using vsoc::screen::ScreenRegionView;
diff --git a/guest/hals/hwcomposer/legacy/vsoc_composer.h b/guest/hals/hwcomposer/vsoc/vsoc_composer.h
similarity index 97%
rename from guest/hals/hwcomposer/legacy/vsoc_composer.h
rename to guest/hals/hwcomposer/vsoc/vsoc_composer.h
index 48f290f..0e8e2c2 100644
--- a/guest/hals/hwcomposer/legacy/vsoc_composer.h
+++ b/guest/hals/hwcomposer/vsoc/vsoc_composer.h
@@ -22,7 +22,7 @@
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
#include "base_composer.h"
-#include "hwcomposer_common.h"
+#include "hwcomposer.h"
namespace cvd {
diff --git a/guest/hals/lights/Android.mk b/guest/hals/lights/Android.mk
index 6add0f2..8c292dc 100644
--- a/guest/hals/lights/Android.mk
+++ b/guest/hals/lights/Android.mk
@@ -29,7 +29,7 @@
LOCAL_HEADER_LIBRARIES := libhardware_headers
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := lights_vsoc.c
-LOCAL_MODULE := lights.vsoc
+LOCAL_MODULE := lights.cutf
LOCAL_CFLAGS += -DLIGHT_BACKLIGHT -DLOG_TAG=\"VSoC-lights\" $(VSOC_VERSION_CFLAGS)
LOCAL_VENDOR_MODULE := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/power/Android.mk b/guest/hals/power/Android.mk
index d5ead05..d73bc0f 100644
--- a/guest/hals/power/Android.mk
+++ b/guest/hals/power/Android.mk
@@ -32,7 +32,7 @@
LOCAL_HEADER_LIBRARIES := libhardware_headers libutils_headers
LOCAL_SHARED_LIBRARIES := liblog libcutils
LOCAL_SRC_FILES := power.c
-LOCAL_MODULE := power.vsoc
+LOCAL_MODULE := power.cutf
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/ril/Android.mk b/guest/hals/ril/Android.mk
index ccf0910..0110fd2 100644
--- a/guest/hals/ril/Android.mk
+++ b/guest/hals/ril/Android.mk
@@ -17,17 +17,17 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- vsoc_ril.cpp
+ cuttlefish_ril.cpp
LOCAL_SHARED_LIBRARIES := \
liblog \
libcutils \
libutils \
+ ${CUTTLEFISH_LIBRIL_NAME} \
libcuttlefish_fs \
cuttlefish_net \
cuttlefish_auto_resources \
libbase \
- vsoc_lib
LOCAL_C_INCLUDES := \
device/google/cuttlefish_common \
@@ -38,59 +38,7 @@
-Werror \
$(VSOC_VERSION_CFLAGS)
-# only for PLATFORM_VERSION greater or equal to Q
-ifeq ($(PLATFORM_VERSION), $(word 1, $(sort Q $(PLATFORM_VERSION))))
-
- LOCAL_SRC_FILES += \
- libril/ril.cpp \
- libril/ril_service.cpp \
- libril/ril_event.cpp \
- libril/RilSapSocket.cpp \
- libril/sap_service.cpp
-
- LOCAL_SHARED_LIBRARIES += \
- libhardware_legacy \
- libhidlbase \
- libhidltransport \
- libhwbinder \
- librilutils \
- android.hardware.radio@1.0 \
- android.hardware.radio@1.1 \
- android.hardware.radio.deprecated@1.0 \
- android.hardware.radio@1.2 \
- android.hardware.radio@1.3 \
- android.hardware.radio@1.4
-
-
- LOCAL_STATIC_LIBRARIES := \
- libprotobuf-c-nano-enable_malloc \
-
- LOCAL_C_INCLUDES += \
- external/nanopb-c \
- hardware/ril/include \
- hardware/ril/libril
-
- LOCAL_CFLAGS += \
- -Wextra \
- -Wno-unused-parameter
-
- LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
- ifeq ($(SIM_COUNT), 2)
- LOCAL_CFLAGS += -DANDROID_MULTI_SIM -DDSDA_RILD1
- LOCAL_CFLAGS += -DANDROID_SIM_COUNT_2
- endif
-
- ifneq ($(DISABLE_RILD_OEM_HOOK),)
- LOCAL_CFLAGS += -DOEM_HOOK_DISABLED
- endif
-else
- $(info Use deprecated libril)
- LOCAL_SHARED_LIBRARIES += \
- libril
-endif
-
-LOCAL_MODULE:= libvsoc-ril
+LOCAL_MODULE:= libcuttlefish-ril
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
@@ -100,3 +48,5 @@
endif
include $(BUILD_SHARED_LIBRARY)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/guest/hals/ril/vsoc_ril.cpp b/guest/hals/ril/cuttlefish_ril.cpp
similarity index 95%
rename from guest/hals/ril/vsoc_ril.cpp
rename to guest/hals/ril/cuttlefish_ril.cpp
index d6138f0..0acc452 100644
--- a/guest/hals/ril/vsoc_ril.cpp
+++ b/guest/hals/ril/cuttlefish_ril.cpp
@@ -14,9 +14,10 @@
** limitations under the License.
*/
-#include "guest/hals/ril/vsoc_ril.h"
+#include "guest/hals/ril/cuttlefish_ril.h"
#include <cutils/properties.h>
+#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
@@ -26,13 +27,13 @@
#include <string>
#include <vector>
+#include "common/libs/constants/ril.h"
#include "common/libs/net/netlink_client.h"
#include "common/libs/net/network_interface.h"
#include "common/libs/net/network_interface_manager.h"
-#include "common/vsoc/lib/ril_region_view.h"
#include "guest/libs/platform_support/api_level_fixes.h"
-#define VSOC_RIL_VERSION_STRING "Android VSoC RIL 1.4"
+#define CUTTLEFISH_RIL_VERSION_STRING "Android Cuttlefish RIL 1.4"
/* Modem Technology bits */
#define MDM_GSM 0x01
@@ -61,6 +62,49 @@
RUIM_NETWORK_PERSONALIZATION = 11
} SIM_Status;
+class RilConfig {
+ public:
+ static void InitRilConfig();
+
+ static char* address_and_prefixlength() {
+ return RilConfig::global_ril_config_.address_and_prefixlength_;
+ }
+
+ static char* dns() {
+ return RilConfig::global_ril_config_.dns_;
+ }
+
+ static char* gateway() {
+ return RilConfig::global_ril_config_.gateway_;
+ }
+
+ static char* ipaddr() {
+ return RilConfig::global_ril_config_.ipaddr_;
+ }
+
+ static int prefixlen() {
+ return RilConfig::global_ril_config_.prefixlen_;
+ }
+
+ static char* broadcast() {
+ return RilConfig::global_ril_config_.broadcast_;
+ }
+
+ private:
+ RilConfig() = default;
+ RilConfig(const RilConfig&) = default;
+
+ char ipaddr_[16]; // xxx.xxx.xxx.xxx\0 = 16 bytes
+ char gateway_[16];
+ char dns_[16];
+ char broadcast_[16];
+ char address_and_prefixlength_[19]; // <ipaddr>/dd
+ int prefixlen_;
+
+ static RilConfig global_ril_config_;
+};
+RilConfig RilConfig::global_ril_config_;
+
static const struct RIL_Env* gce_ril_env;
static const struct timeval TIMEVAL_SIMPOLL = {3, 0};
@@ -130,6 +174,43 @@
return false;
}
+static bool ReadStringProperty(char* dst, const char* key, size_t max_size) {
+ char buffer[PROPERTY_VALUE_MAX];
+ auto res = property_get(key, buffer, NULL);
+ if (res < 0) {
+ ALOGE("Failed to read property %s", key);
+ return false;
+ }
+ if (res > static_cast<int>(max_size - 1)) {
+ ALOGE("Invalid value in property %s: value too long: %s", key, buffer);
+ return false;
+ }
+ snprintf(dst, res + 1, "%s", buffer);
+ return true;
+}
+
+void RilConfig::InitRilConfig() {
+ RilConfig tmp_config;
+ ReadStringProperty(&tmp_config.ipaddr_[0], CUTTLEFISH_RIL_ADDR_PROPERTY,
+ sizeof(tmp_config.ipaddr_));
+ ReadStringProperty(&tmp_config.gateway_[0],
+ CUTTLEFISH_RIL_GATEWAY_PROPERTY,
+ sizeof(tmp_config.gateway_));
+ ReadStringProperty(&tmp_config.dns_[0], CUTTLEFISH_RIL_DNS_PROPERTY,
+ sizeof(tmp_config.dns_));
+ ReadStringProperty(&tmp_config.broadcast_[0],
+ CUTTLEFISH_RIL_BROADCAST_PROPERTY,
+ sizeof(tmp_config.broadcast_));
+ tmp_config.prefixlen_ =
+ property_get_int32(CUTTLEFISH_RIL_PREFIXLEN_PROPERTY, 30);
+
+ snprintf(&tmp_config.address_and_prefixlength_[0],
+ sizeof(tmp_config.address_and_prefixlength_), "%s/%d",
+ tmp_config.ipaddr_, tmp_config.prefixlen_);
+
+ RilConfig::global_ril_config_ = tmp_config;
+}
+
// TearDownNetworkInterface disables network interface.
// This call returns true, if operation was successful.
bool TearDownNetworkInterface() {
@@ -187,13 +268,11 @@
break;
}
- auto ril_region_view = vsoc::ril::RilRegionView::GetInstance();
-
responses[index].ifname = (char*)"rmnet0";
responses[index].addresses =
- const_cast<char*>(ril_region_view->address_and_prefix_length());
- responses[index].dnses = (char*)ril_region_view->data()->dns;
- responses[index].gateways = (char*)ril_region_view->data()->gateway;
+ const_cast<char*>(RilConfig::address_and_prefixlength());
+ responses[index].dnses = RilConfig::dns();
+ responses[index].gateways = RilConfig::gateway();
#if VSOC_PLATFORM_SDK_AFTER(N_MR1)
responses[index].pcscf = (char*)"";
responses[index].mtu = 1440;
@@ -304,7 +383,7 @@
}
if (call.connection_type_ != DataCall::kConnTypeIPv4) {
- ALOGE("Non-IPv4 connections are not supported by VSOC RIL.");
+ ALOGE("Non-IPv4 connections are not supported by Cuttlefish RIL.");
gce_ril_env->OnRequestComplete(t, RIL_E_INVALID_ARGUMENTS, NULL, 0);
return;
}
@@ -316,10 +395,8 @@
}
if (gDataCalls.empty()) {
- auto ril_region_view = vsoc::ril::RilRegionView::GetInstance();
- SetUpNetworkInterface(ril_region_view->data()->ipaddr,
- ril_region_view->data()->prefixlen,
- ril_region_view->data()->broadcast);
+ SetUpNetworkInterface(RilConfig::ipaddr(), RilConfig::prefixlen(),
+ RilConfig::broadcast());
}
gDataCalls[gNextDataCallId] = call;
@@ -1378,6 +1455,7 @@
gce_ril_env->OnRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
}
+#if VSOC_PLATFORM_SDK_AFTER(P)
static void request_set_carrier_restrictions4(void* /*data*/,
size_t /*datalen*/,
RIL_Token t) {
@@ -1391,6 +1469,7 @@
// Carrier restrictions are not supported on cuttlefish, as they are specific for locked devices
gce_ril_env->OnRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
}
+#endif
static RIL_RadioState gce_ril_current_state() {
ALOGV("Reporting radio state %d", gRadioPowerState);
@@ -1407,8 +1486,8 @@
}
static const char* gce_ril_get_version(void) {
- ALOGV("Reporting VSOC version " VSOC_RIL_VERSION_STRING);
- return VSOC_RIL_VERSION_STRING;
+ ALOGV("Reporting Cuttlefish version " CUTTLEFISH_RIL_VERSION_STRING);
+ return CUTTLEFISH_RIL_VERSION_STRING;
}
static int s_cell_info_rate_ms = INT_MAX;
@@ -2295,13 +2374,19 @@
return;
}
- // Ignore all non-power requests when RADIO_STATE_OFF (except
- // RIL_REQUEST_GET_SIM_STATUS)
- if (gRadioPowerState == RADIO_STATE_OFF &&
- !(request == RIL_REQUEST_RADIO_POWER ||
- request == RIL_REQUEST_GET_SIM_STATUS)) {
- gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
- return;
+ // Ignore all non-power requests when RADIO_STATE_OFF.
+ if (gRadioPowerState == RADIO_STATE_OFF) {
+ switch (request) {
+ case RIL_REQUEST_GET_SIM_STATUS:
+ case RIL_REQUEST_OPERATOR:
+ case RIL_REQUEST_RADIO_POWER:
+ case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
+ // Process all the above, even though the radio is off
+ break;
+ default:
+ gce_ril_env->OnRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0);
+ return;
+ }
}
ALOGV("Received request %d", request);
@@ -2581,10 +2666,10 @@
}
}
-#define VSOC_RIL_VERSION 6
+#define CUTTLEFISH_RIL_VERSION 6
static const RIL_RadioFunctions ril_callbacks = {
- VSOC_RIL_VERSION, gce_ril_on_request, gce_ril_current_state,
+ CUTTLEFISH_RIL_VERSION, gce_ril_on_request, gce_ril_current_state,
gce_ril_on_supports, gce_ril_on_cancel, gce_ril_get_version};
extern "C" {
@@ -2594,6 +2679,8 @@
time(&gce_ril_start_time);
gce_ril_env = env;
+ RilConfig::InitRilConfig();
+
TearDownNetworkInterface();
init_modem_supported_network_types();
diff --git a/guest/hals/ril/vsoc_ril.h b/guest/hals/ril/cuttlefish_ril.h
similarity index 96%
rename from guest/hals/ril/vsoc_ril.h
rename to guest/hals/ril/cuttlefish_ril.h
index 19909bb..10340d8 100644
--- a/guest/hals/ril/vsoc_ril.h
+++ b/guest/hals/ril/cuttlefish_ril.h
@@ -18,7 +18,7 @@
#define RIL_SHLIB
-#define LOG_TAG "VSoCRil"
+#define LOG_TAG "CuttlefishRil"
#include <log/log.h>
#include <stdint.h>
diff --git a/guest/hals/ril/libril/Android.mk b/guest/hals/ril/libril/Android.mk
new file mode 100644
index 0000000..ad02bf6
--- /dev/null
+++ b/guest/hals/ril/libril/Android.mk
@@ -0,0 +1,66 @@
+# 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.
+
+# We're forced to use Android.mk here because:
+# This depends on headers in hardware/ril/libril
+# hardware/ril/libril is still on Android.mk
+
+ifeq (libril-cuttlefish-fork,$(CUTTLEFISH_LIBRIL_NAME))
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_VENDOR_MODULE := true
+LOCAL_MODULE := libril-cuttlefish-fork
+LOCAL_SRC_FILES:= \
+ ril.cpp \
+ ril_service.cpp \
+ ril_event.cpp \
+ RilSapSocket.cpp \
+ sap_service.cpp \
+
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libutils \
+ libcutils \
+ libhardware_legacy \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ librilutils \
+ android.hardware.radio@1.0 \
+ android.hardware.radio@1.1 \
+ android.hardware.radio.deprecated@1.0 \
+ android.hardware.radio@1.2 \
+ android.hardware.radio@1.3 \
+ android.hardware.radio@1.4 \
+
+LOCAL_STATIC_LIBRARIES := \
+ libprotobuf-c-nano-enable_malloc \
+
+LOCAL_C_INCLUDES += \
+ device/google/cuttlefish_common \
+ hardware/include \
+ external/nanopb-c \
+ hardware/ril/include \
+ hardware/ril/libril
+
+LOCAL_CFLAGS += \
+ -Wextra \
+ -Wno-unused-parameter
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := hardware/ril/include
+
+include $(BUILD_SHARED_LIBRARY)
+endif
diff --git a/guest/hals/rild/Android.mk b/guest/hals/rild/Android.mk
index f3c9757..01c709b 100644
--- a/guest/hals/rild/Android.mk
+++ b/guest/hals/rild/Android.mk
@@ -13,51 +13,35 @@
# limitations under the License.
# only for PLATFORM_VERSION greater or equal to Q
-ifeq ($(PLATFORM_VERSION), $(word 1, $(sort Q $(PLATFORM_VERSION))))
+ifeq (true,$(ENABLE_CUTTLEFISH_RILD))
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
- ifndef ENABLE_VENDOR_RIL_SERVICE
+LOCAL_SRC_FILES:= \
+ rild_cuttlefish.c
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libdl \
+ liblog \
+ libril-cuttlefish-fork
- LOCAL_SRC_FILES:= \
- rild_cuttlefish.c
+LOCAL_C_INCLUDES += \
+ device/google/cuttlefish_common
- LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libdl \
- liblog \
- libvsoc-ril
+# Temporary hack for broken vendor RILs.
+LOCAL_WHOLE_STATIC_LIBRARIES := \
+ librilutils
- LOCAL_C_INCLUDES += \
- device/google/cuttlefish_common
+LOCAL_CFLAGS := -DRIL_SHLIB
+LOCAL_CFLAGS += -Wall -Wextra -Werror
- # Temporary hack for broken vendor RILs.
- LOCAL_WHOLE_STATIC_LIBRARIES := \
- librilutils
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE:= libcuttlefish-rild
+LOCAL_OVERRIDES_PACKAGES := rild
+PACKAGES.$(LOCAL_MODULE).OVERRIDES := rild
+LOCAL_INIT_RC := rild_cuttlefish.rc
- LOCAL_CFLAGS := -DRIL_SHLIB
- LOCAL_CFLAGS += -Wall -Wextra -Werror
-
- ifeq ($(SIM_COUNT), 2)
- LOCAL_CFLAGS += -DANDROID_MULTI_SIM
- LOCAL_CFLAGS += -DANDROID_SIM_COUNT_2
- endif
-
- LOCAL_MODULE_RELATIVE_PATH := hw
- LOCAL_PROPRIETARY_MODULE := true
- LOCAL_MODULE:= libvsoc-rild
- LOCAL_OVERRIDES_PACKAGES := rild
- PACKAGES.$(LOCAL_MODULE).OVERRIDES := rild
- ifeq ($(PRODUCT_COMPATIBLE_PROPERTY),true)
- LOCAL_INIT_RC := rild_cuttlefish.rc
- LOCAL_CFLAGS += -DPRODUCT_COMPATIBLE_PROPERTY
- else
- LOCAL_INIT_RC := rild_cuttlefish.legacy.rc
- endif
-
- include $(BUILD_EXECUTABLE)
-
- endif
-
+include $(BUILD_EXECUTABLE)
endif
diff --git a/guest/hals/rild/rild_cuttlefish.c b/guest/hals/rild/rild_cuttlefish.c
index 064a4d1..c2efe44 100644
--- a/guest/hals/rild/rild_cuttlefish.c
+++ b/guest/hals/rild/rild_cuttlefish.c
@@ -36,13 +36,8 @@
#include <sys/types.h>
#include <guest/hals/ril/libril/ril_ex.h>
-#if defined(PRODUCT_COMPATIBLE_PROPERTY)
#define LIB_PATH_PROPERTY "vendor.rild.libpath"
#define LIB_ARGS_PROPERTY "vendor.rild.libargs"
-#else
-#define LIB_PATH_PROPERTY "rild.libpath"
-#define LIB_ARGS_PROPERTY "rild.libargs"
-#endif
#define MAX_LIB_ARGS 16
static void usage(const char *argv0) {
diff --git a/guest/hals/rild/rild_cuttlefish.legacy.rc b/guest/hals/rild/rild_cuttlefish.legacy.rc
index 43898ce..e9c2d07 100644
--- a/guest/hals/rild/rild_cuttlefish.legacy.rc
+++ b/guest/hals/rild/rild_cuttlefish.legacy.rc
@@ -1,4 +1,4 @@
-service ril-daemon /vendor/bin/hw/libvsoc-rild
+service ril-daemon /vendor/bin/hw/libcuttlefish-rild
class main
user radio
group radio cache inet misc audio log readproc wakelock
diff --git a/guest/hals/rild/rild_cuttlefish.rc b/guest/hals/rild/rild_cuttlefish.rc
index e7d8d9a..9990a7a 100644
--- a/guest/hals/rild/rild_cuttlefish.rc
+++ b/guest/hals/rild/rild_cuttlefish.rc
@@ -1,4 +1,4 @@
-service vendor.ril-daemon /vendor/bin/hw/libvsoc-rild
+service vendor.ril-daemon /vendor/bin/hw/libcuttlefish-rild
class main
user radio
group radio cache inet misc audio log readproc wakelock
diff --git a/guest/hals/sensors/Android.mk b/guest/hals/sensors/Android.mk
index 8dd8a52..e9a1a1a 100644
--- a/guest/hals/sensors/Android.mk
+++ b/guest/hals/sensors/Android.mk
@@ -44,6 +44,7 @@
LOCAL_CFLAGS := -DLOG_TAG=\"VSoC-Sensors\" \
$(VSOC_VERSION_CFLAGS) \
+ -std=c++17 \
-Werror -Wall -Wno-missing-field-initializers -Wno-unused-parameter
LOCAL_C_INCLUDES := \
@@ -56,7 +57,7 @@
libcuttlefish_remoter_framework \
$(VSOC_STLPORT_STATIC_LIBS)
-LOCAL_MODULE := sensors.vsoc
+LOCAL_MODULE := sensors.cutf
LOCAL_VENDOR_MODULE := true
include $(BUILD_SHARED_LIBRARY)
diff --git a/guest/hals/sensors/vsoc_sensors.cpp b/guest/hals/sensors/vsoc_sensors.cpp
index 86809a0..336cd65 100644
--- a/guest/hals/sensors/vsoc_sensors.cpp
+++ b/guest/hals/sensors/vsoc_sensors.cpp
@@ -40,17 +40,6 @@
namespace cvd {
-namespace {
-template <typename F>
-struct HWDeviceThunker : ThunkerBase<hw_device_t, GceSensors, F> {};
-template <typename F>
-struct SensorsThunker : ThunkerBase<sensors_poll_device_t, GceSensors, F> {};
-template <typename F>
-struct SensorsThunker1 : ThunkerBase<sensors_poll_device_1, GceSensors, F> {};
-template <typename F>
-struct SensorsThreadThunker : ThunkerBase<void, GceSensors, F> {};
-}
-
int GceSensors::total_sensor_count_ = -1;
SensorInfo* GceSensors::sensor_infos_ = NULL;
const int GceSensors::kInjectedEventWaitPeriods = 3;
@@ -111,28 +100,28 @@
rval->common.tag = HARDWARE_DEVICE_TAG;
rval->common.version = VSOC_SENSOR_DEVICE_VERSION;
rval->common.module = (struct hw_module_t*)module;
- rval->common.close = HWDeviceThunker<int()>::call<&GceSensors::Close>;
- rval->poll =
- SensorsThunker<int(sensors_event_t*, int)>::call<&GceSensors::Poll>;
- rval->activate = SensorsThunker<int(int, int)>::call<&GceSensors::Activate>;
- rval->setDelay =
- SensorsThunker<int(int, int64_t)>::call<&GceSensors::SetDelay>;
+ rval->common.close = cvd::thunk<hw_device_t, &GceSensors::Close>;
+
+ rval->poll = cvd::thunk<sensors_poll_device_t, &GceSensors::Poll>;
+ rval->activate = cvd::thunk<sensors_poll_device_t, &GceSensors::Activate>;
+ rval->setDelay = cvd::thunk<sensors_poll_device_t, &GceSensors::SetDelay>;
#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_0)
- rval->batch = SensorsThunker1<int(int, int, int64_t,
- int64_t)>::call<&GceSensors::Batch>;
+
+ rval->batch = cvd::thunk<sensors_poll_device_1, &GceSensors::Batch>;
#endif
#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_1)
- rval->flush = SensorsThunker1<int(int)>::call<&GceSensors::Flush>;
+ rval->flush = cvd::thunk<sensors_poll_device_1, &GceSensors::Flush>;
#endif
#if VSOC_SENSORS_DEVICE_API_VERSION_ATLEAST(1_4)
- rval->inject_sensor_data = SensorsThunker1<int(
- const sensors_event_t*)>::call<&GceSensors::InjectSensorData>;
+ rval->inject_sensor_data =
+ cvd::thunk<sensors_poll_device_1, &GceSensors::InjectSensorData>;
#endif
// Spawn a thread to listen for incoming data from the remoter.
int err = pthread_create(
&rval->receiver_thread_, NULL,
- SensorsThreadThunker<void*()>::call<&GceSensors::Receiver>, rval);
+ cvd::thunk<void, &GceSensors::Receiver>,
+ rval);
if (err) {
ALOGE("GceSensors::%s: Unable to start receiver thread (%s)",
__FUNCTION__, strerror(err));
diff --git a/guest/libs/platform_support/api_level_fixes.h b/guest/libs/platform_support/api_level_fixes.h
index 66fe137..c49a8c2 100644
--- a/guest/libs/platform_support/api_level_fixes.h
+++ b/guest/libs/platform_support/api_level_fixes.h
@@ -41,6 +41,10 @@
#include <time.h>
#ifndef VSOC_PLATFORM_SDK_VERSION
+#include "platform_version.h"
+#endif
+
+#ifndef VSOC_PLATFORM_SDK_VERSION
#error VSOC_PLATFORM_SDK_VERSION is not set. Check your Android.mk
#endif
diff --git a/guest/vsoc/lib/Android.mk b/guest/vsoc/lib/Android.mk
index ad27f69..73240f4 100644
--- a/guest/vsoc/lib/Android.mk
+++ b/guest/vsoc/lib/Android.mk
@@ -61,3 +61,27 @@
LOCAL_MULTILIB := first
LOCAL_VENDOR_MODULE := true
include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vsoc_managed_region_e2e_test
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := managed_region_e2e_test.cpp
+
+LOCAL_C_INCLUDES := \
+ device/google/cuttlefish_common \
+ device/google/cuttlefish_kernel
+
+LOCAL_CFLAGS += -DGTEST_OS_LINUX_ANDROID -DGTEST_HAS_STD_STRING -Werror -Wall
+
+LOCAL_STATIC_LIBRARIES := \
+ libgtest
+
+LOCAL_SHARED_LIBRARIES := \
+ vsoc_lib \
+ cuttlefish_auto_resources \
+ libcuttlefish_fs \
+ libbase
+
+LOCAL_MULTILIB := first
+LOCAL_VENDOR_MODULE := true
+include $(BUILD_EXECUTABLE)
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/guest_region_e2e_test.cpp b/guest/vsoc/lib/guest_region_e2e_test.cpp
index 8e3181e..5fd644a 100644
--- a/guest/vsoc/lib/guest_region_e2e_test.cpp
+++ b/guest/vsoc/lib/guest_region_e2e_test.cpp
@@ -18,29 +18,12 @@
* 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"
-// TODO(b/64462568) Move the manager tests to a separate target
-#include "guest/vsoc/lib/manager_region_view.h"
#include <android-base/logging.h>
#include <gtest/gtest.h>
-#define DEATH_TEST_MESSAGE "abort converted to exit of 2 during death test"
-
-using vsoc::layout::e2e_test::E2EManagedTestRegionLayout;
-using vsoc::layout::e2e_test::E2EManagerTestRegionLayout;
-
-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);
- });
-}
-
template <typename View>
void DeathTestView() {
disable_tombstones();
@@ -151,92 +134,14 @@
".*" DEATH_TEST_MESSAGE ".*");
}
-// 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);
int rval = RUN_ALL_TESTS();
@@ -244,6 +149,8 @@
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/host/commands/ivserver/qemu_client.cc b/host/commands/ivserver/qemu_client.cc
index 3a61d78..660c915 100644
--- a/host/commands/ivserver/qemu_client.cc
+++ b/host/commands/ivserver/qemu_client.cc
@@ -70,7 +70,7 @@
// 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()) {
+ 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();
@@ -84,7 +84,7 @@
// 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()) {
+ 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();
diff --git a/host/commands/kernel_log_monitor/kernel_log_server.cc b/host/commands/kernel_log_monitor/kernel_log_server.cc
index ddb9b25..0ecf5fe 100644
--- a/host/commands/kernel_log_monitor/kernel_log_server.cc
+++ b/host/commands/kernel_log_monitor/kernel_log_server.cc
@@ -26,6 +26,11 @@
using cvd::SharedFD;
namespace {
+static const std::map<std::string, std::string> kInformationalPatterns = {
+ {"] Linux version ", "GUEST_KERNEL_VERSION: "},
+ {"GUEST_BUILD_FINGERPRINT: ", "GUEST_BUILD_FINGERPRINT: "},
+};
+
static const std::map<std::string, monitor::BootEvent> kStageToEventMap = {
{"VIRTUAL_DEVICE_BOOT_STARTED", monitor::BootEvent::BootStarted},
{"VIRTUAL_DEVICE_BOOT_COMPLETED", monitor::BootEvent::BootCompleted},
@@ -121,6 +126,14 @@
// Detect VIRTUAL_DEVICE_BOOT_*
for (ssize_t i=0; i<ret; i++) {
if ('\n' == buf[i]) {
+ for (auto& info_kv : kInformationalPatterns) {
+ auto& match = info_kv.first;
+ auto& prefix = info_kv.second;
+ auto pos = line_.find(match);
+ if (std::string::npos != pos) {
+ LOG(INFO) << prefix << line_.substr(pos + match.size());
+ }
+ }
for (auto& stage_kv : kStageToEventMap) {
auto& stage = stage_kv.first;
auto event = stage_kv.second;
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index b34bc93..49672b1 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -18,10 +18,13 @@
srcs: [
"main.cc",
"screen_region_handler.cc",
- "ril_region_handler.cc",
+ "ril_config.cc",
"vsoc_shared_memory.cc",
- "wifi_region_handler.cc",
"boot_image_unpacker.cc",
+ "process_monitor.cc",
+ "launch.cc",
+ "data_image.cc",
+ "flags.cc",
],
header_libs: [
"cuttlefish_glog",
@@ -32,8 +35,6 @@
"libcuttlefish_strings",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
- "libicuuc",
- "libandroidicu",
"libbase",
"libnl"
],
@@ -45,5 +46,5 @@
"libxml2",
"libjsoncpp",
],
- defaults: ["cuttlefish_host_only"],
+ defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
}
diff --git a/host/commands/launch/boot_image_unpacker.cc b/host/commands/launch/boot_image_unpacker.cc
index 5558029..e830646 100644
--- a/host/commands/launch/boot_image_unpacker.cc
+++ b/host/commands/launch/boot_image_unpacker.cc
@@ -100,4 +100,26 @@
path);
}
+bool BootImageUnpacker::Unpack(const std::string& ramdisk_image_path,
+ const std::string& kernel_image_path) {
+ if (HasRamdiskImage()) {
+ if (!ExtractRamdiskImage(ramdisk_image_path)) {
+ LOG(ERROR) << "Error extracting ramdisk from boot image";
+ return false;
+ }
+ }
+ if (!kernel_image_path.empty()) {
+ if (HasKernelImage()) {
+ if (!ExtractKernelImage(kernel_image_path)) {
+ LOG(ERROR) << "Error extracting kernel from boot image";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "No kernel found on boot image";
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace cvd
diff --git a/host/commands/launch/boot_image_unpacker.h b/host/commands/launch/boot_image_unpacker.h
index 69fc7bd..05fe671 100644
--- a/host/commands/launch/boot_image_unpacker.h
+++ b/host/commands/launch/boot_image_unpacker.h
@@ -45,6 +45,9 @@
// as root.
bool ExtractRamdiskImage(const std::string& path) const;
+ bool Unpack(const std::string& ramdisk_image_path,
+ const std::string& kernel_image_path);
+
private:
BootImageUnpacker(SharedFD boot_image, const std::string& cmdline,
uint32_t kernel_image_size, uint32_t kernel_image_offset,
diff --git a/host/commands/launch/data_image.cc b/host/commands/launch/data_image.cc
new file mode 100644
index 0000000..9bdc421
--- /dev/null
+++ b/host/commands/launch/data_image.cc
@@ -0,0 +1,136 @@
+#include "host/commands/launch/data_image.h"
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/files.h"
+#include "common/libs/utils/subprocess.h"
+
+namespace {
+const std::string kDataPolicyUseExisting = "use_existing";
+const std::string kDataPolicyCreateIfMissing = "create_if_missing";
+const std::string kDataPolicyAlwaysCreate = "always_create";
+const std::string kDataPolicyResizeUpTo= "resize_up_to";
+
+const int FSCK_ERROR_CORRECTED = 1;
+const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
+
+bool ForceFsckImage(const char* data_image) {
+ int fsck_status = cvd::execute({"/sbin/e2fsck", "-y", "-f", data_image});
+ if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
+ LOG(ERROR) << "`e2fsck -y -f " << data_image << "` failed with code "
+ << fsck_status;
+ return false;
+ }
+ return true;
+}
+
+bool ResizeImage(const char* data_image, int data_image_mb) {
+ auto file_mb = cvd::FileSize(data_image) >> 20;
+ if (file_mb > data_image_mb) {
+ LOG(ERROR) << data_image << " is already " << file_mb << " MB, will not "
+ << "resize down.";
+ return false;
+ } else if (file_mb == data_image_mb) {
+ LOG(INFO) << data_image << " is already the right size";
+ return true;
+ } else {
+ off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
+ int truncate_status =
+ cvd::SharedFD::Open(data_image, O_RDWR)->Truncate(raw_target);
+ if (truncate_status != 0) {
+ LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
+ << data_image << "` failed with code " << truncate_status;
+ return false;
+ }
+ bool fsck_success = ForceFsckImage(data_image);
+ if (!fsck_success) {
+ return false;
+ }
+ int resize_status = cvd::execute({"/sbin/resize2fs", data_image});
+ if (resize_status != 0) {
+ LOG(ERROR) << "`resize2fs " << data_image << "` failed with code "
+ << resize_status;
+ return false;
+ }
+ fsck_success = ForceFsckImage(data_image);
+ if (!fsck_success) {
+ return false;
+ }
+ }
+ return true;
+}
+} // namespace
+
+void CreateBlankImage(
+ const std::string& image, int image_mb, const std::string& image_fmt) {
+ LOG(INFO) << "Creating " << image;
+ std::string of = "of=";
+ of += image;
+ std::string count = "count=";
+ count += std::to_string(image_mb);
+ cvd::execute({"/bin/dd", "if=/dev/zero", of, "bs=1M", count});
+ if (image_fmt != "none") {
+ cvd::execute({"/sbin/mkfs", "-t", image_fmt, image}, {"PATH=/sbin"});
+ }
+}
+
+bool ApplyDataImagePolicy(const vsoc::CuttlefishConfig& config) {
+ std::string data_image = config.data_image_path();
+ bool data_exists = cvd::FileHasContent(data_image.c_str());
+ bool remove{};
+ bool create{};
+ bool resize{};
+
+ if (config.data_policy() == kDataPolicyUseExisting) {
+ if (!data_exists) {
+ LOG(ERROR) << "Specified data image file does not exists: " << data_image;
+ return false;
+ }
+ if (config.blank_data_image_mb() > 0) {
+ LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
+ << kDataPolicyUseExisting;
+ return false;
+ }
+ create = false;
+ remove = false;
+ resize = false;
+ } else if (config.data_policy() == kDataPolicyAlwaysCreate) {
+ remove = data_exists;
+ create = true;
+ resize = false;
+ } else if (config.data_policy() == kDataPolicyCreateIfMissing) {
+ create = !data_exists;
+ remove = false;
+ resize = false;
+ } else if (config.data_policy() == kDataPolicyResizeUpTo) {
+ create = false;
+ remove = false;
+ resize = true;
+ } else {
+ LOG(ERROR) << "Invalid data_policy: " << config.data_policy();
+ return false;
+ }
+
+ if (remove) {
+ cvd::RemoveFile(data_image.c_str());
+ }
+
+ if (create) {
+ if (config.blank_data_image_mb() <= 0) {
+ LOG(ERROR) << "-blank_data_image_mb is required to create data image";
+ return false;
+ }
+ CreateBlankImage(data_image.c_str(), config.blank_data_image_mb(),
+ config.blank_data_image_fmt());
+ } else if (resize) {
+ if (!data_exists) {
+ LOG(ERROR) << data_image << " does not exist, but resizing was requested";
+ return false;
+ }
+ return ResizeImage(data_image.c_str(), config.blank_data_image_mb());
+ } else {
+ LOG(INFO) << data_image << " exists. Not creating it.";
+ }
+
+ return true;
+}
diff --git a/host/commands/launch/data_image.h b/host/commands/launch/data_image.h
new file mode 100644
index 0000000..1cf9ca2
--- /dev/null
+++ b/host/commands/launch/data_image.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <string>
+
+#include "host/libs/config/cuttlefish_config.h"
+
+bool ApplyDataImagePolicy(const vsoc::CuttlefishConfig& config);
+void CreateBlankImage(
+ const std::string& image, int image_mb, const std::string& image_fmt);
diff --git a/host/commands/launch/flags.cc b/host/commands/launch/flags.cc
new file mode 100644
index 0000000..6cadcf0
--- /dev/null
+++ b/host/commands/launch/flags.cc
@@ -0,0 +1,662 @@
+#include "host/commands/launch/flags.h"
+
+#include <iostream>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/strings/str_split.h"
+#include "common/libs/utils/environment.h"
+#include "common/libs/utils/files.h"
+#include "common/vsoc/lib/vsoc_memory.h"
+#include "host/commands/launch/boot_image_unpacker.h"
+#include "host/commands/launch/data_image.h"
+#include "host/commands/launch/launch.h"
+#include "host/commands/launch/launcher_defs.h"
+#include "host/commands/launch/ril_config.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+#include "host/libs/vm_manager/vm_manager.h"
+
+using vsoc::GetPerInstanceDefault;
+using cvd::LauncherExitCodes;
+
+DEFINE_string(
+ system_image, "",
+ "Path to the system image, if empty it is assumed to be a file named "
+ "system.img in the directory specified by -system_image_dir");
+DEFINE_string(cache_image, "", "Location of the cache partition image.");
+DEFINE_string(metadata_image, "", "Location of the metadata partition image "
+ "to be generated.");
+DEFINE_int32(blank_metadata_image_mb, 16,
+ "The size of the blank metadata image to generate, MB.");
+DEFINE_int32(cpus, 2, "Virtual CPU count.");
+DEFINE_string(data_image, "", "Location of the data partition image.");
+DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
+ " Either 'use_existing', 'create_if_missing', 'resize_up_to', or "
+ "'always_create'.");
+DEFINE_int32(blank_data_image_mb, 0,
+ "The size of the blank data image to generate, MB.");
+DEFINE_string(blank_data_image_fmt, "ext4",
+ "The fs format for the blank data image. Used with mkfs.");
+DEFINE_string(qemu_gdb, "",
+ "Debug flag to pass to qemu. e.g. -qemu_gdb=tcp::1234");
+
+DEFINE_int32(x_res, 720, "Width of the screen in pixels");
+DEFINE_int32(y_res, 1280, "Height of the screen in pixels");
+DEFINE_int32(dpi, 160, "Pixels per inch for the screen");
+DEFINE_int32(refresh_rate_hz, 60, "Screen refresh rate in Hertz");
+DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
+DEFINE_string(kernel_path, "",
+ "Path to the kernel. Overrides the one from the boot image");
+DEFINE_bool(decompress_kernel, false,
+ "Whether to decompress the kernel image. Required for crosvm.");
+DEFINE_string(kernel_decompresser_executable,
+ vsoc::DefaultHostArtifactsPath("bin/extract-vmlinux"),
+ "Path to the extract-vmlinux executable.");
+DEFINE_string(extra_kernel_cmdline, "",
+ "Additional flags to put on the kernel command line");
+DEFINE_int32(loop_max_part, 7, "Maximum number of loop partitions");
+DEFINE_string(console, "ttyS0", "Console device for the guest kernel.");
+DEFINE_string(androidboot_console, "ttyS1",
+ "Console device for the Android framework");
+DEFINE_string(
+ hardware_name, "",
+ "The codename of the device's hardware, one of {cutf_ivsh, cutf_cvm}");
+DEFINE_string(guest_security, "selinux",
+ "The security module to use in the guest");
+DEFINE_bool(guest_enforce_security, false,
+ "Whether to run in enforcing mode (non permissive). Ignored if "
+ "-guest_security is empty.");
+DEFINE_bool(guest_audit_security, true,
+ "Whether to log security audits.");
+DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
+DEFINE_int32(memory_mb, 2048,
+ "Total amount of memory available for guest, MB.");
+std::string g_default_mempath{vsoc::GetDefaultMempath()};
+DEFINE_string(mempath, g_default_mempath.c_str(),
+ "Target location for the shmem file.");
+DEFINE_string(mobile_interface, "", // default handled on ParseCommandLine
+ "Network interface to use for mobile networking");
+DEFINE_string(mobile_tap_name, "", // default handled on ParseCommandLine
+ "The name of the tap interface to use for mobile");
+std::string g_default_serial_number{GetPerInstanceDefault("CUTTLEFISHCVD")};
+DEFINE_string(serial_number, g_default_serial_number.c_str(),
+ "Serial number to use for the device");
+DEFINE_string(instance_dir, "", // default handled on ParseCommandLine
+ "A directory to put all instance specific files");
+DEFINE_string(
+ vm_manager, vm_manager::QemuManager::name(),
+ "What virtual machine manager to use, one of {qemu_cli, crosvm}");
+DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
+ "Location of the system partition images.");
+DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
+DEFINE_string(product_image, "", "Location of the product partition image.");
+
+DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
+ " host kernel. This is only used during transition of our clients."
+ " Will be deprecated soon.");
+DEFINE_bool(start_vnc_server, true, "Whether to start the vnc server process.");
+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.");
+DEFINE_string(kernel_log_monitor_binary,
+ vsoc::DefaultHostArtifactsPath("bin/kernel_log_monitor"),
+ "Location of the log monitor binary.");
+DEFINE_string(ivserver_binary,
+ vsoc::DefaultHostArtifactsPath("bin/ivserver"),
+ "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.");
+DEFINE_string(socket_vsock_proxy_binary,
+ vsoc::DefaultHostArtifactsPath("bin/socket_vsock_proxy"),
+ "Location of the socket_vsock_proxy binary.");
+DEFINE_string(adb_mode, "",
+ "Mode for ADB connection. Can be 'usb' for USB forwarding, "
+ "'tunnel' for a TCP connection tunneled through VSoC, "
+ "'vsock_tunnel' for a TCP connection tunneled through vsock, "
+ "'native_vsock' for a direct connection to the guest ADB over "
+ "vsock, 'vsock_half_tunnel' for a TCP connection forwarded to "
+ "the guest ADB server, or a comma separated list of types as in "
+ "'usb,tunnel'");
+DEFINE_bool(run_adb_connector, true,
+ "Maintain adb connection by sending 'adb connect' commands to the "
+ "server. Only relevant with -adb_mode=tunnel or vsock_tunnel");
+DEFINE_string(adb_connector_binary,
+ vsoc::DefaultHostArtifactsPath("bin/adb_connector"),
+ "Location of the adb_connector binary. Only relevant if "
+ "-run_adb_connector is true");
+DEFINE_int32(vhci_port, GetPerInstanceDefault(0), "VHCI port to use for usb");
+DEFINE_string(guest_mac_address,
+ GetPerInstanceDefault("00:43:56:44:80:"), // 00:43:56:44:80:0x
+ "MAC address of the wifi interface to be created on the guest.");
+DEFINE_string(host_mac_address,
+ "42:00:00:00:00:00",
+ "MAC address of the wifi interface running on the host.");
+DEFINE_string(wifi_tap_name, "", // default handled on ParseCommandLine
+ "The name of the tap interface to use for wifi");
+DEFINE_int32(vsock_guest_cid,
+ vsoc::GetDefaultPerInstanceVsockCid(),
+ "Guest identifier for vsock. Disabled if under 3.");
+
+// TODO(b/72969289) This should be generated
+DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
+DEFINE_string(gsi_fstab,
+ vsoc::DefaultHostArtifactsPath("config/gsi.fstab"),
+ "Path to the GSI fstab file");
+
+DEFINE_string(uuid, vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix),
+ "UUID to use for the device. Random if not specified");
+DEFINE_bool(daemon, false,
+ "Run cuttlefish in background, the launcher exits on boot "
+ "completed/failed");
+
+DEFINE_string(device_title, "", "Human readable name for the instance, "
+ "used by the vnc_server for its server title");
+DEFINE_string(setupwizard_mode, "DISABLED",
+ "One of DISABLED,OPTIONAL,REQUIRED");
+
+DEFINE_string(qemu_binary,
+ "/usr/bin/qemu-system-x86_64",
+ "The qemu binary to use");
+DEFINE_string(crosvm_binary,
+ vsoc::DefaultHostArtifactsPath("bin/crosvm"),
+ "The Crosvm binary to use");
+DEFINE_bool(restart_subprocesses, true, "Restart any crashed host process");
+DEFINE_bool(run_e2e_test, true, "Run e2e test after device launches");
+DEFINE_string(e2e_test_binary,
+ vsoc::DefaultHostArtifactsPath("bin/host_region_e2e_test"),
+ "Location of the region end to end test binary");
+DEFINE_string(logcat_receiver_binary,
+ vsoc::DefaultHostArtifactsPath("bin/logcat_receiver"),
+ "Binary for the logcat server");
+DEFINE_string(logcat_mode, "", "How to send android's log messages from "
+ "guest to host. One of [serial, vsock]");
+DEFINE_int32(logcat_vsock_port, vsoc::GetPerInstanceDefault(5620),
+ "The port for logcat over vsock");
+DEFINE_int32(frames_vsock_port, vsoc::GetPerInstanceDefault(5580),
+ "The vsock port to receive frames from the guest on");
+namespace {
+
+template<typename S, typename T>
+static std::string concat(const S& s, const T& t) {
+ std::ostringstream os;
+ os << s << t;
+ return os.str();
+}
+
+bool ResolveInstanceFiles() {
+ if (FLAGS_system_image_dir.empty()) {
+ LOG(ERROR) << "--system_image_dir must be specified.";
+ return false;
+ }
+
+ // If user did not specify location of either of these files, expect them to
+ // be placed in --system_image_dir location.
+ std::string default_system_image = FLAGS_system_image_dir + "/system.img";
+ SetCommandLineOptionWithMode("system_image", default_system_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_boot_image = FLAGS_system_image_dir + "/boot.img";
+ SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_cache_image = FLAGS_system_image_dir + "/cache.img";
+ SetCommandLineOptionWithMode("cache_image", default_cache_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_data_image = FLAGS_system_image_dir + "/userdata.img";
+ SetCommandLineOptionWithMode("data_image", default_data_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_vendor_image = FLAGS_system_image_dir + "/vendor.img";
+ SetCommandLineOptionWithMode("vendor_image", default_vendor_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_metadata_image = FLAGS_system_image_dir + "/metadata.img";
+ SetCommandLineOptionWithMode("metadata_image", default_metadata_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ std::string default_product_image = FLAGS_system_image_dir + "/product.img";
+ SetCommandLineOptionWithMode("product_image", default_product_image.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+
+ return true;
+}
+
+std::string GetCuttlefishEnvPath() {
+ return cvd::StringFromEnv("HOME", ".") + "/.cuttlefish.sh";
+}
+
+// Initializes the config object and saves it to file. It doesn't return it, all
+// further uses of the config should happen through the singleton
+bool InitializeCuttlefishConfiguration(
+ const cvd::BootImageUnpacker& boot_image_unpacker) {
+ 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)) {
+ LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
+ return false;
+ }
+ tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
+
+ tmp_config_obj.set_serial_number(FLAGS_serial_number);
+
+ tmp_config_obj.set_cpus(FLAGS_cpus);
+ tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
+
+ tmp_config_obj.set_dpi(FLAGS_dpi);
+ tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
+ tmp_config_obj.set_x_res(FLAGS_x_res);
+ tmp_config_obj.set_y_res(FLAGS_y_res);
+ tmp_config_obj.set_num_screen_buffers(FLAGS_num_screen_buffers);
+ tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
+ tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
+ std::vector<std::string> adb = cvd::StrSplit(FLAGS_adb_mode, ',');
+ tmp_config_obj.set_adb_mode(std::set<std::string>(adb.begin(), adb.end()));
+ tmp_config_obj.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
+
+ tmp_config_obj.set_device_title(FLAGS_device_title);
+ if (FLAGS_kernel_path.size()) {
+ tmp_config_obj.set_kernel_image_path(FLAGS_kernel_path);
+ tmp_config_obj.set_use_unpacked_kernel(false);
+ } else {
+ tmp_config_obj.set_kernel_image_path(
+ tmp_config_obj.PerInstancePath("kernel"));
+ tmp_config_obj.set_use_unpacked_kernel(true);
+ }
+ tmp_config_obj.set_decompress_kernel(FLAGS_decompress_kernel);
+ if (FLAGS_decompress_kernel) {
+ tmp_config_obj.set_decompressed_kernel_image_path(
+ tmp_config_obj.PerInstancePath("vmlinux"));
+ }
+
+ auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
+ bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
+ if (!use_ramdisk) {
+ LOG(INFO) << "No ramdisk present; assuming system-as-root build";
+ ramdisk_path = "";
+ }
+
+ // This needs to be done here because the dtb path depends on the presence of
+ // the ramdisk
+ if (FLAGS_dtb.empty()) {
+ if (use_ramdisk) {
+ FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
+ } else {
+ FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
+ }
+ }
+
+ tmp_config_obj.add_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
+ if (!use_ramdisk) {
+ tmp_config_obj.add_kernel_cmdline("root=/dev/vda");
+ }
+ tmp_config_obj.add_kernel_cmdline("init=/init");
+ tmp_config_obj.add_kernel_cmdline(
+ concat("androidboot.serialno=", FLAGS_serial_number));
+ tmp_config_obj.add_kernel_cmdline("mac80211_hwsim.radios=0");
+ tmp_config_obj.add_kernel_cmdline(concat("androidboot.lcd_density=", FLAGS_dpi));
+ tmp_config_obj.add_kernel_cmdline(
+ concat("androidboot.setupwizard_mode=", FLAGS_setupwizard_mode));
+ tmp_config_obj.add_kernel_cmdline(concat("loop.max_part=", FLAGS_loop_max_part));
+ if (!FLAGS_console.empty()) {
+ tmp_config_obj.add_kernel_cmdline(concat("console=", FLAGS_console));
+ }
+ if (!FLAGS_androidboot_console.empty()) {
+ tmp_config_obj.add_kernel_cmdline(
+ concat("androidboot.console=", FLAGS_androidboot_console));
+ }
+ if (!FLAGS_hardware_name.empty()) {
+ tmp_config_obj.add_kernel_cmdline(
+ concat("androidboot.hardware=", FLAGS_hardware_name));
+ }
+ if (FLAGS_logcat_mode == cvd::kLogcatVsockMode) {
+ tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_logcat_port=",
+ FLAGS_logcat_vsock_port));
+ }
+ tmp_config_obj.set_hardware_name(FLAGS_hardware_name);
+ if (!FLAGS_guest_security.empty()) {
+ tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
+ if (FLAGS_guest_enforce_security) {
+ tmp_config_obj.add_kernel_cmdline("enforcing=1");
+ } else {
+ tmp_config_obj.add_kernel_cmdline("enforcing=0");
+ tmp_config_obj.add_kernel_cmdline("androidboot.selinux=permissive");
+ }
+ if (FLAGS_guest_audit_security) {
+ tmp_config_obj.add_kernel_cmdline("audit=1");
+ } else {
+ tmp_config_obj.add_kernel_cmdline("audit=0");
+ }
+ }
+ if (FLAGS_run_e2e_test) {
+ tmp_config_obj.add_kernel_cmdline("androidboot.vsoc_e2e_test=1");
+ }
+ if (FLAGS_extra_kernel_cmdline.size()) {
+ tmp_config_obj.add_kernel_cmdline(FLAGS_extra_kernel_cmdline);
+ }
+
+ tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
+ tmp_config_obj.set_system_image_path(FLAGS_system_image);
+ tmp_config_obj.set_cache_image_path(FLAGS_cache_image);
+ tmp_config_obj.set_data_image_path(FLAGS_data_image);
+ tmp_config_obj.set_vendor_image_path(FLAGS_vendor_image);
+ tmp_config_obj.set_metadata_image_path(FLAGS_metadata_image);
+ tmp_config_obj.set_product_image_path(FLAGS_product_image);
+ tmp_config_obj.set_dtb_path(FLAGS_dtb);
+ tmp_config_obj.set_gsi_fstab_path(FLAGS_gsi_fstab);
+
+ tmp_config_obj.set_mempath(FLAGS_mempath);
+ tmp_config_obj.set_ivshmem_qemu_socket_path(
+ tmp_config_obj.PerInstancePath("ivshmem_socket_qemu"));
+ tmp_config_obj.set_ivshmem_client_socket_path(
+ tmp_config_obj.PerInstancePath("ivshmem_socket_client"));
+ tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
+
+ if (AdbUsbEnabled(tmp_config_obj)) {
+ tmp_config_obj.set_usb_v1_socket_name(tmp_config_obj.PerInstancePath("usb-v1"));
+ tmp_config_obj.set_vhci_port(FLAGS_vhci_port);
+ tmp_config_obj.set_usb_ip_socket_name(tmp_config_obj.PerInstancePath("usb-ip"));
+ }
+
+ tmp_config_obj.set_kernel_log_socket_name(tmp_config_obj.PerInstancePath("kernel-log"));
+ tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
+ tmp_config_obj.set_console_path(tmp_config_obj.PerInstancePath("console"));
+ tmp_config_obj.set_logcat_path(tmp_config_obj.PerInstancePath("logcat"));
+ tmp_config_obj.set_logcat_receiver_binary(FLAGS_logcat_receiver_binary);
+ tmp_config_obj.set_launcher_log_path(tmp_config_obj.PerInstancePath("launcher.log"));
+ tmp_config_obj.set_launcher_monitor_socket_path(
+ tmp_config_obj.PerInstancePath("launcher_monitor.sock"));
+
+ tmp_config_obj.set_mobile_bridge_name(FLAGS_mobile_interface);
+ tmp_config_obj.set_mobile_tap_name(FLAGS_mobile_tap_name);
+ ConfigureRil(&tmp_config_obj);
+
+ tmp_config_obj.set_wifi_tap_name(FLAGS_wifi_tap_name);
+
+ tmp_config_obj.set_wifi_guest_mac_addr(FLAGS_guest_mac_address);
+ tmp_config_obj.set_wifi_host_mac_addr(FLAGS_host_mac_address);
+
+ tmp_config_obj.set_vsock_guest_cid(FLAGS_vsock_guest_cid);
+
+ tmp_config_obj.set_entropy_source("/dev/urandom");
+ tmp_config_obj.set_uuid(FLAGS_uuid);
+
+ tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
+ tmp_config_obj.set_crosvm_binary(FLAGS_crosvm_binary);
+ tmp_config_obj.set_ivserver_binary(FLAGS_ivserver_binary);
+ tmp_config_obj.set_kernel_log_monitor_binary(FLAGS_kernel_log_monitor_binary);
+
+ tmp_config_obj.set_enable_vnc_server(FLAGS_start_vnc_server);
+ 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);
+ tmp_config_obj.set_virtual_usb_manager_binary(
+ FLAGS_virtual_usb_manager_binary);
+ tmp_config_obj.set_socket_forward_proxy_binary(
+ FLAGS_socket_forward_proxy_binary);
+ tmp_config_obj.set_socket_vsock_proxy_binary(FLAGS_socket_vsock_proxy_binary);
+ tmp_config_obj.set_run_as_daemon(FLAGS_daemon);
+ tmp_config_obj.set_run_e2e_test(FLAGS_run_e2e_test);
+ tmp_config_obj.set_e2e_test_binary(FLAGS_e2e_test_binary);
+
+ tmp_config_obj.set_data_policy(FLAGS_data_policy);
+ tmp_config_obj.set_blank_data_image_mb(FLAGS_blank_data_image_mb);
+ tmp_config_obj.set_blank_data_image_fmt(FLAGS_blank_data_image_fmt);
+
+ if(!AdbUsbEnabled(tmp_config_obj)) {
+ tmp_config_obj.disable_usb_adb();
+ }
+
+ tmp_config_obj.set_logcat_mode(FLAGS_logcat_mode);
+ tmp_config_obj.set_logcat_vsock_port(FLAGS_logcat_vsock_port);
+ tmp_config_obj.set_frames_vsock_port(FLAGS_frames_vsock_port);
+ if (!tmp_config_obj.enable_ivserver()) {
+ tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_frames_port=",
+ FLAGS_frames_vsock_port));
+ }
+
+ tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
+
+ auto config_file = GetConfigFilePath(tmp_config_obj);
+ auto config_link = vsoc::GetGlobalConfigFileLink();
+ // Save the config object before starting any host process
+ if (!tmp_config_obj.SaveToFile(config_file)) {
+ LOG(ERROR) << "Unable to save config object";
+ return false;
+ }
+ setenv(vsoc::kCuttlefishConfigEnvVarName, config_file.c_str(), true);
+ if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
+ LOG(ERROR) << "Failed to create symlink to config file at " << config_link
+ << ": " << strerror(errno);
+ return false;
+ }
+
+ return true;
+}
+
+void SetDefaultFlagsForQemu() {
+ auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
+ SetCommandLineOptionWithMode("mobile_interface",
+ default_mobile_interface.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
+ SetCommandLineOptionWithMode("mobile_tap_name",
+ default_mobile_tap_name.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
+ SetCommandLineOptionWithMode("wifi_tap_name",
+ default_wifi_tap_name.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_instance_dir =
+ cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
+ SetCommandLineOptionWithMode("instance_dir",
+ default_instance_dir.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("hardware_name", "cutf_ivsh",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("adb_mode", "tunnel",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("decompress_kernel", "false",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatSerialMode,
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
+
+void SetDefaultFlagsForCrosvm() {
+ auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
+ SetCommandLineOptionWithMode("mobile_interface",
+ default_mobile_interface.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
+ SetCommandLineOptionWithMode("mobile_tap_name",
+ default_mobile_tap_name.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
+ SetCommandLineOptionWithMode("wifi_tap_name",
+ default_wifi_tap_name.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ auto default_instance_dir =
+ cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
+ SetCommandLineOptionWithMode("instance_dir",
+ default_instance_dir.c_str(),
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("hardware_name", "cutf_cvm",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("adb_mode", "vsock_tunnel",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("decompress_kernel", "true",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("run_e2e_test", "false",
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+ SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode,
+ google::FlagSettingMode::SET_FLAGS_DEFAULT);
+}
+
+bool ParseCommandLineFlags(int* argc, char*** argv) {
+ // The config_file is created by the launcher, so the launcher is the only
+ // host process that doesn't use the flag.
+ // Set the default to empty.
+ google::SetCommandLineOptionWithMode("config_file", "",
+ gflags::SET_FLAGS_DEFAULT);
+ google::ParseCommandLineNonHelpFlags(argc, argv, true);
+ bool invalid_manager = false;
+ if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
+ SetDefaultFlagsForQemu();
+ } else if (FLAGS_vm_manager == vm_manager::CrosvmManager::name()) {
+ SetDefaultFlagsForCrosvm();
+ } else {
+ std::cerr << "Unknown Virtual Machine Manager: " << FLAGS_vm_manager
+ << std::endl;
+ invalid_manager = true;
+ }
+ google::HandleCommandLineHelpFlags();
+ if (invalid_manager) {
+ return false;
+ }
+ // Set the env variable to empty (in case the caller passed a value for it).
+ unsetenv(vsoc::kCuttlefishConfigEnvVarName);
+
+ return ResolveInstanceFiles();
+}
+
+bool CleanPriorFiles() {
+ // Everything on the instance directory
+ std::string prior_files = FLAGS_instance_dir + "/*";
+ // The shared memory file
+ prior_files += " " + FLAGS_mempath;
+ // The environment file
+ prior_files += " " + GetCuttlefishEnvPath();
+ // The global link to the config file
+ prior_files += " " + vsoc::GetGlobalConfigFileLink();
+ LOG(INFO) << "Assuming prior files of " << prior_files;
+ std::string fuser_cmd = "fuser " + prior_files + " 2> /dev/null";
+ int rval = std::system(fuser_cmd.c_str());
+ // fuser returns 0 if any of the files are open
+ if (WEXITSTATUS(rval) == 0) {
+ LOG(ERROR) << "Clean aborted: files are in use";
+ return false;
+ }
+ std::string clean_command = "rm -rf " + prior_files;
+ rval = std::system(clean_command.c_str());
+ if (WEXITSTATUS(rval) != 0) {
+ LOG(ERROR) << "Remove of files failed";
+ return false;
+ }
+ return true;
+}
+
+bool DecompressKernel(const std::string& src, const std::string& dst) {
+ cvd::Command decomp_cmd(FLAGS_kernel_decompresser_executable);
+ decomp_cmd.AddParameter(src);
+ auto output_file = cvd::SharedFD::Creat(dst.c_str(), 0666);
+ if (!output_file->IsOpen()) {
+ LOG(ERROR) << "Unable to create decompressed image file: "
+ << output_file->StrError();
+ return false;
+ }
+ decomp_cmd.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut, output_file);
+ auto decomp_proc = decomp_cmd.Start(false);
+ return decomp_proc.Started() && decomp_proc.Wait() == 0;
+}
+} // namespace
+
+vsoc::CuttlefishConfig* InitFilesystemAndCreateConfig(int* argc, char*** argv) {
+ if (!ParseCommandLineFlags(argc, argv)) {
+ LOG(ERROR) << "Failed to parse command arguments";
+ exit(LauncherExitCodes::kArgumentParsingError);
+ }
+
+ // Clean up prior files before saving the config file (doing it after would
+ // delete it)
+ if (!CleanPriorFiles()) {
+ LOG(ERROR) << "Failed to clean prior files";
+ exit(LauncherExitCodes::kPrioFilesCleanupError);
+ }
+ // Create instance directory if it doesn't exist.
+ if (!cvd::DirectoryExists(FLAGS_instance_dir.c_str())) {
+ LOG(INFO) << "Setting up " << FLAGS_instance_dir;
+ if (mkdir(FLAGS_instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
+ LOG(ERROR) << "Failed to create instance directory: "
+ << FLAGS_instance_dir << ". Error: " << errno;
+ exit(LauncherExitCodes::kInstanceDirCreationError);
+ }
+ }
+
+ if (!cvd::FileHasContent(FLAGS_boot_image)) {
+ LOG(ERROR) << "File not found: " << FLAGS_boot_image;
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
+
+ auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
+
+ if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
+ LOG(ERROR) << "Failed to initialize configuration";
+ exit(LauncherExitCodes::kCuttlefishConfigurationInitError);
+ }
+ // Do this early so that the config object is ready for anything that needs it
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (!config) {
+ LOG(ERROR) << "Failed to obtain config singleton";
+ exit(LauncherExitCodes::kCuttlefishConfigurationInitError);
+ }
+
+ if (!boot_img_unpacker->Unpack(config->ramdisk_image_path(),
+ config->use_unpacked_kernel()
+ ? config->kernel_image_path()
+ : "")) {
+ LOG(ERROR) << "Failed to unpack boot image";
+ exit(LauncherExitCodes::kBootImageUnpackError);
+ }
+
+ if (config->decompress_kernel()) {
+ if (!DecompressKernel(config->kernel_image_path(),
+ config->decompressed_kernel_image_path())) {
+ LOG(ERROR) << "Failed to decompress kernel";
+ exit(LauncherExitCodes::kKernelDecompressError);
+ }
+ }
+
+ ValidateAdbModeFlag(*config);
+
+ // Create data if necessary
+ if (!ApplyDataImagePolicy(*config)) {
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
+
+ CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
+
+ // Check that the files exist
+ for (const auto& file :
+ {config->system_image_path(), config->vendor_image_path(),
+ config->cache_image_path(), config->data_image_path(),
+ config->metadata_image_path(), config->product_image_path()}) {
+ if (!cvd::FileHasContent(file.c_str())) {
+ LOG(ERROR) << "File not found: " << file;
+ exit(cvd::kCuttlefishConfigurationInitError);
+ }
+ }
+
+ return config;
+}
+
+std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config) {
+ return config.PerInstancePath("cuttlefish_config.json");
+}
diff --git a/host/commands/launch/flags.h b/host/commands/launch/flags.h
new file mode 100644
index 0000000..9ca6f58
--- /dev/null
+++ b/host/commands/launch/flags.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "host/libs/config/cuttlefish_config.h"
+
+vsoc::CuttlefishConfig* InitFilesystemAndCreateConfig(int* argc, char*** argv);
+std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config);
diff --git a/host/commands/launch/launch.cc b/host/commands/launch/launch.cc
new file mode 100644
index 0000000..766efb0
--- /dev/null
+++ b/host/commands/launch/launch.cc
@@ -0,0 +1,312 @@
+#include "host/commands/launch/launch.h"
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/size_utils.h"
+#include "common/vsoc/shm/screen_layout.h"
+#include "host/commands/launch/launcher_defs.h"
+#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/commands/launch/vsoc_shared_memory.h"
+
+using cvd::LauncherExitCodes;
+using cvd::MonitorEntry;
+
+namespace {
+
+constexpr char kAdbModeTunnel[] = "tunnel";
+constexpr char kAdbModeNativeVsock[] = "native_vsock";
+constexpr char kAdbModeVsockTunnel[] = "vsock_tunnel";
+constexpr char kAdbModeVsockHalfTunnel[] = "vsock_half_tunnel";
+constexpr char kAdbModeUsb[] = "usb";
+
+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() {
+ return std::string{"--host_ports="} + std::to_string(GetHostPort());
+}
+
+std::string GetAdbConnectorTcpArg() {
+ return std::string{"--addresses=127.0.0.1:"} + std::to_string(GetHostPort());
+}
+
+std::string GetAdbConnectorVsockArg(const vsoc::CuttlefishConfig& config) {
+ return std::string{"--addresses=vsock:"}
+ + std::to_string(config.vsock_guest_cid())
+ + std::string{":5555"};
+}
+
+bool AdbModeEnabled(const vsoc::CuttlefishConfig& config, const char* mode) {
+ return config.adb_mode().count(mode) > 0;
+}
+
+bool AdbTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+ return AdbModeEnabled(config, kAdbModeTunnel);
+}
+
+bool AdbVsockTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+ return config.vsock_guest_cid() > 2
+ && AdbModeEnabled(config, kAdbModeVsockTunnel);
+}
+
+bool AdbVsockHalfTunnelEnabled(const vsoc::CuttlefishConfig& config) {
+ return config.vsock_guest_cid() > 2
+ && AdbModeEnabled(config, kAdbModeVsockHalfTunnel);
+}
+
+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()
+ && (tunnel || vsock_tunnel || vsock_half_tunnel);
+}
+
+bool AdbVsockConnectorEnabled(const vsoc::CuttlefishConfig& config) {
+ return config.run_adb_connector()
+ && AdbModeEnabled(config, kAdbModeNativeVsock);
+}
+
+cvd::OnSocketReadyCb GetOnSubprocessExitCallback(
+ const vsoc::CuttlefishConfig& config) {
+ if (config.restart_subprocesses()) {
+ return cvd::ProcessMonitor::RestartOnExitCb;
+ } else {
+ return cvd::ProcessMonitor::DoNotMonitorCb;
+ }
+}
+} // namespace
+
+int GetHostPort() {
+ constexpr int kFirstHostPort = 6520;
+ return vsoc::GetPerInstanceDefault(kFirstHostPort);
+}
+
+bool LogcatReceiverEnabled(const vsoc::CuttlefishConfig& config) {
+ return config.logcat_mode() == cvd::kLogcatVsockMode;
+}
+
+bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config) {
+ return AdbModeEnabled(config, kAdbModeUsb);
+}
+
+void ValidateAdbModeFlag(const vsoc::CuttlefishConfig& config) {
+ if (!AdbUsbEnabled(config) && !AdbTunnelEnabled(config)
+ && !AdbVsockTunnelEnabled(config)) {
+ LOG(INFO) << "ADB not enabled";
+ }
+}
+
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config) {
+ // Resize gralloc 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;
+}
+
+// Build the kernel log monitor command. If boot_event_pipe is not NULL, a
+// subscription to boot events from the kernel log monitor will be created and
+// events will appear on *boot_events_pipe
+cvd::Command GetKernelLogMonitorCommand(const vsoc::CuttlefishConfig& config,
+ cvd::SharedFD* boot_events_pipe) {
+ auto log_name = config.kernel_log_socket_name();
+ auto server = cvd::SharedFD::SocketLocalServer(log_name.c_str(), false,
+ SOCK_STREAM, 0666);
+ cvd::Command kernel_log_monitor(config.kernel_log_monitor_binary());
+ kernel_log_monitor.AddParameter("-log_server_fd=", server);
+ if (boot_events_pipe) {
+ cvd::SharedFD pipe_write_end;
+ if (!cvd::SharedFD::Pipe(boot_events_pipe, &pipe_write_end)) {
+ LOG(ERROR) << "Unable to create boot events pipe: " << strerror(errno);
+ std::exit(LauncherExitCodes::kPipeIOError);
+ }
+ kernel_log_monitor.AddParameter("-subscriber_fd=", pipe_write_end);
+ }
+ return kernel_log_monitor;
+}
+
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor) {
+ if (!LogcatReceiverEnabled(config)) {
+ return;
+ }
+ auto port = config.logcat_vsock_port();
+ auto socket = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+ if (!socket->IsOpen()) {
+ LOG(ERROR) << "Unable to create logcat server socket: "
+ << socket->StrError();
+ std::exit(LauncherExitCodes::kLogcatServerError);
+ }
+ cvd::Command cmd(config.logcat_receiver_binary());
+ cmd.AddParameter("-server_fd=", socket);
+ process_monitor->StartSubprocess(std::move(cmd),
+ GetOnSubprocessExitCallback(config));
+}
+
+void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor) {
+ if (!AdbUsbEnabled(config)) {
+ return;
+ }
+ auto socket_name = config.usb_v1_socket_name();
+ auto usb_v1_server = cvd::SharedFD::SocketLocalServer(
+ socket_name.c_str(), false, SOCK_STREAM, 0666);
+ if (!usb_v1_server->IsOpen()) {
+ LOG(ERROR) << "Unable to create USB v1 server socket: "
+ << usb_v1_server->StrError();
+ std::exit(cvd::LauncherExitCodes::kUsbV1SocketError);
+ }
+ cvd::Command usb_server(config.virtual_usb_manager_binary());
+ usb_server.AddParameter("-usb_v1_fd=", usb_v1_server);
+ process_monitor->StartSubprocess(std::move(usb_server),
+ GetOnSubprocessExitCallback(config));
+}
+
+cvd::SharedFD CreateVncInputServer(const std::string& path) {
+ auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666);
+ if (!server->IsOpen()) {
+ LOG(ERROR) << "Unable to create mouse server: "
+ << server->StrError();
+ return cvd::SharedFD();
+ }
+ return server;
+}
+
+void LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor,
+ std::function<bool(MonitorEntry*)> callback) {
+ if (config.enable_vnc_server()) {
+ // Launch the vnc server, don't wait for it to complete
+ auto port_options = "-port=" + std::to_string(config.vnc_server_port());
+ cvd::Command vnc_server(config.vnc_server_binary());
+ vnc_server.AddParameter(port_options);
+ if (!config.enable_ivserver()) {
+ // When the ivserver is not enabled, the vnc touch_server needs to serve
+ // on unix sockets and send input events to whoever connects to it (namely
+ // crosvm)
+ auto touch_server = CreateVncInputServer(config.touch_socket_path());
+ if (!touch_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-touch_fd=", touch_server);
+
+ auto keyboard_server =
+ CreateVncInputServer(config.keyboard_socket_path());
+ if (!keyboard_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-keyboard_fd=", keyboard_server);
+ // TODO(b/128852363): This should be handled through the wayland mock
+ // instead.
+ // Additionally it receives the frame updates from a virtual socket
+ // instead
+ auto frames_server =
+ cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM);
+ if (!frames_server->IsOpen()) {
+ return;
+ }
+ vnc_server.AddParameter("-frame_server_fd=", frames_server);
+ }
+ process_monitor->StartSubprocess(std::move(vnc_server), callback);
+ }
+}
+
+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) {
+ if (AdbTcpConnectorEnabled(config)) {
+ cvd::Command adb_connector(config.adb_connector_binary());
+ adb_connector.AddParameter(GetAdbConnectorTcpArg());
+ process_monitor->StartSubprocess(std::move(adb_connector),
+ GetOnSubprocessExitCallback(config));
+ }
+ if (AdbVsockConnectorEnabled(config)) {
+ cvd::Command adb_connector(config.adb_connector_binary());
+ adb_connector.AddParameter(GetAdbConnectorVsockArg(config));
+ process_monitor->StartSubprocess(std::move(adb_connector),
+ GetOnSubprocessExitCallback(config));
+ }
+}
+
+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());
+ process_monitor->StartSubprocess(std::move(adb_tunnel),
+ GetOnSubprocessExitCallback(config));
+ }
+}
+
+void LaunchSocketVsockProxyIfEnabled(cvd::ProcessMonitor* process_monitor,
+ const vsoc::CuttlefishConfig& config) {
+ if (AdbVsockTunnelEnabled(config)) {
+ cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
+ adb_tunnel.AddParameter("--vsock_port=6520");
+ adb_tunnel.AddParameter(
+ std::string{"--tcp_port="} + std::to_string(GetHostPort()));
+ adb_tunnel.AddParameter(std::string{"--vsock_guest_cid="} +
+ std::to_string(config.vsock_guest_cid()));
+ process_monitor->StartSubprocess(std::move(adb_tunnel),
+ GetOnSubprocessExitCallback(config));
+ }
+ if (AdbVsockHalfTunnelEnabled(config)) {
+ cvd::Command adb_tunnel(config.socket_vsock_proxy_binary());
+ adb_tunnel.AddParameter("--vsock_port=5555");
+ adb_tunnel.AddParameter(
+ std::string{"--tcp_port="} + std::to_string(GetHostPort()));
+ adb_tunnel.AddParameter(std::string{"--vsock_guest_cid="} +
+ std::to_string(config.vsock_guest_cid()));
+ process_monitor->StartSubprocess(std::move(adb_tunnel),
+ 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/launch/launch.h b/host/commands/launch/launch.h
new file mode 100644
index 0000000..550ce02
--- /dev/null
+++ b/host/commands/launch/launch.h
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <functional>
+
+#include "common/libs/utils/subprocess.h"
+#include "host/commands/launch/process_monitor.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+int GetHostPort();
+bool AdbUsbEnabled(const vsoc::CuttlefishConfig& config);
+void ValidateAdbModeFlag(const vsoc::CuttlefishConfig& config);
+
+cvd::Command GetIvServerCommand(const vsoc::CuttlefishConfig& config);
+cvd::Command GetKernelLogMonitorCommand(const vsoc::CuttlefishConfig& config,
+ cvd::SharedFD* boot_events_pipe);
+void LaunchLogcatReceiverIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor);
+void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config,
+ cvd::ProcessMonitor* process_monitor);
+void 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);
+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);
diff --git a/host/commands/launch/launcher_defs.h b/host/commands/launch/launcher_defs.h
index 2732fdf..9a78a4e 100644
--- a/host/commands/launch/launcher_defs.h
+++ b/host/commands/launch/launcher_defs.h
@@ -17,6 +17,9 @@
namespace cvd {
+constexpr char kLogcatSerialMode[] = "serial";
+constexpr char kLogcatVsockMode[] = "vsock";
+
enum LauncherExitCodes : int {
kSuccess = 0,
kArgumentParsingError = 1,
@@ -34,6 +37,9 @@
kMonitorCreationFailed = 13,
kServerError = 14,
kUsbV1SocketError = 15,
+ kE2eTestFailed = 16,
+ kKernelDecompressError = 17,
+ kLogcatServerError = 18,
};
// Actions supported by the launcher server
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index f9d453b..ff2c7dd 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -37,6 +37,7 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
+#include <host/libs/vm_manager/crosvm_manager.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/fs/shared_select.h"
@@ -48,764 +49,151 @@
#include "common/vsoc/lib/vsoc_memory.h"
#include "common/vsoc/shm/screen_layout.h"
#include "host/commands/launch/boot_image_unpacker.h"
+#include "host/commands/launch/data_image.h"
+#include "host/commands/launch/flags.h"
+#include "host/commands/launch/launch.h"
#include "host/commands/launch/launcher_defs.h"
-#include "host/commands/launch/pre_launch_initializers.h"
+#include "host/commands/launch/process_monitor.h"
#include "host/commands/launch/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/vm_manager.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
using vsoc::GetPerInstanceDefault;
using cvd::LauncherExitCodes;
-DEFINE_string(
- system_image, "",
- "Path to the system image, if empty it is assumed to be a file named "
- "system.img in the directory specified by -system_image_dir");
-DEFINE_string(cache_image, "", "Location of the cache partition image.");
-DEFINE_int32(cpus, 2, "Virtual CPU count.");
-DEFINE_string(data_image, "", "Location of the data partition image.");
-DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
- " Either 'use_existing', 'create_if_missing', 'resize_up_to', or "
- "'always_create'.");
-DEFINE_int32(blank_data_image_mb, 0,
- "The size of the blank data image to generate, MB.");
-DEFINE_string(blank_data_image_fmt, "ext4",
- "The fs format for the blank data image. Used with mkfs.");
-DEFINE_string(qemu_gdb, "",
- "Debug flag to pass to qemu. e.g. -qemu_gdb=tcp::1234");
-
-DEFINE_int32(x_res, 720, "Width of the screen in pixels");
-DEFINE_int32(y_res, 1280, "Height of the screen in pixels");
-DEFINE_int32(dpi, 160, "Pixels per inch for the screen");
-DEFINE_int32(refresh_rate_hz, 60, "Screen refresh rate in Hertz");
-DEFINE_int32(num_screen_buffers, 3, "The number of screen buffers");
-
-DEFINE_bool(disable_app_armor_security, false,
- "Disable AppArmor security in libvirt. For debug only.");
-DEFINE_bool(disable_dac_security, false,
- "Disable DAC security in libvirt. For debug only.");
-DEFINE_string(kernel_path, "",
- "Path to the kernel. Overrides the one from the boot image");
-DEFINE_string(extra_kernel_cmdline, "",
- "Additional flags to put on the kernel command line");
-DEFINE_int32(loop_max_part, 7, "Maximum number of loop partitions");
-DEFINE_string(console, "ttyS0", "Console device for the guest kernel.");
-DEFINE_string(androidboot_console, "ttyS1",
- "Console device for the Android framework");
-DEFINE_string(hardware_name, "vsoc",
- "The codename of the device's hardware");
-DEFINE_string(guest_security, "selinux",
- "The security module to use in the guest");
-DEFINE_bool(guest_enforce_security, true,
- "Whether to run in enforcing mode (non permissive). Ignored if "
- "-guest_security is empty.");
-DEFINE_bool(guest_audit_security, true,
- "Whether to log security audits.");
-DEFINE_string(boot_image, "", "Location of cuttlefish boot image.");
-DEFINE_int32(memory_mb, 2048,
- "Total amount of memory available for guest, MB.");
-std::string g_default_mempath{vsoc::GetDefaultMempath()};
-DEFINE_string(mempath, g_default_mempath.c_str(),
- "Target location for the shmem file.");
-DEFINE_string(mobile_interface, "", // default handled on ParseCommandLine
- "Network interface to use for mobile networking");
-DEFINE_string(mobile_tap_name, "", // default handled on ParseCommandLine
- "The name of the tap interface to use for mobile");
-std::string g_default_serial_number{GetPerInstanceDefault("CUTTLEFISHCVD")};
-DEFINE_string(serial_number, g_default_serial_number.c_str(),
- "Serial number to use for the device");
-DEFINE_string(instance_dir, "", // default handled on ParseCommandLine
- "A directory to put all instance specific files");
-DEFINE_string(
- vm_manager,
- vsoc::HostSupportsQemuCli() ? vm_manager::QemuManager::name()
- : vm_manager::LibvirtManager::name(),
- "What virtual machine manager to use, one of libvirt or qemu_cli");
-DEFINE_string(system_image_dir, vsoc::DefaultGuestImagePath(""),
- "Location of the system partition images.");
-DEFINE_string(vendor_image, "", "Location of the vendor partition image.");
-
-DEFINE_bool(deprecated_boot_completed, false, "Log boot completed message to"
- " host kernel. This is only used during transition of our clients."
- " Will be deprecated soon.");
-DEFINE_bool(start_vnc_server, true, "Whether to start the vnc server process.");
-DEFINE_string(vnc_server_binary,
- vsoc::DefaultHostArtifactsPath("bin/vnc_server"),
- "Location of the vnc server binary.");
-DEFINE_string(virtual_usb_manager_binary,
- vsoc::DefaultHostArtifactsPath("bin/virtual_usb_manager"),
- "Location of the virtual usb manager binary.");
-DEFINE_string(kernel_log_monitor_binary,
- vsoc::DefaultHostArtifactsPath("bin/kernel_log_monitor"),
- "Location of the log monitor binary.");
-DEFINE_string(ivserver_binary,
- vsoc::DefaultHostArtifactsPath("bin/ivserver"),
- "Location of the ivshmem server binary.");
-DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
- "The port on which the vnc server should listen");
-DEFINE_string(socket_forward_proxy_binary,
- vsoc::DefaultHostArtifactsPath("bin/socket_forward_proxy"),
- "Location of the socket_forward_proxy binary.");
-DEFINE_string(adb_mode, "tunnel",
- "Mode for adb connection. Can be 'usb' for usb forwarding, "
- "'tunnel' for tcp connection, or a comma separated list of types "
- "as in 'usb,tunnel'");
-DEFINE_bool(run_adb_connector, true,
- "Maintain adb connection by sending 'adb connect' commands to the "
- "server. Only relevant with -adb_mode=tunnel");
-DEFINE_string(adb_connector_binary,
- vsoc::DefaultHostArtifactsPath("bin/adb_connector"),
- "Location of the adb_connector binary. Only relevant if "
- "-run_adb_connector is true");
-DEFINE_int32(vhci_port, GetPerInstanceDefault(0), "VHCI port to use for usb");
-DEFINE_string(guest_mac_address,
- GetPerInstanceDefault("00:43:56:44:80:"), // 00:43:56:44:80:0x
- "MAC address of the wifi interface to be created on the guest.");
-DEFINE_string(host_mac_address,
- "42:00:00:00:00:00",
- "MAC address of the wifi interface running on the host.");
-DEFINE_string(wifi_interface, "", // default handled on ParseCommandLine
- "Network interface to use for wifi");
-DEFINE_string(wifi_tap_name, "", // default handled on ParseCommandLine
- "The name of the tap interface to use for wifi");
-// TODO(b/72969289) This should be generated
-DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
-
-DEFINE_string(uuid, vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix),
- "UUID to use for the device. Random if not specified");
-DEFINE_bool(daemon, false,
- "Run cuttlefish in background, the launcher exits on boot "
- "completed/failed");
-
-DEFINE_string(device_title, "", "Human readable name for the instance, "
- "used by the vnc_server for its server title");
-DEFINE_string(setupwizard_mode, "DISABLED",
- "One of DISABLED,OPTIONAL,REQUIRED");
-
-DEFINE_string(qemu_binary,
- "/usr/bin/qemu-system-x86_64",
- "The qemu binary to use");
-DEFINE_string(hypervisor_uri, "qemu:///system", "Hypervisor cannonical uri.");
-DEFINE_bool(log_xml, false, "Log the XML machine configuration");
-
-DECLARE_string(config_file);
-
namespace {
-const std::string kDataPolicyUseExisting = "use_existing";
-const std::string kDataPolicyCreateIfMissing = "create_if_missing";
-const std::string kDataPolicyAlwaysCreate = "always_create";
-const std::string kDataPolicyResizeUpTo= "resize_up_to";
-constexpr char kAdbModeTunnel[] = "tunnel";
-constexpr char kAdbModeUsb[] = "usb";
-
-void CreateBlankImage(
- const std::string& image, int image_mb, const std::string& image_fmt) {
- LOG(INFO) << "Creating " << image;
- std::string of = "of=";
- of += image;
- std::string count = "count=";
- count += std::to_string(image_mb);
- cvd::execute({"/bin/dd", "if=/dev/zero", of, "bs=1M", count});
- cvd::execute({"/sbin/mkfs", "-t", image_fmt, image}, {"PATH=/sbin"});
-}
-
-void RemoveFile(const std::string& file) {
- LOG(INFO) << "Removing " << file;
- cvd::execute({"/bin/rm", "-f", file});
-}
-
-const int FSCK_ERROR_CORRECTED = 1;
-const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
-
-bool ForceFsckImage(const char* data_image) {
- int fsck_status = cvd::execute({"/sbin/e2fsck", "-y", "-f", data_image});
- if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
- LOG(ERROR) << "`e2fsck -y -f " << data_image << "` failed with code "
- << fsck_status;
- return false;
- }
- return true;
-}
-
-bool ResizeImage(const char* data_image, int data_image_mb) {
- auto file_mb = cvd::FileSize(data_image) >> 20;
- if (file_mb > data_image_mb) {
- LOG(ERROR) << data_image << " is already " << file_mb << " MB, will not "
- << "resize down.";
- return false;
- } else if (file_mb == data_image_mb) {
- LOG(INFO) << data_image << " is already the right size";
- return true;
+cvd::OnSocketReadyCb GetOnSubprocessExitCallback(
+ const vsoc::CuttlefishConfig& config) {
+ if (config.restart_subprocesses()) {
+ return cvd::ProcessMonitor::RestartOnExitCb;
} else {
- off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
- int truncate_status =
- cvd::SharedFD::Open(data_image, O_RDWR)->Truncate(raw_target);
- if (truncate_status != 0) {
- LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
- << data_image << "` failed with code " << truncate_status;
- return false;
+ return cvd::ProcessMonitor::DoNotMonitorCb;
+ }
+}
+
+// Maintains the state of the boot process, once a final state is reached
+// (success or failure) it sends the appropriate exit code to the foreground
+// launcher process
+class CvdBootStateMachine {
+ public:
+ CvdBootStateMachine(cvd::SharedFD fg_launcher_pipe)
+ : fg_launcher_pipe_(fg_launcher_pipe), state_(kBootStarted) {}
+
+ // Returns true if the machine is left in a final state
+ bool OnBootEvtReceived(cvd::SharedFD boot_events_pipe) {
+ monitor::BootEvent evt;
+ auto bytes_read = boot_events_pipe->Read(&evt, sizeof(evt));
+ if (bytes_read != sizeof(evt)) {
+ LOG(ERROR) << "Fail to read a complete event, read " << bytes_read
+ << " bytes only instead of the expected " << sizeof(evt);
+ state_ |= kGuestBootFailed;
+ } else if (evt == monitor::BootEvent::BootCompleted) {
+ LOG(INFO) << "Virtual device booted successfully";
+ state_ |= kGuestBootCompleted;
+ } else if (evt == monitor::BootEvent::BootFailed) {
+ LOG(ERROR) << "Virtual device failed to boot";
+ state_ |= kGuestBootFailed;
+ } // Ignore the other signals
+
+ return MaybeWriteToForegroundLauncher();
+ }
+
+ bool OnE2eTestCompleted(int exit_code) {
+ if (exit_code != 0) {
+ LOG(ERROR) << "VSoC e2e test failed";
+ state_ |= kE2eTestFailed;
+ } else {
+ LOG(INFO) << "VSoC e2e test passed";
+ state_ |= kE2eTestPassed;
}
- bool fsck_success = ForceFsckImage(data_image);
- if (!fsck_success) {
- return false;
- }
- int resize_status = cvd::execute({"/sbin/resize2fs", data_image});
- if (resize_status != 0) {
- LOG(ERROR) << "`resize2fs " << data_image << "` failed with code "
- << resize_status;
- return false;
- }
- fsck_success = ForceFsckImage(data_image);
- if (!fsck_success) {
- return false;
- }
- }
- return true;
-}
-
-bool ApplyDataImagePolicy(const char* data_image) {
- bool data_exists = cvd::FileHasContent(data_image);
- bool remove{};
- bool create{};
- bool resize{};
-
- if (FLAGS_data_policy == kDataPolicyUseExisting) {
- if (!data_exists) {
- LOG(ERROR) << "Specified data image file does not exists: " << data_image;
- return false;
- }
- if (FLAGS_blank_data_image_mb > 0) {
- LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
- << kDataPolicyUseExisting;
- return false;
- }
- create = false;
- remove = false;
- resize = false;
- } else if (FLAGS_data_policy == kDataPolicyAlwaysCreate) {
- remove = data_exists;
- create = true;
- resize = false;
- } else if (FLAGS_data_policy == kDataPolicyCreateIfMissing) {
- create = !data_exists;
- remove = false;
- resize = false;
- } else if (FLAGS_data_policy == kDataPolicyResizeUpTo) {
- create = false;
- remove = false;
- resize = true;
- } else {
- LOG(ERROR) << "Invalid data_policy: " << FLAGS_data_policy;
- return false;
+ return MaybeWriteToForegroundLauncher();
}
- if (remove) {
- RemoveFile(data_image);
+ bool BootCompleted() const {
+ bool boot_completed = state_ & kGuestBootCompleted;
+ bool test_passed_or_disabled =
+ (state_ & kE2eTestPassed) || (state_ & kE2eTestDisabled);
+ bool something_failed =
+ state_ & ~(kGuestBootCompleted | kE2eTestPassed | kE2eTestDisabled);
+ return boot_completed && test_passed_or_disabled && !something_failed;
}
- if (create) {
- if (FLAGS_blank_data_image_mb <= 0) {
- LOG(ERROR) << "-blank_data_image_mb is required to create data image";
- return false;
- }
- CreateBlankImage(
- data_image, FLAGS_blank_data_image_mb, FLAGS_blank_data_image_fmt);
- } else if (resize) {
- if (!data_exists) {
- LOG(ERROR) << data_image << " does not exist, but resizing was requested";
- return false;
- }
- return ResizeImage(data_image, FLAGS_blank_data_image_mb);
- } else {
- LOG(INFO) << data_image << " exists. Not creating it.";
+ bool DisableE2eTests() {
+ state_ |= kE2eTestDisabled;
+ return MaybeWriteToForegroundLauncher();
}
- return true;
-}
-
-std::string GetConfigFilePath(const vsoc::CuttlefishConfig& config) {
- return config.PerInstancePath("cuttlefish_config.json");
-}
-
-std::string GetGuestPortArg() {
- constexpr int kEmulatorPort = 5555;
- return std::string{"--guest_ports="} + std::to_string(kEmulatorPort);
-}
-
-int GetHostPort() {
- constexpr int kFirstHostPort = 6520;
- return vsoc::GetPerInstanceDefault(kFirstHostPort);
-}
-
-std::string GetHostPortArg() {
- return std::string{"--host_ports="} + std::to_string(GetHostPort());
-}
-
-std::string GetAdbConnectorPortArg() {
- return std::string{"--ports="} + std::to_string(GetHostPort());
-}
-
-bool AdbModeEnabled(const char* mode) {
- auto modes = cvd::StrSplit(FLAGS_adb_mode, ',');
- return std::find(modes.begin(), modes.end(), mode) != modes.end();
-}
-
-bool AdbTunnelEnabled() {
- return AdbModeEnabled(kAdbModeTunnel);
-}
-
-bool AdbUsbEnabled() {
- return AdbModeEnabled(kAdbModeUsb);
-}
-
-void ValidateAdbModeFlag() {
- if (!AdbUsbEnabled() && !AdbTunnelEnabled()) {
- LOG(INFO) << "ADB not enabled";
- }
-}
-
-cvd::SharedFD CreateIvServerUnixSocket(const std::string& path) {
- return cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM,
- 0666);
-}
-
-bool AdbConnectorEnabled() {
- return FLAGS_run_adb_connector && AdbTunnelEnabled();
-}
-
-void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config) {
- if (!AdbUsbEnabled()) {
- return;
- }
- auto socket_name = config.usb_v1_socket_name();
- auto usb_v1_server = cvd::SharedFD::SocketLocalServer(
- socket_name.c_str(), false, SOCK_STREAM, 0666);
- if (!usb_v1_server->IsOpen()) {
- LOG(ERROR) << "Unable to create USB v1 server socket: "
- << usb_v1_server->StrError();
- std::exit(cvd::LauncherExitCodes::kUsbV1SocketError);
- }
- cvd::Command usb_server(FLAGS_virtual_usb_manager_binary);
- usb_server.AddParameter("-usb_v1_fd=", usb_v1_server);
- usb_server.Start();
-}
-
-void LaunchKernelLogMonitor(const vsoc::CuttlefishConfig& config,
- cvd::SharedFD boot_events_pipe) {
- auto log_name = config.kernel_log_socket_name();
- auto server = cvd::SharedFD::SocketLocalServer(log_name.c_str(), false,
- SOCK_STREAM, 0666);
- cvd::Command kernel_log_monitor(FLAGS_kernel_log_monitor_binary);
- kernel_log_monitor.AddParameter("-log_server_fd=", server);
- if (boot_events_pipe->IsOpen()) {
- kernel_log_monitor.AddParameter("-subscriber_fd=", boot_events_pipe);
- }
- kernel_log_monitor.Start();
-}
-
-void LaunchIvServer(const vsoc::CuttlefishConfig& config) {
- // Resize gralloc region
- auto actual_width = cvd::AlignToPowerOf2(FLAGS_x_res * 4, 4); // align to 16
- uint32_t screen_buffers_size =
- FLAGS_num_screen_buffers *
- cvd::AlignToPageSize(actual_width * FLAGS_y_res + 16 /* padding */);
- screen_buffers_size +=
- (FLAGS_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(FLAGS_ivserver_binary);
- ivserver.AddParameter(
- "-qemu_socket_fd=",
- CreateIvServerUnixSocket(config.ivshmem_qemu_socket_path()));
- ivserver.AddParameter(
- "-client_socket_fd=",
- CreateIvServerUnixSocket(config.ivshmem_client_socket_path()));
- ivserver.Start();
-}
-
-void LaunchAdbConnectorIfEnabled() {
- if (AdbConnectorEnabled()) {
- cvd::Command adb_connector(FLAGS_adb_connector_binary);
- adb_connector.AddParameter(GetAdbConnectorPortArg());
- adb_connector.Start();
- }
-}
-
-void LaunchSocketForwardProxyIfEnabled() {
- if (AdbTunnelEnabled()) {
- cvd::Command adb_tunnel(FLAGS_socket_forward_proxy_binary);
- adb_tunnel.AddParameter(GetGuestPortArg());
- adb_tunnel.AddParameter(GetHostPortArg());
- adb_tunnel.Start();
- }
-}
-
-void LaunchVNCServerIfEnabled() {
- if (FLAGS_start_vnc_server) {
- // Launch the vnc server, don't wait for it to complete
- auto port_options = "-port=" + std::to_string(FLAGS_vnc_server_port);
- cvd::Command vnc_server(FLAGS_vnc_server_binary);
- vnc_server.AddParameter(port_options);
- vnc_server.Start();
- }
-}
-
-bool ResolveInstanceFiles() {
- if (FLAGS_system_image_dir.empty()) {
- LOG(ERROR) << "--system_image_dir must be specified.";
- return false;
+ bool BootFailed() const {
+ return state_ & (kGuestBootFailed | kE2eTestFailed);
}
- // If user did not specify location of either of these files, expect them to
- // be placed in --system_image_dir location.
- if (FLAGS_system_image.empty()) {
- FLAGS_system_image = FLAGS_system_image_dir + "/system.img";
+ private:
+ void SendExitCode(cvd::LauncherExitCodes exit_code) {
+ fg_launcher_pipe_->Write(&exit_code, sizeof(exit_code));
+ // The foreground process will exit after receiving the exit code, if we try
+ // to write again we'll get a SIGPIPE
+ fg_launcher_pipe_->Close();
}
- if (FLAGS_boot_image.empty()) {
- FLAGS_boot_image = FLAGS_system_image_dir + "/boot.img";
- }
- if (FLAGS_cache_image.empty()) {
- FLAGS_cache_image = FLAGS_system_image_dir + "/cache.img";
- }
- if (FLAGS_data_image.empty()) {
- FLAGS_data_image = FLAGS_system_image_dir + "/userdata.img";
- }
- if (FLAGS_vendor_image.empty()) {
- FLAGS_vendor_image = FLAGS_system_image_dir + "/vendor.img";
- }
-
- // Create data if necessary
- if (!ApplyDataImagePolicy(FLAGS_data_image.c_str())) {
- return false;
- }
-
- // Check that the files exist
- for (const auto& file :
- {FLAGS_system_image, FLAGS_vendor_image, FLAGS_cache_image,
- FLAGS_data_image, FLAGS_boot_image}) {
- if (!cvd::FileHasContent(file.c_str())) {
- LOG(ERROR) << "File not found: " << file;
- return false;
- }
- }
- return true;
-}
-
-bool UnpackBootImage(const cvd::BootImageUnpacker& boot_image_unpacker,
- const vsoc::CuttlefishConfig& config) {
- if (boot_image_unpacker.HasRamdiskImage()) {
- if (!boot_image_unpacker.ExtractRamdiskImage(
- config.ramdisk_image_path())) {
- LOG(ERROR) << "Error extracting ramdisk from boot image";
- return false;
- }
- }
- if (!FLAGS_kernel_path.size()) {
- if (boot_image_unpacker.HasKernelImage()) {
- if (!boot_image_unpacker.ExtractKernelImage(
- config.kernel_image_path())) {
- LOG(ERROR) << "Error extracting kernel from boot image";
+ bool MaybeWriteToForegroundLauncher() {
+ if (fg_launcher_pipe_->IsOpen()) {
+ if (BootCompleted()) {
+ SendExitCode(cvd::LauncherExitCodes::kSuccess);
+ } else if (state_ & kGuestBootFailed) {
+ SendExitCode(cvd::LauncherExitCodes::kVirtualDeviceBootFailed);
+ } else if (state_ & kE2eTestFailed) {
+ SendExitCode(cvd::LauncherExitCodes::kE2eTestFailed);
+ } else {
+ // No final state was reached
return false;
}
- } else {
- LOG(ERROR) << "No kernel found on boot image";
- return false;
}
+ // Either we sent the code before or just sent it, in any case the state is
+ // final
+ return true;
}
- return true;
+
+ cvd::SharedFD fg_launcher_pipe_;
+ int state_;
+ static const int kBootStarted = 0;
+ static const int kGuestBootCompleted = 1 << 0;
+ static const int kGuestBootFailed = 1 << 1;
+ static const int kE2eTestPassed = 1 << 2;
+ static const int kE2eTestFailed = 1 << 3;
+ static const int kE2eTestDisabled = 1 << 4;
+};
+
+// Abuse the process monitor to make it call us back when boot events are ready
+void SetUpHandlingOfBootEvents(
+ cvd::ProcessMonitor* process_monitor, cvd::SharedFD boot_events_pipe,
+ std::shared_ptr<CvdBootStateMachine> state_machine) {
+ process_monitor->MonitorExistingSubprocess(
+ // A dummy command, so logs are desciptive
+ cvd::Command("boot_events_listener"),
+ // A dummy subprocess, with the boot events pipe as control socket
+ cvd::Subprocess(-1, boot_events_pipe),
+ [boot_events_pipe, state_machine](cvd::MonitorEntry*) {
+ auto sent_code = state_machine->OnBootEvtReceived(boot_events_pipe);
+ return !sent_code;
+ });
}
-template<typename S, typename T>
-static std::string concat(const S& s, const T& t) {
- std::ostringstream os;
- os << s << t;
- return os.str();
-}
-
-std::string GetCuttlefishEnvPath() {
- return cvd::StringFromEnv("HOME", ".") + "/.cuttlefish.sh";
-}
-
-// Initializes the config object and saves it to file. It doesn't return it, all
-// further uses of the config should happen through the singleton
-bool InitializeCuttlefishConfiguration(
- const cvd::BootImageUnpacker& boot_image_unpacker) {
- 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)) {
- LOG(ERROR) << "Invalid vm_manager: " << FLAGS_vm_manager;
- return false;
- }
- tmp_config_obj.set_vm_manager(FLAGS_vm_manager);
-
- tmp_config_obj.set_serial_number(FLAGS_serial_number);
-
- tmp_config_obj.set_cpus(FLAGS_cpus);
- tmp_config_obj.set_memory_mb(FLAGS_memory_mb);
-
- tmp_config_obj.set_dpi(FLAGS_dpi);
- tmp_config_obj.set_setupwizard_mode(FLAGS_setupwizard_mode);
- tmp_config_obj.set_x_res(FLAGS_x_res);
- tmp_config_obj.set_y_res(FLAGS_y_res);
- tmp_config_obj.set_refresh_rate_hz(FLAGS_refresh_rate_hz);
- tmp_config_obj.set_gdb_flag(FLAGS_qemu_gdb);
- tmp_config_obj.set_adb_mode(FLAGS_adb_mode);
- tmp_config_obj.set_adb_ip_and_port("127.0.0.1:" + std::to_string(GetHostPort()));
-
- tmp_config_obj.set_device_title(FLAGS_device_title);
- if (FLAGS_kernel_path.size()) {
- tmp_config_obj.set_kernel_image_path(FLAGS_kernel_path);
+void LaunchE2eTestIfEnabled(cvd::ProcessMonitor* process_monitor,
+ std::shared_ptr<CvdBootStateMachine> state_machine,
+ const vsoc::CuttlefishConfig& config) {
+ if (config.run_e2e_test()) {
+ process_monitor->StartSubprocess(
+ cvd::Command(config.e2e_test_binary()),
+ [state_machine](cvd::MonitorEntry* entry) {
+ auto test_result = entry->proc->Wait();
+ state_machine->OnE2eTestCompleted(test_result);
+ return false;
+ });
} else {
- tmp_config_obj.set_kernel_image_path(
- tmp_config_obj.PerInstancePath("kernel"));
+ state_machine->DisableE2eTests();
}
-
- auto ramdisk_path = tmp_config_obj.PerInstancePath("ramdisk.img");
- bool use_ramdisk = boot_image_unpacker.HasRamdiskImage();
- if (!use_ramdisk) {
- LOG(INFO) << "No ramdisk present; assuming system-as-root build";
- ramdisk_path = "";
- }
-
- // This needs to be done here because the dtb path depends on the presence of
- // the ramdisk
- if (FLAGS_dtb.empty()) {
- if (use_ramdisk) {
- FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/initrd-root.dtb");
- } else {
- FLAGS_dtb = vsoc::DefaultHostArtifactsPath("config/system-root.dtb");
- }
- }
-
- tmp_config_obj.add_kernel_cmdline(boot_image_unpacker.kernel_cmdline());
- if (!use_ramdisk) {
- tmp_config_obj.add_kernel_cmdline("root=/dev/vda init=/init");
- }
- tmp_config_obj.add_kernel_cmdline(
- concat("androidboot.serialno=", FLAGS_serial_number));
- tmp_config_obj.add_kernel_cmdline("mac80211_hwsim.radios=0");
- tmp_config_obj.add_kernel_cmdline(concat("androidboot.lcd_density=", FLAGS_dpi));
- tmp_config_obj.add_kernel_cmdline(
- concat("androidboot.setupwizard_mode=", FLAGS_setupwizard_mode));
- tmp_config_obj.add_kernel_cmdline(concat("loop.max_part=", FLAGS_loop_max_part));
- if (!FLAGS_console.empty()) {
- tmp_config_obj.add_kernel_cmdline(concat("console=", FLAGS_console));
- }
- if (!FLAGS_androidboot_console.empty()) {
- tmp_config_obj.add_kernel_cmdline(
- concat("androidboot.console=", FLAGS_androidboot_console));
- }
- if (!FLAGS_hardware_name.empty()) {
- tmp_config_obj.add_kernel_cmdline(
- concat("androidboot.hardware=", FLAGS_hardware_name));
- }
- if (!FLAGS_guest_security.empty()) {
- tmp_config_obj.add_kernel_cmdline(concat("security=", FLAGS_guest_security));
- if (FLAGS_guest_enforce_security) {
- tmp_config_obj.add_kernel_cmdline("enforcing=1");
- } else {
- tmp_config_obj.add_kernel_cmdline("enforcing=0");
- tmp_config_obj.add_kernel_cmdline("androidboot.selinux=permissive");
- }
- if (FLAGS_guest_audit_security) {
- tmp_config_obj.add_kernel_cmdline("audit=1");
- } else {
- tmp_config_obj.add_kernel_cmdline("audit=0");
- }
- }
- if (FLAGS_extra_kernel_cmdline.size()) {
- tmp_config_obj.add_kernel_cmdline(FLAGS_extra_kernel_cmdline);
- }
-
- tmp_config_obj.set_ramdisk_image_path(ramdisk_path);
- tmp_config_obj.set_system_image_path(FLAGS_system_image);
- tmp_config_obj.set_cache_image_path(FLAGS_cache_image);
- tmp_config_obj.set_data_image_path(FLAGS_data_image);
- tmp_config_obj.set_vendor_image_path(FLAGS_vendor_image);
- tmp_config_obj.set_dtb_path(FLAGS_dtb);
-
- tmp_config_obj.set_mempath(FLAGS_mempath);
- tmp_config_obj.set_ivshmem_qemu_socket_path(
- tmp_config_obj.PerInstancePath("ivshmem_socket_qemu"));
- tmp_config_obj.set_ivshmem_client_socket_path(
- tmp_config_obj.PerInstancePath("ivshmem_socket_client"));
- tmp_config_obj.set_ivshmem_vector_count(memory_layout.GetRegions().size());
-
- if (AdbUsbEnabled()) {
- tmp_config_obj.set_usb_v1_socket_name(tmp_config_obj.PerInstancePath("usb-v1"));
- tmp_config_obj.set_vhci_port(FLAGS_vhci_port);
- tmp_config_obj.set_usb_ip_socket_name(tmp_config_obj.PerInstancePath("usb-ip"));
- }
-
- tmp_config_obj.set_kernel_log_socket_name(tmp_config_obj.PerInstancePath("kernel-log"));
- tmp_config_obj.set_deprecated_boot_completed(FLAGS_deprecated_boot_completed);
- tmp_config_obj.set_console_path(tmp_config_obj.PerInstancePath("console"));
- tmp_config_obj.set_logcat_path(tmp_config_obj.PerInstancePath("logcat"));
- tmp_config_obj.set_launcher_log_path(tmp_config_obj.PerInstancePath("launcher.log"));
- tmp_config_obj.set_launcher_monitor_socket_path(
- tmp_config_obj.PerInstancePath("launcher_monitor.sock"));
-
- tmp_config_obj.set_mobile_bridge_name(FLAGS_mobile_interface);
- tmp_config_obj.set_mobile_tap_name(FLAGS_mobile_tap_name);
-
- tmp_config_obj.set_wifi_bridge_name(FLAGS_wifi_interface);
- tmp_config_obj.set_wifi_tap_name(FLAGS_wifi_tap_name);
-
- tmp_config_obj.set_wifi_guest_mac_addr(FLAGS_guest_mac_address);
- tmp_config_obj.set_wifi_host_mac_addr(FLAGS_host_mac_address);
-
- tmp_config_obj.set_entropy_source("/dev/urandom");
- tmp_config_obj.set_uuid(FLAGS_uuid);
-
- tmp_config_obj.set_disable_dac_security(FLAGS_disable_dac_security);
- tmp_config_obj.set_disable_app_armor_security(FLAGS_disable_app_armor_security);
-
- tmp_config_obj.set_qemu_binary(FLAGS_qemu_binary);
- tmp_config_obj.set_hypervisor_uri(FLAGS_hypervisor_uri);
- tmp_config_obj.set_log_xml(FLAGS_log_xml);
-
- if(!AdbUsbEnabled()) {
- tmp_config_obj.disable_usb_adb();
- }
-
- tmp_config_obj.set_cuttlefish_env_path(GetCuttlefishEnvPath());
-
- auto config_file = GetConfigFilePath(tmp_config_obj);
- auto config_link = vsoc::GetGlobalConfigFileLink();
- // Save the config object before starting any host process
- if (!tmp_config_obj.SaveToFile(config_file)) {
- LOG(ERROR) << "Unable to save config object";
- return false;
- }
- setenv(vsoc::kCuttlefishConfigEnvVarName, config_file.c_str(), true);
- if (symlink(config_file.c_str(), config_link.c_str()) != 0) {
- LOG(ERROR) << "Failed to create symlink to config file at " << config_link
- << ": " << strerror(errno);
- return false;
- }
-
- return true;
-}
-
-void SetDefaultFlagsForQemu() {
- auto default_mobile_interface = GetPerInstanceDefault("cvd-mbr-");
- SetCommandLineOptionWithMode("mobile_interface",
- default_mobile_interface.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_mobile_tap_name = GetPerInstanceDefault("cvd-mtap-");
- SetCommandLineOptionWithMode("mobile_tap_name",
- default_mobile_tap_name.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_wifi_interface = GetPerInstanceDefault("cvd-wbr-");
- SetCommandLineOptionWithMode("wifi_interface",
- default_wifi_interface.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_wifi_tap_name = GetPerInstanceDefault("cvd-wtap-");
- SetCommandLineOptionWithMode("wifi_tap_name",
- default_wifi_tap_name.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_instance_dir =
- cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
- SetCommandLineOptionWithMode("instance_dir",
- default_instance_dir.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
-}
-
-void SetDefaultFlagsForLibvirt() {
- auto default_mobile_interface = GetPerInstanceDefault("cvd-mobile-");
- SetCommandLineOptionWithMode("mobile_interface",
- default_mobile_interface.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_mobile_tap_name = GetPerInstanceDefault("amobile");
- SetCommandLineOptionWithMode("mobile_tap_name",
- default_mobile_tap_name.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_wifi_interface = GetPerInstanceDefault("cvd-wifi-");
- SetCommandLineOptionWithMode("wifi_interface",
- default_wifi_interface.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_wifi_tap_name = GetPerInstanceDefault("awifi");
- SetCommandLineOptionWithMode("wifi_tap_name",
- default_wifi_tap_name.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
- auto default_instance_dir =
- "/var/run/libvirt-" +
- vsoc::GetPerInstanceDefault(vsoc::kDefaultUuidPrefix);
- SetCommandLineOptionWithMode("instance_dir",
- default_instance_dir.c_str(),
- google::FlagSettingMode::SET_FLAGS_DEFAULT);
-}
-
-bool ParseCommandLineFlags(int* argc, char*** argv) {
- // The config_file is created by the launcher, so the launcher is the only
- // host process that doesn't use the flag.
- // Set the default to empty.
- google::SetCommandLineOptionWithMode("config_file", "",
- gflags::SET_FLAGS_DEFAULT);
- google::ParseCommandLineNonHelpFlags(argc, argv, true);
- bool invalid_manager = false;
- if (FLAGS_vm_manager == vm_manager::LibvirtManager::name()) {
- SetDefaultFlagsForLibvirt();
- } else if (FLAGS_vm_manager == vm_manager::QemuManager::name()) {
- SetDefaultFlagsForQemu();
- } else {
- std::cerr << "Unknown Virtual Machine Manager: " << FLAGS_vm_manager
- << std::endl;
- invalid_manager = true;
- }
- google::HandleCommandLineHelpFlags();
- if (invalid_manager) {
- return false;
- }
- // Set the env variable to empty (in case the caller passed a value for it).
- unsetenv(vsoc::kCuttlefishConfigEnvVarName);
-
- ValidateAdbModeFlag();
-
- return ResolveInstanceFiles();
-}
-
-bool CleanPriorFiles() {
- // Everything on the instance directory
- std::string prior_files = FLAGS_instance_dir + "/*";
- // The shared memory file
- prior_files += " " + FLAGS_mempath;
- // The environment file
- prior_files += " " + GetCuttlefishEnvPath();
- // The global link to the config file
- prior_files += " " + vsoc::GetGlobalConfigFileLink();
- LOG(INFO) << "Assuming prior files of " << prior_files;
- std::string fuser_cmd = "fuser " + prior_files + " 2> /dev/null";
- int rval = std::system(fuser_cmd.c_str());
- // fuser returns 0 if any of the files are open
- if (WEXITSTATUS(rval) == 0) {
- LOG(ERROR) << "Clean aborted: files are in use";
- return false;
- }
- std::string clean_command = "rm -rf " + prior_files;
- if (FLAGS_vm_manager == vm_manager::LibvirtManager::name()) {
- // Libvirt runs as libvirt-qemu so we need sudo to delete the files it
- // creates
- clean_command = "sudo " + clean_command;
- }
- rval = std::system(clean_command.c_str());
- if (WEXITSTATUS(rval) != 0) {
- LOG(ERROR) << "Remove of files failed";
- return false;
- }
- return true;
}
bool WriteCuttlefishEnvironment(const vsoc::CuttlefishConfig& config) {
@@ -818,7 +206,7 @@
std::string config_env = "export CUTTLEFISH_PER_INSTANCE_PATH=\"" +
config.PerInstancePath(".") + "\"\n";
config_env += "export ANDROID_SERIAL=";
- if (AdbUsbEnabled()) {
+ if (AdbUsbEnabled(config)) {
config_env += config.serial_number();
} else {
config_env += "127.0.0.1:" + std::to_string(GetHostPort());
@@ -841,24 +229,22 @@
// Explicitly close here, otherwise we may end up reading forever if the
// child process dies.
write_end->Close();
- monitor::BootEvent evt;
- while(true) {
- auto bytes_read = read_end->Read(&evt, sizeof(evt));
- if (bytes_read != sizeof(evt)) {
- LOG(ERROR) << "Fail to read a complete event, read " << bytes_read
- << " bytes only instead of the expected " << sizeof(evt);
- std::exit(LauncherExitCodes::kPipeIOError);
- }
- if (evt == monitor::BootEvent::BootCompleted) {
- LOG(INFO) << "Virtual device booted successfully";
- std::exit(LauncherExitCodes::kSuccess);
- }
- if (evt == monitor::BootEvent::BootFailed) {
- LOG(ERROR) << "Virtual device failed to boot";
- std::exit(LauncherExitCodes::kVirtualDeviceBootFailed);
- }
- // Do nothing for the other signals
+ LauncherExitCodes exit_code;
+ auto bytes_read = read_end->Read(&exit_code, sizeof(exit_code));
+ if (bytes_read != sizeof(exit_code)) {
+ LOG(ERROR) << "Failed to read a complete exit code, read " << bytes_read
+ << " bytes only instead of the expected " << sizeof(exit_code);
+ exit_code = LauncherExitCodes::kPipeIOError;
+ } else if (exit_code == LauncherExitCodes::kSuccess) {
+ LOG(INFO) << "Virtual device booted successfully";
+ } else if (exit_code == LauncherExitCodes::kVirtualDeviceBootFailed) {
+ LOG(ERROR) << "Virtual device failed to boot";
+ } else if (exit_code == LauncherExitCodes::kE2eTestFailed) {
+ LOG(ERROR) << "Host VSoC region end to end test failed";
+ } else {
+ LOG(ERROR) << "Unexpected exit code: " << exit_code;
}
+ std::exit(exit_code);
} else {
// The child returns the write end of the pipe
if (daemon(/*nochdir*/ 1, /*noclose*/ 1) != 0) {
@@ -961,38 +347,8 @@
int main(int argc, char** argv) {
::android::base::InitLogging(argv, android::base::StderrLogger);
- if (!ParseCommandLineFlags(&argc, &argv)) {
- LOG(ERROR) << "Failed to parse command arguments";
- return LauncherExitCodes::kArgumentParsingError;
- }
- // Clean up prior files before saving the config file (doing it after would
- // delete it)
- if (!CleanPriorFiles()) {
- LOG(ERROR) << "Failed to clean prior files";
- return LauncherExitCodes::kPrioFilesCleanupError;
- }
- // For now it has to be the vm manager who ensures the instance dir exists
- // because in the case of the libvirt manager root privileges are required to
- // create and set acls on the directory
- if (!vm_manager::VmManager::EnsureInstanceDirExists(FLAGS_vm_manager,
- FLAGS_instance_dir)) {
- LOG(ERROR) << "Failed to create instance directory";
- return LauncherExitCodes::kInstanceDirCreationError;
- }
-
- auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
-
- if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
- LOG(ERROR) << "Failed to initialize configuration";
- return LauncherExitCodes::kCuttlefishConfigurationInitError;
- }
- // Do this early so that the config object is ready for anything that needs it
- auto config = vsoc::CuttlefishConfig::Get();
- if (!config) {
- LOG(ERROR) << "Failed to obtain config singleton";
- return LauncherExitCodes::kCuttlefishConfigurationInitError;
- }
+ auto config = InitFilesystemAndCreateConfig(&argc, &argv);
auto vm_manager = vm_manager::VmManager::Get(config->vm_manager(), config);
@@ -1009,23 +365,12 @@
return LauncherExitCodes::kInvalidHostConfiguration;
}
- if (!vm_manager->EnsureInstanceDirExists(FLAGS_vm_manager,
- FLAGS_instance_dir)) {
- LOG(ERROR) << "Failed to create instance directory: " << FLAGS_instance_dir;
- return LauncherExitCodes::kInstanceDirCreationError;
- }
-
- if (!UnpackBootImage(*boot_img_unpacker, *config)) {
- LOG(ERROR) << "Failed to unpack boot image";
- return LauncherExitCodes::kBootImageUnpackError;
- }
-
if (!WriteCuttlefishEnvironment(*config)) {
LOG(ERROR) << "Unable to write cuttlefish environment file";
}
LOG(INFO) << "The following files contain useful debugging information:";
- if (FLAGS_daemon) {
+ if (config->run_as_daemon()) {
LOG(INFO) << " Launcher log: " << config->launcher_log_path();
}
LOG(INFO) << " Android's logcat output: " << config->logcat_path();
@@ -1043,10 +388,10 @@
<< launcher_monitor_socket->StrError();
return cvd::LauncherExitCodes::kMonitorCreationFailed;
}
- cvd::SharedFD boot_events_pipe;
- if (FLAGS_daemon) {
- boot_events_pipe = DaemonizeLauncher(*config);
- if (!boot_events_pipe->IsOpen()) {
+ cvd::SharedFD foreground_launcher_pipe;
+ if (config->run_as_daemon()) {
+ foreground_launcher_pipe = DaemonizeLauncher(*config);
+ if (!foreground_launcher_pipe->IsOpen()) {
return LauncherExitCodes::kDaemonizationError;
}
} else {
@@ -1061,23 +406,44 @@
}
}
- LaunchKernelLogMonitor(*config, boot_events_pipe);
- LaunchUsbServerIfEnabled(*config);
- LaunchIvServer(*config);
+ auto boot_state_machine =
+ std::make_shared<CvdBootStateMachine>(foreground_launcher_pipe);
- // Initialize the regions that require so before the VM starts.
- PreLaunchInitializers::Initialize(*config);
+ // Monitor and restart host processes supporting the CVD
+ cvd::ProcessMonitor process_monitor;
+
+ cvd::SharedFD boot_events_pipe;
+ // Only subscribe to boot events if running as daemon
+ process_monitor.StartSubprocess(
+ GetKernelLogMonitorCommand(*config,
+ config->run_as_daemon()
+ ? &boot_events_pipe
+ : nullptr),
+ GetOnSubprocessExitCallback(*config));
+
+ SetUpHandlingOfBootEvents(&process_monitor, boot_events_pipe,
+ boot_state_machine);
+
+ LaunchLogcatReceiverIfEnabled(*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);
// Start the guest VM
- if (!vm_manager->Start()) {
- LOG(ERROR) << "Unable to start vm_manager";
- // TODO(111453282): All host processes should die here.
- return LauncherExitCodes::kVMCreationError;
- }
+ process_monitor.StartSubprocess(vm_manager->StartCommand(),
+ GetOnSubprocessExitCallback(*config));
- LaunchSocketForwardProxyIfEnabled();
- LaunchVNCServerIfEnabled();
- LaunchAdbConnectorIfEnabled();
+ // Start other host processes
+ LaunchSocketForwardProxyIfEnabled(&process_monitor, *config);
+ LaunchSocketVsockProxyIfEnabled(&process_monitor, *config);
+ LaunchVNCServerIfEnabled(*config, &process_monitor,
+ GetOnSubprocessExitCallback(*config));
+ LaunchStreamAudioIfEnabled(*config, &process_monitor,
+ GetOnSubprocessExitCallback(*config));
+ LaunchAdbConnectorIfEnabled(&process_monitor, *config);
ServerLoop(launcher_monitor_socket, vm_manager); // Should not return
LOG(ERROR) << "The server loop returned, it should never happen!!";
diff --git a/host/commands/launch/pre_launch_initializers.h b/host/commands/launch/pre_launch_initializers.h
index 925cdf8..79f6eed 100644
--- a/host/commands/launch/pre_launch_initializers.h
+++ b/host/commands/launch/pre_launch_initializers.h
@@ -25,14 +25,10 @@
// To add initializers for more regions declare here, implement in its own
// source file and call from PreLaunchInitializers::Initialize().
void InitializeScreenRegion(const vsoc::CuttlefishConfig& config);
-void InitializeRilRegion(const vsoc::CuttlefishConfig& config);
-void InitializeWifiRegion(const vsoc::CuttlefishConfig& config);
class PreLaunchInitializers {
public:
static void Initialize(const vsoc::CuttlefishConfig& config) {
InitializeScreenRegion(config);
- InitializeRilRegion(config);
- InitializeWifiRegion(config);
}
};
diff --git a/host/commands/launch/process_monitor.cc b/host/commands/launch/process_monitor.cc
new file mode 100644
index 0000000..a405180
--- /dev/null
+++ b/host/commands/launch/process_monitor.cc
@@ -0,0 +1,185 @@
+/*
+ * 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <map>
+
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_select.h"
+#include "host/commands/launch/process_monitor.h"
+
+namespace cvd {
+
+namespace {
+
+void NotifyThread(SharedFD fd) {
+ // The restarter thread is (likely) blocked on a call to select, to make it
+ // wake up and do some work we write something (anything, the content is not
+ // important) into the main side of the socket pair so that the call to select
+ // returns and the notification fd (restarter side of the socket pair) is
+ // marked as ready to read.
+ char buffer = 'a';
+ fd->Write(&buffer, sizeof(buffer));
+}
+
+void ConsumeNotifications(SharedFD fd) {
+ // Once the starter thread is waken up due to a notification, the calls to
+ // select will continue to return immediately unless we read what was written
+ // on the main side of the socket pair. More than one notification can
+ // accumulate before the restarter thread consumes them, so we attempt to read
+ // more than it's written to consume them all at once. In the unlikely case of
+ // more than 8 notifications acummulating we simply read the first 8 and have
+ // another iteration on the restarter thread loop.
+ char buffer[8];
+ fd->Read(buffer, sizeof(buffer));
+}
+
+} // namespace
+
+ProcessMonitor::ProcessMonitor() {
+ if (!SharedFD::SocketPair(AF_LOCAL, SOCK_STREAM, 0, &thread_comm_main_,
+ &thread_comm_monitor_)) {
+ LOG(ERROR) << "Unable to create restarter communication socket pair: "
+ << strerror(errno);
+ return;
+ }
+ monitor_thread_ = std::thread([this]() { MonitorRoutine(); });
+}
+
+void ProcessMonitor::StartSubprocess(Command cmd, OnSocketReadyCb callback) {
+ auto proc = cmd.Start(true);
+ if (!proc.Started()) {
+ LOG(ERROR) << "Failed to start process";
+ return;
+ }
+ MonitorExistingSubprocess(std::move(cmd), std::move(proc), callback);
+}
+
+void ProcessMonitor::MonitorExistingSubprocess(Command cmd, Subprocess proc,
+ OnSocketReadyCb callback) {
+ {
+ std::lock_guard<std::mutex> lock(processes_mutex_);
+ monitored_processes_.push_back(MonitorEntry());
+ auto& entry = monitored_processes_.back();
+ entry.cmd.reset(new Command(std::move(cmd)));
+ entry.proc.reset(new Subprocess(std::move(proc)));
+ entry.on_control_socket_ready_cb = callback;
+ }
+ // Wake the restarter thread up so that it starts monitoring this subprocess
+ // Do this after releasing the lock so that the restarter thread is free to
+ // begin work as soon as select returns.
+ NotifyThread(thread_comm_main_);
+}
+
+bool ProcessMonitor::RestartOnExitCb(MonitorEntry* entry) {
+ // Make sure the process actually exited
+ char buffer[16];
+ auto bytes_read = entry->proc->control_socket()->Read(buffer, sizeof(buffer));
+ if (bytes_read > 0) {
+ LOG(WARNING) << "Subprocess " << entry->cmd->GetShortName() << " wrote "
+ << bytes_read
+ << " bytes on the control socket, this is unexpected";
+ // The process may not have exited, continue monitoring without restarting
+ return true;
+ }
+
+ LOG(INFO) << "Detected exit of monitored subprocess";
+ // Make sure the subprocess isn't left in a zombie state, and that the
+ // pid is logged
+ int wstatus;
+ auto wait_ret = TEMP_FAILURE_RETRY(entry->proc->Wait(&wstatus, 0));
+ // None of the error conditions specified on waitpid(2) apply
+ assert(wait_ret > 0);
+ if (WIFEXITED(wstatus)) {
+ LOG(INFO) << "Subprocess " << entry->cmd->GetShortName() << " ("
+ << wait_ret << ") has exited with exit code "
+ << WEXITSTATUS(wstatus);
+ } else if (WIFSIGNALED(wstatus)) {
+ LOG(ERROR) << "Subprocess " << entry->cmd->GetShortName() << " ("
+ << wait_ret << ") was interrupted by a signal: "
+ << WTERMSIG(wstatus);
+ } else {
+ LOG(INFO) << "subprocess " << entry->cmd->GetShortName() << " ("
+ << wait_ret << ") has exited for unknown reasons";
+ }
+ entry->proc.reset(new Subprocess(entry->cmd->Start(true)));
+ return true;
+}
+
+bool ProcessMonitor::DoNotMonitorCb(MonitorEntry*) {
+ return false;
+}
+
+void ProcessMonitor::MonitorRoutine() {
+ LOG(INFO) << "Started monitoring subprocesses";
+ do {
+ SharedFDSet read_set;
+ read_set.Set(thread_comm_monitor_);
+ {
+ std::lock_guard<std::mutex> lock(processes_mutex_);
+ for (auto& monitored_process: monitored_processes_) {
+ auto control_socket = monitored_process.proc->control_socket();
+ if (!control_socket->IsOpen()) {
+ LOG(ERROR) << "The control socket for "
+ << monitored_process.cmd->GetShortName()
+ << " is closed, it's effectively NOT being monitored";
+ }
+ read_set.Set(control_socket);
+ }
+ }
+ // We can't call select while holding the lock as it would lead to a
+ // deadlock (restarter thread waiting for notifications from main thread,
+ // main thread waiting for the lock)
+ int num_fds = cvd::Select(&read_set, nullptr, nullptr, nullptr);
+ if (num_fds < 0) {
+ LOG(ERROR) << "Select call returned error on restarter thread: "
+ << strerror(errno);
+ }
+ if (num_fds > 0) {
+ // Try the communication fd, it's the most likely to be set
+ if (read_set.IsSet(thread_comm_monitor_)) {
+ --num_fds;
+ ConsumeNotifications(thread_comm_monitor_);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(processes_mutex_);
+ // Keep track of the number of file descriptors ready for read, chances
+ // are we don't need to go over the entire list of subprocesses
+ auto it = monitored_processes_.begin();
+ while (it != monitored_processes_.end()) {
+ auto control_socket = it->proc->control_socket();
+ bool keep_monitoring = true;
+ if (read_set.IsSet(control_socket)) {
+ --num_fds;
+ keep_monitoring = it->on_control_socket_ready_cb(&(*it));
+ }
+ if (keep_monitoring) {
+ ++it;
+ } else {
+ it = monitored_processes_.erase(it);
+ }
+ }
+ }
+ assert(num_fds == 0);
+ } while (true);
+}
+
+} // namespace cvd
diff --git a/host/commands/launch/process_monitor.h b/host/commands/launch/process_monitor.h
new file mode 100644
index 0000000..a375653
--- /dev/null
+++ b/host/commands/launch/process_monitor.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <common/libs/utils/subprocess.h>
+
+namespace cvd {
+
+struct MonitorEntry;
+using OnSocketReadyCb = std::function<bool(MonitorEntry*)>;
+
+struct MonitorEntry {
+ std::unique_ptr<Command> cmd;
+ std::unique_ptr<Subprocess> proc;
+ OnSocketReadyCb on_control_socket_ready_cb;
+};
+
+// Keeps track of launched subprocesses, restarts them if they unexpectedly exit
+class ProcessMonitor {
+ public:
+ ProcessMonitor();
+ // Starts a managed subprocess with a controlling socket. The
+ // on_control_socket_ready_cb callback will be called when data is ready to be
+ // read from the socket or the subprocess has ended. No member functions of
+ // the process monitor object should be called from the callback as it may
+ // lead to a dealock. If the callback returns false the subprocess will no
+ // longer be monitored
+ void StartSubprocess(Command cmd, OnSocketReadyCb on_control_socket_ready_cb);
+ // Monitors an alreacy started subprocess
+ void MonitorExistingSubprocess(Command cmd, Subprocess sub_process,
+ OnSocketReadyCb on_control_socket_ready_cb);
+ static bool RestartOnExitCb(MonitorEntry* entry);
+ static bool DoNotMonitorCb(MonitorEntry* entry);
+
+ private:
+ void MonitorRoutine();
+
+ std::vector<MonitorEntry> monitored_processes_;
+ // Used for communication with the restarter thread
+ cvd::SharedFD thread_comm_main_, thread_comm_monitor_;
+ std::thread monitor_thread_;
+ // Protects access to the monitored_processes_
+ std::mutex processes_mutex_;
+};
+} // namespace cvd
diff --git a/host/commands/launch/ril_region_handler.cc b/host/commands/launch/ril_config.cc
similarity index 76%
rename from host/commands/launch/ril_region_handler.cc
rename to host/commands/launch/ril_config.cc
index b3bb00a..195256a 100644
--- a/host/commands/launch/ril_region_handler.cc
+++ b/host/commands/launch/ril_config.cc
@@ -20,11 +20,13 @@
#include <string.h>
#include <memory>
+#include <sstream>
#include <string>
-#include "common/vsoc/lib/ril_region_view.h"
-#include "host/commands/launch/pre_launch_initializers.h"
-#include "host/libs/config/cuttlefish_config.h"
+#include <glog/logging.h>
+
+#include "common/libs/constants/ril.h"
+#include "host/commands/launch/ril_config.h"
namespace {
@@ -119,31 +121,31 @@
return ret;
}
};
+
+template <typename T>
+std::string BuildPropertyDefinition(const std::string& prop_name,
+ const T& prop_value) {
+ std::ostringstream stream;
+ stream << prop_name << "=" << prop_value;
+ return stream.str();
+}
} // namespace
-void InitializeRilRegion(const vsoc::CuttlefishConfig& config) {
+void ConfigureRil(vsoc::CuttlefishConfig* config) {
NetConfig netconfig;
- if (!netconfig.ObtainConfig(config.mobile_bridge_name())) {
+ if (!netconfig.ObtainConfig(config->mobile_bridge_name())) {
LOG(ERROR) << "Unable to obtain the network configuration";
return;
}
- auto region =
- vsoc::ril::RilRegionView::GetInstance(vsoc::GetDomain().c_str());
-
- if (!region) {
- LOG(ERROR) << "Ril region was not found";
- return;
- }
-
- auto dest = region->data();
-
- snprintf(dest->ipaddr, sizeof(dest->ipaddr), "%s",
- netconfig.ril_ipaddr.c_str());
- snprintf(dest->gateway, sizeof(dest->gateway), "%s",
- netconfig.ril_gateway.c_str());
- snprintf(dest->dns, sizeof(dest->dns), "%s", netconfig.ril_dns.c_str());
- snprintf(dest->broadcast, sizeof(dest->broadcast), "%s",
- netconfig.ril_broadcast.c_str());
- dest->prefixlen = netconfig.ril_prefixlen;
+ config->add_kernel_cmdline(BuildPropertyDefinition(
+ CUTTLEFISH_RIL_ADDR_PROPERTY, netconfig.ril_ipaddr));
+ config->add_kernel_cmdline(BuildPropertyDefinition(
+ CUTTLEFISH_RIL_GATEWAY_PROPERTY, netconfig.ril_gateway));
+ config->add_kernel_cmdline(BuildPropertyDefinition(
+ CUTTLEFISH_RIL_DNS_PROPERTY, netconfig.ril_dns));
+ config->add_kernel_cmdline(BuildPropertyDefinition(
+ CUTTLEFISH_RIL_BROADCAST_PROPERTY, netconfig.ril_broadcast));
+ config->add_kernel_cmdline(BuildPropertyDefinition(
+ CUTTLEFISH_RIL_PREFIXLEN_PROPERTY, netconfig.ril_prefixlen));
}
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/host/commands/launch/ril_config.h
similarity index 73%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to host/commands/launch/ril_config.h
index b6a037b..49a43e8 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/host/commands/launch/ril_config.h
@@ -1,6 +1,7 @@
#pragma once
+
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +16,6 @@
* limitations under the License.
*/
-#include "hwcomposer_common.h"
+#include "host/libs/config/cuttlefish_config.h"
-namespace cvd {
-
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
-
-} // namespace cvd
+void ConfigureRil(vsoc::CuttlefishConfig* config);
\ No newline at end of file
diff --git a/host/commands/launch/wifi_region_handler.cc b/host/commands/launch/wifi_region_handler.cc
deleted file mode 100644
index 5c001b7..0000000
--- a/host/commands/launch/wifi_region_handler.cc
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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 <string>
-
-#include <glog/logging.h>
-
-#include "common/vsoc/lib/wifi_exchange_view.h"
-#include "host/commands/launch/pre_launch_initializers.h"
-#include "host/libs/config/cuttlefish_config.h"
-
-using vsoc::wifi::WifiExchangeView;
-
-void InitializeWifiRegion(const vsoc::CuttlefishConfig& config) {
- auto region = WifiExchangeView::GetInstance(vsoc::GetDomain().c_str());
- if (!region) {
- LOG(FATAL) << "Wifi region not found";
- return;
- }
- WifiExchangeView::MacAddress guest_mac, host_mac;
- if (!WifiExchangeView::ParseMACAddress(config.wifi_guest_mac_addr(),
- &guest_mac)) {
- LOG(FATAL) << "Unable to parse guest mac address: "
- << config.wifi_guest_mac_addr();
- return;
- }
- LOG(INFO) << "Setting guest mac to " << config.wifi_guest_mac_addr();
- region->SetGuestMACAddress(guest_mac);
- if (!WifiExchangeView::ParseMACAddress(config.wifi_host_mac_addr(),
- &host_mac)) {
- LOG(FATAL) << "Unable to parse guest mac address: "
- << config.wifi_guest_mac_addr();
- return;
- }
- LOG(INFO) << "Setting host mac to " << config.wifi_host_mac_addr();
- region->SetHostMACAddress(host_mac);
-}
diff --git a/host/commands/logcat_receiver/Android.bp b/host/commands/logcat_receiver/Android.bp
new file mode 100644
index 0000000..16f3bf9
--- /dev/null
+++ b/host/commands/logcat_receiver/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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_host {
+ name: "logcat_receiver",
+ srcs: [
+ "main.cpp",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcuttlefish_fs",
+ "liblog",
+ "libcuttlefish_utils",
+ "cuttlefish_auto_resources",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libgflags",
+ "libjsoncpp",
+ ],
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/commands/logcat_receiver/main.cpp b/host/commands/logcat_receiver/main.cpp
new file mode 100644
index 0000000..e9cbad2
--- /dev/null
+++ b/host/commands/logcat_receiver/main.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+DEFINE_int32(
+ server_fd, -1,
+ "File descriptor to an already created vsock server. If negative a new "
+ "server will be created at the port specified on the config file");
+
+int main(int argc, char** argv) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ auto config = vsoc::CuttlefishConfig::Get();
+
+ auto path = config->logcat_path();
+ auto logcat_file =
+ cvd::SharedFD::Open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0666);
+ CHECK(logcat_file->IsOpen())
+ << "Unable to open logcat file: " << logcat_file->StrError();
+
+ cvd::SharedFD server_fd;
+ if (FLAGS_server_fd < 0) {
+ unsigned int port = config->logcat_vsock_port();
+ server_fd = cvd::SharedFD::VsockServer(port, SOCK_STREAM);
+ } else {
+ server_fd = cvd::SharedFD::Dup(FLAGS_server_fd);
+ close(FLAGS_server_fd);
+ }
+
+ CHECK(server_fd->IsOpen()) << "Error creating or inheriting logcat server: "
+ << server_fd->StrError();
+
+ // Server loop
+ while (true) {
+ auto conn = cvd::SharedFD::Accept(*server_fd);
+
+ while (true) {
+ char buff[1024];
+ auto read = conn->Read(buff, sizeof(buff));
+ if (read <= 0) {
+ // Close here to ensure the other side gets reset if it's still
+ // connected
+ conn->Close();
+ LOG(WARNING) << "Detected close from the other side";
+ break;
+ }
+ auto written = logcat_file->Write(buff, read);
+ CHECK(written == read)
+ << "Error writing to log file: " << logcat_file->StrError()
+ << ". This is unrecoverable.";
+ }
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/host/commands/record_audio/main.cc b/host/commands/record_audio/main.cc
index 18466f3..3c024b8 100644
--- a/host/commands/record_audio/main.cc
+++ b/host/commands/record_audio/main.cc
@@ -50,7 +50,7 @@
auto worker = audio_data_rv->StartWorker();
std::unique_ptr<WaveWriter> writer;
- int64_t frameCount = 0ll;
+ int64_t frameCount = 0LL;
// The configuration the writer is setup for.
gce_audio_message writer_hdr;
diff --git a/host/commands/stop_cvd/Android.bp b/host/commands/stop_cvd/Android.bp
index 25ab59c..b8322b5 100644
--- a/host/commands/stop_cvd/Android.bp
+++ b/host/commands/stop_cvd/Android.bp
@@ -26,8 +26,6 @@
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_auto_resources",
- "libicuuc",
- "libandroidicu",
],
static_libs: [
"libcuttlefish_host_config",
@@ -36,5 +34,5 @@
"libgflags",
"libxml2",
],
- defaults: ["cuttlefish_host_only"],
+ defaults: ["cuttlefish_host_only", "cuttlefish_libicuuc"],
}
diff --git a/host/commands/stop_cvd/main.cc b/host/commands/stop_cvd/main.cc
index e537314..b631d75 100644
--- a/host/commands/stop_cvd/main.cc
+++ b/host/commands/stop_cvd/main.cc
@@ -44,7 +44,6 @@
#include "host/commands/launch/launcher_defs.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/vm_manager/vm_manager.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
DEFINE_int32(wait_for_launcher, 5,
"How many seconds to wait for the launcher to respond to the stop "
@@ -54,12 +53,9 @@
// Gets a set of the possible process groups of a previous launch
std::set<pid_t> GetCandidateProcessGroups() {
std::string cmd = "fuser";
- // Add the instance directory for qemu
+ // Add the instance directory
cmd += " " + cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime/*";
- // Add the instance directory for libvirt
- auto libvirt_instance_dir =
- std::string("/var/run/libvirt-") + vsoc::kDefaultUuidPrefix;
- cmd += " " + vsoc::GetPerInstanceDefault(libvirt_instance_dir.c_str()) + "/*";
+ // Add the shared memory file
cmd += " " + vsoc::GetPerInstanceDefault("/dev/shm/cvd-");
std::shared_ptr<FILE> cmd_out(popen(cmd.c_str(), "r"), pclose);
if (!cmd_out) {
@@ -84,22 +80,6 @@
int FallBackStop() {
auto exit_code = 1; // Having to fallback is an error
- if (vm_manager::VmManager::IsVmManagerSupported(
- vm_manager::LibvirtManager::name())) {
- // Libvirt doesn't run as the same user as stop_cvd, so we must stop it
- // through the manager. Qemu on the other hand would get killed by the
- // commands below.
- vsoc::CuttlefishConfig config;
- config.set_instance_dir(std::string("/var/run/libvirt-") +
- vsoc::kDefaultUuidPrefix);
- auto vm_manager =
- vm_manager::VmManager::Get(vm_manager::LibvirtManager::name(), &config);
- if (!vm_manager->Stop()) {
- LOG(WARNING) << "Failed to stop the libvirt domain: Is it still running? "
- "Is it using qemu_cli?";
- exit_code |= 2;
- }
- }
auto process_groups = GetCandidateProcessGroups();
for (auto pgid: process_groups) {
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/adb_connector/main.cpp b/host/frontend/adb_connector/main.cpp
index 27574ee..e046b3d 100644
--- a/host/frontend/adb_connector/main.cpp
+++ b/host/frontend/adb_connector/main.cpp
@@ -30,18 +30,18 @@
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/adb_connection_maintainer/adb_connection_maintainer.h"
-DEFINE_string(ports, "", "Comma-separated list of ports to 'adb connect' to");
+DEFINE_string(addresses, "", "Comma-separated list of addresses to 'adb connect' to");
namespace {
-void LaunchConnectionMaintainerThread(int port) {
- std::thread(cvd::EstablishAndMaintainConnection, port).detach();
+void LaunchConnectionMaintainerThread(const std::string& address) {
+ std::thread(cvd::EstablishAndMaintainConnection, address).detach();
}
-std::vector<int> ParsePortsList(std::string ports) {
+std::vector<std::string> ParseAddressList(std::string ports) {
std::replace(ports.begin(), ports.end(), ',', ' ');
std::istringstream port_stream{ports};
- return {std::istream_iterator<int>{port_stream},
- std::istream_iterator<int>{}};
+ return {std::istream_iterator<std::string>{port_stream},
+ std::istream_iterator<std::string>{}};
}
[[noreturn]] void SleepForever() {
@@ -53,10 +53,10 @@
int main(int argc, char* argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
- CHECK(!FLAGS_ports.empty()) << "Must specify --ports flag";
+ CHECK(!FLAGS_addresses.empty()) << "Must specify --addresses flag";
- for (auto port : ParsePortsList(FLAGS_ports)) {
- LaunchConnectionMaintainerThread(port);
+ for (auto address : ParseAddressList(FLAGS_addresses)) {
+ LaunchConnectionMaintainerThread(address);
}
SleepForever();
diff --git a/host/frontend/stream_audio/Android.bp b/host/frontend/stream_audio/Android.bp
new file mode 100644
index 0000000..ab5ff8e
--- /dev/null
+++ b/host/frontend/stream_audio/Android.bp
@@ -0,0 +1,37 @@
+//
+// 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",
+ 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..e0b97ac
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_host_static {
+ name: "libopuscpp",
+ srcs: [
+ "opus_wrapper.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "libopus",
+ ],
+ cpp_std: "c++17",
+ 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 241df83..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -20,6 +20,7 @@
"frame_buffer_watcher.cpp",
"jpeg_compressor.cpp",
"main.cpp",
+ "screen_connector.cpp",
"simulated_hw_composer.cpp",
"virtual_inputs.cpp",
"vnc_client_connection.cpp",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
new file mode 100644
index 0000000..ea82f7e
--- /dev/null
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/frontend/vnc_server/screen_connector.h"
+
+#include <atomic>
+#include <condition_variable>
+
+#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, "");
+
+namespace cvd {
+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 {
+ public:
+ SocketBasedScreenConnector(vsoc::CuttlefishConfig* config) : config_(config) {
+ screen_server_thread_ = std::thread([this]() { ServerLoop(); });
+ }
+
+ int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+ std::unique_lock<std::mutex> lock(new_frame_mtx_);
+ while (seq_num_ == *seq_num) {
+ new_frame_cond_var_.wait(lock);
+ }
+ return newest_buffer_;
+ }
+
+ void* GetBuffer(int buffer_idx) override {
+ if (buffer_idx < 0) return nullptr;
+ buffer_idx %= NUM_BUFFERS_;
+ return &buffer_[buffer_idx * ScreenSizeInBytes()];
+ }
+
+ private:
+ static constexpr int NUM_BUFFERS_ = 4;
+
+ void ServerLoop() {
+ if (FLAGS_frame_server_fd < 0) {
+ LOG(FATAL) << "Invalid file descriptor: " << FLAGS_frame_server_fd;
+ return;
+ }
+ auto server = SharedFD::Dup(FLAGS_frame_server_fd);
+ close(FLAGS_frame_server_fd);
+ if (!server->IsOpen()) {
+ LOG(FATAL) << "Unable to dup screen server: " << server->StrError();
+ return;
+ }
+
+ int current_buffer = 0;
+
+ while (1) {
+ auto conn = SharedFD::Accept(*server);
+ while (conn->IsOpen()) {
+ SendScreenParameters(conn);
+
+ int32_t size = 0;
+ conn->Read(&size, sizeof(size));
+ auto buff = reinterpret_cast<uint8_t*>(GetBuffer(current_buffer));
+ while (size > 0) {
+ auto read = conn->Read(buff, size);
+ if (read < 0) {
+ LOG(ERROR) << "Failed to read from hwcomposer: "
+ << conn->StrError();
+ return;
+ }
+ size -= read;
+ buff += read;
+ }
+ BroadcastNewFrame(current_buffer);
+ current_buffer = (current_buffer + 1) % NUM_BUFFERS_;
+ }
+ }
+ }
+
+ void SendScreenParameters(SharedFD conn) const {
+ // TODO(b/128842613): Send this info from the configuration server
+ int32_t screen_params[4];
+ screen_params[0] = config_->x_res();
+ screen_params[1] = config_->y_res();
+ screen_params[2] = config_->dpi();
+ screen_params[3] = config_->refresh_rate_hz();
+ int buff_size = sizeof(screen_params);
+ int res = conn->Write(screen_params, buff_size);
+ if (res != buff_size) {
+ LOG(FATAL)
+ << "Unable to send full screen parameters to the hwcomposer ("
+ << res << "): " << conn->StrError();
+ }
+ }
+
+ void BroadcastNewFrame(int buffer_idx) {
+ {
+ std::lock_guard<std::mutex> lock(new_frame_mtx_);
+ seq_num_++;
+ newest_buffer_ = buffer_idx;
+ }
+ new_frame_cond_var_.notify_all();
+ }
+
+ vsoc::CuttlefishConfig* config_;
+ std::vector<std::uint8_t> buffer_ =
+ std::vector<std::uint8_t>(NUM_BUFFERS_ * ScreenSizeInBytes());
+ std::uint32_t seq_num_{0};
+ int newest_buffer_ = 0;
+ std::condition_variable new_frame_cond_var_;
+ std::mutex new_frame_mtx_;
+ std::thread screen_server_thread_;
+};
+} // namespace
+
+ScreenConnector* ScreenConnector::Get() {
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (config->enable_ivserver()) {
+ return new VSoCScreenConnector();
+ } else {
+ return new SocketBasedScreenConnector(config);
+ }
+}
+
+} // namespace vnc
+} // namespace cvd
\ No newline at end of file
diff --git a/guest/hals/hwcomposer/legacy/geometry_utils.h b/host/frontend/vnc_server/screen_connector.h
similarity index 61%
copy from guest/hals/hwcomposer/legacy/geometry_utils.h
copy to host/frontend/vnc_server/screen_connector.h
index b6a037b..9c7b9e0 100644
--- a/guest/hals/hwcomposer/legacy/geometry_utils.h
+++ b/host/frontend/vnc_server/screen_connector.h
@@ -1,6 +1,5 @@
-#pragma once
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -15,10 +14,25 @@
* limitations under the License.
*/
-#include "hwcomposer_common.h"
+#pragma once
+
+#include <cstdint>
namespace cvd {
+namespace vnc {
-bool LayersOverlap(const vsoc_hwc_layer& layer1, const vsoc_hwc_layer& layer2);
+class ScreenConnector {
+ public:
+ static ScreenConnector* Get();
-} // namespace cvd
+ virtual ~ScreenConnector() = default;
+
+ virtual int WaitForNewFrameSince(std::uint32_t* seq_num) = 0;
+ virtual void* GetBuffer(int buffer_idx) = 0;
+
+ protected:
+ ScreenConnector() = default;
+};
+
+} // namespace vnc
+} // namespace cvd
\ No newline at end of file
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index c8b7f03..f7061ef 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -18,7 +18,6 @@
#include "host/frontend/vnc_server/vnc_utils.h"
#include "host/libs/config/cuttlefish_config.h"
-#include "common/vsoc/lib/screen_region_view.h"
using cvd::vnc::SimulatedHWComposer;
using vsoc::screen::ScreenRegionView;
@@ -74,12 +73,11 @@
auto screen_height = ActualScreenHeight();
Message raw_screen;
std::uint64_t stripe_seq_num = 1;
- auto screen_view = ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
while (!closed()) {
bb_->WaitForAtLeastOneClientConnection();
- int buffer_idx = screen_view->WaitForNewFrameSince(&previous_seq_num);
+ int buffer_idx = screen_connector_->WaitForNewFrameSince(&previous_seq_num);
const char* frame_start =
- static_cast<char*>(screen_view->GetBuffer(buffer_idx));
+ static_cast<char*>(screen_connector_->GetBuffer(buffer_idx));
raw_screen.assign(frame_start, frame_start + ScreenSizeInBytes());
for (int i = 0; i < kNumStripes; ++i) {
diff --git a/host/frontend/vnc_server/simulated_hw_composer.h b/host/frontend/vnc_server/simulated_hw_composer.h
index 5d6e3e2..802cc7f 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.h
+++ b/host/frontend/vnc_server/simulated_hw_composer.h
@@ -26,6 +26,7 @@
#include "common/libs/thread_safe_queue/thread_safe_queue.h"
#include "common/libs/threads/thread_annotations.h"
#include "host/frontend/vnc_server/blackboard.h"
+#include "host/frontend/vnc_server/screen_connector.h"
namespace cvd {
namespace vnc {
@@ -59,6 +60,7 @@
BlackBoard* bb_{};
ThreadSafeQueue<Stripe> stripes_;
std::thread stripe_maker_;
+ std::shared_ptr<ScreenConnector> screen_connector_{ScreenConnector::Get()};
};
} // namespace vnc
} // namespace cvd
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index b2f519c..e7e3757 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -22,11 +22,20 @@
#include <mutex>
#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");
+
+DEFINE_int32(keyboard_fd, -1,
+ "A fd for a socket where to accept keyboard connections");
+
namespace {
-void AddKeyMappings(std::map<uint32_t, uint32_t>* key_mapping) {
+void AddKeyMappings(std::map<uint32_t, uint16_t>* key_mapping) {
(*key_mapping)[cvd::xk::AltLeft] = KEY_LEFTALT;
(*key_mapping)[cvd::xk::ControlLeft] = KEY_LEFTCTRL;
(*key_mapping)[cvd::xk::ShiftLeft] = KEY_LEFTSHIFT;
@@ -214,30 +223,132 @@
(*key_mapping)[cvd::xk::Menu] = KEY_MENU;
(*key_mapping)[cvd::xk::VNCMenu] = KEY_MENU;
}
+
+void InitInputEvent(struct input_event* evt, uint16_t type, uint16_t code,
+ int32_t value) {
+ evt->type = type;
+ evt->code = code;
+ evt->value = value;
+}
+
} // namespace
-VirtualInputs::VirtualInputs()
- : 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";
+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";
+ }
}
- AddKeyMappings(&keymapping_);
-}
-void VirtualInputs::GenerateKeyPressEvent(int key_code, bool down) {
- if (keymapping_.count(key_code)) {
- input_events_region_view_->HandleKeyboardEvent(down, keymapping_[key_code]);
+ 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()
+ : client_connector_([this]() { ClientConnectorLoop(); }) {}
+
+ void GenerateKeyPressEvent(int key_code, bool down) override {
+ struct input_event events[2];
+ InitInputEvent(&events[0], EV_KEY, keymapping_[key_code], down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+ SendEvents(keyboard_socket_, events, sizeof(events));
+ }
+
+ void PressPowerButton(bool down) override {
+ struct input_event events[2];
+ InitInputEvent(&events[0], EV_KEY, KEY_POWER, down);
+ InitInputEvent(&events[1], EV_SYN, 0, 0);
+
+ SendEvents(keyboard_socket_, events, sizeof(events));
+ }
+
+ void HandlePointerEvent(bool touch_down, int x, int y) override {
+ // TODO(b/124121375): Use multitouch when available
+ struct input_event events[4];
+ InitInputEvent(&events[0], EV_ABS, ABS_X, x);
+ InitInputEvent(&events[1], EV_ABS, ABS_Y, y);
+ InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, touch_down);
+ InitInputEvent(&events[3], EV_SYN, 0, 0);
+
+ SendEvents(touch_socket_, events, sizeof(events));
+ }
+
+ private:
+ void SendEvents(cvd::SharedFD socket, void* event_buffer, int byte_count) {
+ std::lock_guard<std::mutex> lock(socket_mutex_);
+ if (!socket->IsOpen()) {
+ // This is unlikely as it would only happen between the start of the vnc
+ // server and the connection of the VMM to the socket.
+ // If it happens, just drop the events as the VM is not yet ready to
+ // handle it.
+ return;
+ }
+ auto ret = socket->Write(event_buffer, byte_count);
+ if (ret < 0) {
+ LOG(ERROR) << "Error sending input event: " << socket->StrError();
+ }
+ }
+
+ void ClientConnectorLoop() {
+ auto touch_server = cvd::SharedFD::Dup(FLAGS_touch_fd);
+ close(FLAGS_touch_fd);
+ FLAGS_touch_fd = -1;
+
+ auto keyboard_server = cvd::SharedFD::Dup(FLAGS_keyboard_fd);
+ close(FLAGS_keyboard_fd);
+ FLAGS_keyboard_fd = -1;
+
+ while (1) {
+ cvd::SharedFDSet read_set;
+ read_set.Set(touch_server);
+ read_set.Set(keyboard_server);
+ cvd::Select(&read_set, nullptr, nullptr, nullptr);
+ {
+ std::lock_guard<std::mutex> lock(socket_mutex_);
+ if (read_set.IsSet(touch_server)) {
+ touch_socket_ = cvd::SharedFD::Accept(*touch_server);
+ }
+ if (read_set.IsSet(keyboard_server)) {
+ keyboard_socket_ = cvd::SharedFD::Accept(*keyboard_server);
+ }
+ }
+ }
+ }
+ cvd::SharedFD touch_socket_;
+ cvd::SharedFD keyboard_socket_;
+ std::thread client_connector_;
+ std::mutex socket_mutex_;
+};
+
+VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); }
+
+VirtualInputs* VirtualInputs::Get() {
+ if (vsoc::CuttlefishConfig::Get()->enable_ivserver()) {
+ return new VSoCVirtualInputs();
} else {
- LOG(INFO) << "Unknown keycode" << key_code;
+ return new SocketVirtualInputs();
}
}
-
-void VirtualInputs::PressPowerButton(bool down) {
- input_events_region_view_->HandlePowerButtonEvent(down);
-}
-
-void VirtualInputs::HandlePointerEvent(bool touch_down, int x, int y) {
- input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
-}
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index c937a12..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -28,15 +28,18 @@
class VirtualInputs {
public:
+ static VirtualInputs* Get();
+
+ virtual ~VirtualInputs() = default;
+
+ virtual void GenerateKeyPressEvent(int code, bool down) = 0;
+ virtual void PressPowerButton(bool down) = 0;
+ virtual void HandlePointerEvent(bool touch_down, int x, int y) = 0;
+
+ protected:
VirtualInputs();
- void GenerateKeyPressEvent(int code, bool down);
- void PressPowerButton(bool down);
- void HandlePointerEvent(bool touch_down, int x, int y);
-
- private:
- vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
- std::map<uint32_t, uint32_t> keymapping_;
+ std::map<uint32_t, uint16_t> keymapping_;
};
} // namespace vnc
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index 25b007d..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -76,47 +76,6 @@
constexpr size_t kPointerEventLength = 5;
constexpr size_t kClientCutTextLength = 7; // more bytes follow
-void AppendInNetworkByteOrder(Message* msg, const std::uint8_t b) {
- msg->push_back(b);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::uint16_t s) {
- const std::uint16_t n = htons(s);
- auto p = reinterpret_cast<const std::uint8_t*>(&n);
- msg->insert(msg->end(), p, p + sizeof n);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::uint32_t w) {
- const std::uint32_t n = htonl(w);
- auto p = reinterpret_cast<const std::uint8_t*>(&n);
- msg->insert(msg->end(), p, p + sizeof n);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const int32_t w) {
- std::uint32_t u{};
- std::memcpy(&u, &w, sizeof u);
- AppendInNetworkByteOrder(msg, u);
-}
-
-void AppendInNetworkByteOrder(Message* msg, const std::string& str) {
- msg->insert(msg->end(), str.begin(), str.end());
-}
-
-void AppendToMessage(Message*) {}
-
-template <typename T, typename... Ts>
-void AppendToMessage(Message* msg, T v, Ts... vals) {
- AppendInNetworkByteOrder(msg, v);
- AppendToMessage(msg, vals...);
-}
-
-template <typename... Ts>
-Message CreateMessage(Ts... vals) {
- Message m;
- AppendToMessage(&m, vals...);
- return m;
-}
-
std::string HostName() {
auto config = vsoc::CuttlefishConfig::Get();
return !config || config->device_title().empty() ? std::string{"localhost"}
@@ -174,9 +133,9 @@
} // namespace vnc
} // namespace cvd
-VncClientConnection::VncClientConnection(ClientSocket client,
- VirtualInputs* virtual_inputs,
- BlackBoard* bb, bool aggressive)
+VncClientConnection::VncClientConnection(
+ ClientSocket client, std::shared_ptr<VirtualInputs> virtual_inputs,
+ BlackBoard* bb, bool aggressive)
: client_{std::move(client)}, virtual_inputs_{virtual_inputs}, bb_{bb} {
frame_buffer_request_handler_tid_ = std::thread(
&VncClientConnection::FrameBufferUpdateRequestHandler, this, aggressive);
@@ -225,7 +184,8 @@
void VncClientConnection::SetupProtocol() {
static constexpr char kRFBVersion[] = "RFB 003.008\n";
static constexpr auto kVersionLen = (sizeof kRFBVersion) - 1;
- client_.Send(reinterpret_cast<const std::uint8_t*>(kRFBVersion), kVersionLen);
+ client_.SendNoSignal(reinterpret_cast<const std::uint8_t*>(kRFBVersion),
+ kVersionLen);
auto client_protocol = client_.Recv(kVersionLen);
if (std::memcmp(&client_protocol[0], kRFBVersion,
std::min(kVersionLen, client_protocol.size())) != 0) {
@@ -239,7 +199,7 @@
static constexpr std::uint8_t kNoneSecurity = 0x1;
// The first '0x1' indicates the number of items that follow
static constexpr std::uint8_t kOnlyNoneSecurity[] = {0x01, kNoneSecurity};
- client_.Send(kOnlyNoneSecurity);
+ client_.SendNoSignal(kOnlyNoneSecurity);
auto client_security = client_.Recv(1);
if (client_.closed()) {
return;
@@ -249,7 +209,7 @@
<< static_cast<int>(client_security.front());
}
static constexpr std::uint8_t kZero[4] = {};
- client_.Send(kZero);
+ client_.SendNoSignal(kZero);
}
void VncClientConnection::GetClientInit() {
@@ -259,7 +219,7 @@
void VncClientConnection::SendServerInit() {
const std::string server_name = HostName();
std::lock_guard<std::mutex> guard(m_);
- auto server_init = CreateMessage(
+ auto server_init = cvd::CreateMessage(
static_cast<std::uint16_t>(ScreenWidth()),
static_cast<std::uint16_t>(ScreenHeight()), pixel_format_.bits_per_pixel,
pixel_format_.depth, pixel_format_.big_endian, pixel_format_.true_color,
@@ -268,22 +228,22 @@
pixel_format_.blue_shift, std::uint16_t{}, // padding
std::uint8_t{}, // padding
static_cast<std::uint32_t>(server_name.size()), server_name);
- client_.Send(server_init);
+ client_.SendNoSignal(server_init);
}
Message VncClientConnection::MakeFrameBufferUpdateHeader(
std::uint16_t num_stripes) {
- return CreateMessage(std::uint8_t{0}, // message-type
- std::uint8_t{}, // padding
- std::uint16_t{num_stripes});
+ return cvd::CreateMessage(std::uint8_t{0}, // message-type
+ std::uint8_t{}, // padding
+ std::uint16_t{num_stripes});
}
void VncClientConnection::AppendRawStripeHeader(Message* frame_buffer_update,
const Stripe& stripe) {
static constexpr int32_t kRawEncoding = 0;
- AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
- std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
- std::uint16_t{stripe.height}, kRawEncoding);
+ cvd::AppendToMessage(frame_buffer_update, std::uint16_t{stripe.x},
+ std::uint16_t{stripe.y}, std::uint16_t{stripe.width},
+ std::uint16_t{stripe.height}, kRawEncoding);
}
void VncClientConnection::AppendJpegSize(Message* frame_buffer_update,
@@ -293,22 +253,23 @@
constexpr size_t kJpegSizeThreeByteMax = 4194303;
if (jpeg_size <= kJpegSizeOneByteMax) {
- AppendToMessage(frame_buffer_update, static_cast<std::uint8_t>(jpeg_size));
+ cvd::AppendToMessage(frame_buffer_update,
+ static_cast<std::uint8_t>(jpeg_size));
} else if (jpeg_size <= kJpegSizeTwoByteMax) {
auto sz = static_cast<std::uint32_t>(jpeg_size);
- AppendToMessage(frame_buffer_update,
- static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
- static_cast<std::uint8_t>((sz >> 7) & 0xFF));
+ cvd::AppendToMessage(frame_buffer_update,
+ static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+ static_cast<std::uint8_t>((sz >> 7) & 0xFF));
} else {
if (jpeg_size > kJpegSizeThreeByteMax) {
LOG(FATAL) << "jpeg size is too big: " << jpeg_size << " must be under "
<< kJpegSizeThreeByteMax;
}
const auto sz = static_cast<std::uint32_t>(jpeg_size);
- AppendToMessage(frame_buffer_update,
- static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
- static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
- static_cast<std::uint8_t>((sz >> 14) & 0xFF));
+ cvd::AppendToMessage(frame_buffer_update,
+ static_cast<std::uint8_t>((sz & 0x7F) | 0x80),
+ static_cast<std::uint8_t>(((sz >> 7) & 0x7F) | 0x80),
+ static_cast<std::uint8_t>((sz >> 14) & 0xFF));
}
}
@@ -353,8 +314,8 @@
void VncClientConnection::AppendJpegStripeHeader(Message* frame_buffer_update,
const Stripe& stripe) {
static constexpr std::uint8_t kJpegEncoding = 0x90;
- AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
- stripe.height, kTightEncoding, kJpegEncoding);
+ cvd::AppendToMessage(frame_buffer_update, stripe.x, stripe.y, stripe.width,
+ stripe.height, kTightEncoding, kJpegEncoding);
AppendJpegSize(frame_buffer_update, stripe.jpeg_data.size());
}
@@ -401,7 +362,7 @@
? "portrait"
: "landscape")
<< " mode";
- client_.Send(MakeFrameBufferUpdate(stripes));
+ client_.SendNoSignal(MakeFrameBufferUpdate(stripes));
}
if (aggressive) {
bb_->FrameBufferUpdateRequestReceived(this);
@@ -411,13 +372,13 @@
void VncClientConnection::SendDesktopSizeUpdate() {
static constexpr int32_t kDesktopSizeEncoding = -223;
- client_.Send(CreateMessage(std::uint8_t{0}, // message-type,
- std::uint8_t{}, // padding
- std::uint16_t{1}, // one pseudo rectangle
- std::uint16_t{0}, std::uint16_t{0},
- static_cast<std::uint16_t>(ScreenWidth()),
- static_cast<std::uint16_t>(ScreenHeight()),
- kDesktopSizeEncoding));
+ client_.SendNoSignal(cvd::CreateMessage(
+ std::uint8_t{0}, // message-type,
+ std::uint8_t{}, // padding
+ std::uint16_t{1}, // one pseudo rectangle
+ std::uint16_t{0}, std::uint16_t{0},
+ static_cast<std::uint16_t>(ScreenWidth()),
+ static_cast<std::uint16_t>(ScreenHeight()), kDesktopSizeEncoding));
}
bool VncClientConnection::IsUrgent(
diff --git a/host/frontend/vnc_server/vnc_client_connection.h b/host/frontend/vnc_server/vnc_client_connection.h
index 80aaad1..73beae1 100644
--- a/host/frontend/vnc_server/vnc_client_connection.h
+++ b/host/frontend/vnc_server/vnc_client_connection.h
@@ -35,7 +35,8 @@
class VncClientConnection {
public:
- VncClientConnection(ClientSocket client, VirtualInputs* virtual_inputs,
+ VncClientConnection(ClientSocket client,
+ std::shared_ptr<VirtualInputs> virtual_inputs,
BlackBoard* bb, bool aggressive);
VncClientConnection(const VncClientConnection&) = delete;
VncClientConnection& operator=(const VncClientConnection&) = delete;
@@ -139,7 +140,7 @@
ClientSocket client_;
bool control_key_down_ = false;
bool meta_key_down_ = false;
- VirtualInputs* virtual_inputs_{};
+ std::shared_ptr<VirtualInputs> virtual_inputs_{};
FrameBufferUpdateRequest previous_update_request_{};
BlackBoard* bb_;
diff --git a/host/frontend/vnc_server/vnc_server.cpp b/host/frontend/vnc_server/vnc_server.cpp
index 03f5dbe..3aba467 100644
--- a/host/frontend/vnc_server/vnc_server.cpp
+++ b/host/frontend/vnc_server/vnc_server.cpp
@@ -28,7 +28,10 @@
using cvd::vnc::VncServer;
VncServer::VncServer(int port, bool aggressive)
- : server_(port), frame_buffer_watcher_{&bb_}, aggressive_{aggressive} {}
+ : server_(port),
+ virtual_inputs_(VirtualInputs::Get()),
+ frame_buffer_watcher_{&bb_},
+ aggressive_{aggressive} {}
void VncServer::MainLoop() {
while (true) {
@@ -50,7 +53,7 @@
// data members. In the current setup, if the VncServer is destroyed with
// clients still running, the clients will all be left with dangling
// pointers.
- VncClientConnection client(std::move(sock), &virtual_inputs_, &bb_,
+ VncClientConnection client(std::move(sock), virtual_inputs_, &bb_,
aggressive_);
client.StartSession();
}
diff --git a/host/frontend/vnc_server/vnc_server.h b/host/frontend/vnc_server/vnc_server.h
index d88835c..66e17e0 100644
--- a/host/frontend/vnc_server/vnc_server.h
+++ b/host/frontend/vnc_server/vnc_server.h
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+#include <memory>
#include <string>
#include <thread>
#include <utility>
@@ -46,7 +47,7 @@
void StartClientThread(ClientSocket sock);
ServerSocket server_;
- VirtualInputs virtual_inputs_;
+ std::shared_ptr<VirtualInputs> virtual_inputs_;
BlackBoard bb_;
FrameBufferWatcher frame_buffer_watcher_;
bool aggressive_{};
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index 3eac9f3..194e828 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -21,6 +21,7 @@
#include <utility>
#include <vector>
+#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"
@@ -68,16 +69,12 @@
// The width of the screen regardless of orientation. Does not change.
inline int ActualScreenWidth() {
- return vsoc::screen::ScreenRegionView::GetInstance(
- vsoc::GetDomain().c_str())
- ->x_res();
+ return AlignToPowerOf2(vsoc::CuttlefishConfig::Get()->x_res(), 4);
}
// The height of the screen regardless of orientation. Does not change.
inline int ActualScreenHeight() {
- return vsoc::screen::ScreenRegionView::GetInstance(
- vsoc::GetDomain().c_str())
- ->y_res();
+ return vsoc::CuttlefishConfig::Get()->y_res();
}
inline int ScreenSizeInBytes() {
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
index 026622e..1336951 100644
--- a/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
@@ -36,27 +36,20 @@
return ss.str();
}
-std::string MakeIPAndPort(int port) {
- static constexpr char kLocalHostPrefix[] = "127.0.0.1:";
- return kLocalHostPrefix + std::to_string(port);
-}
-
std::string MakeShellUptimeMessage() {
return MakeMessage("shell,raw:cut -d. -f1 /proc/uptime");
}
-std::string MakeTransportMessage(int port) {
- return MakeMessage("host:transport:" + MakeIPAndPort(port));
+std::string MakeTransportMessage(const std::string& address) {
+ return MakeMessage("host:transport:" + address);
}
-std::string MakeConnectMessage(int port) {
- static constexpr char kConnectPrefix[] = "host:connect:";
- return MakeMessage(kConnectPrefix + MakeIPAndPort(port));
+std::string MakeConnectMessage(const std::string& address) {
+ return MakeMessage("host:connect:" + address);
}
-std::string MakeDisconnectMessage(int port) {
- static constexpr char kDisonnectPrefix[] = "host:disconnect:";
- return MakeMessage(kDisonnectPrefix + MakeIPAndPort(port));
+std::string MakeDisconnectMessage(const std::string& address) {
+ return MakeMessage("host:connect:" + address);
}
// returns true if successfully sent the whole message
@@ -116,10 +109,12 @@
return AdbSendMessage(sock, message);
}
-bool AdbConnect(int port) { return AdbSendMessage(MakeConnectMessage(port)); }
+bool AdbConnect(const std::string& address) {
+ return AdbSendMessage(MakeConnectMessage(address));
+}
-bool AdbDisconnect(int port) {
- return AdbSendMessage(MakeDisconnectMessage(port));
+bool AdbDisconnect(const std::string& address) {
+ return AdbSendMessage(MakeDisconnectMessage(address));
}
bool IsInteger(const std::string& str) {
@@ -173,22 +168,22 @@
// seconds is much larger than seems necessary so we should be more than okay.
static constexpr int kAdbCommandGapTime = 5;
-void EstablishConnection(int port) {
- LOG(INFO) << "Attempting to connect to device on port " << port;
- while (!AdbConnect(port)) {
+void EstablishConnection(const std::string& address) {
+ LOG(INFO) << "Attempting to connect to device with address " << address;
+ while (!AdbConnect(address)) {
sleep(kAdbCommandGapTime);
}
- LOG(INFO) << "adb connect message for " << port << " successfully sent";
+ LOG(INFO) << "adb connect message for " << address << " successfully sent";
sleep(kAdbCommandGapTime);
}
-void WaitForAdbDisconnection(int port) {
+void WaitForAdbDisconnection(const std::string& address) {
// adb daemon doesn't seem to handle quick, successive messages well. The
// sleeps stabilize the communication.
- LOG(INFO) << "Watching for disconnect on port " << port;
+ LOG(INFO) << "Watching for disconnect on " << address;
while (true) {
auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
- if (!AdbSendMessage(sock, MakeTransportMessage(port))) {
+ if (!AdbSendMessage(sock, MakeTransportMessage(address))) {
LOG(INFO) << "transport message failed, response body: "
<< RecvAdbResponse(sock);
break;
@@ -203,19 +198,19 @@
LOG(INFO) << "couldn't read uptime result";
break;
}
- LOG(DEBUG) << "device on port " << port << " uptime " << uptime;
+ LOG(DEBUG) << "device on " << address << " uptime " << uptime;
sleep(kAdbCommandGapTime);
}
LOG(INFO) << "Sending adb disconnect";
- AdbDisconnect(port);
+ AdbDisconnect(address);
sleep(kAdbCommandGapTime);
}
} // namespace
-[[noreturn]] void cvd::EstablishAndMaintainConnection(int port) {
+[[noreturn]] void cvd::EstablishAndMaintainConnection(std::string address) {
while (true) {
- EstablishConnection(port);
- WaitForAdbDisconnection(port);
+ EstablishConnection(address);
+ WaitForAdbDisconnection(address);
}
}
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.h b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
index d3378ce..ca5584f 100644
--- a/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
@@ -17,6 +17,6 @@
namespace cvd {
-[[noreturn]] void EstablishAndMaintainConnection(int port);
+[[noreturn]] void EstablishAndMaintainConnection(std::string address);
} // namespace cvd
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 1a838b8..af690ed 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -31,6 +31,8 @@
#include "common/libs/utils/environment.h"
#include "common/libs/utils/files.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
namespace {
@@ -67,6 +69,7 @@
const char* kSerialNumber = "serial_number";
const char* kInstanceDir = "instance_dir";
const char* kVmManager = "vm_manager";
+const char* kHardwareName = "hardware_name";
const char* kDeviceTitle = "device_title";
const char* kCpus = "cpus";
@@ -74,9 +77,13 @@
const char* kDpi = "dpi";
const char* kXRes = "x_res";
const char* kYRes = "y_res";
+const char* kNumScreenBuffers = "num_screen_buffers";
const char* kRefreshRateHz = "refresh_rate_hz";
const char* kKernelImagePath = "kernel_image_path";
+const char* kUseUnpackedKernel = "use_unpacked_kernel";
+const char* kDecompressedKernelImagePath = "decompressed_kernel_image_path";
+const char* kDecompressKernel = "decompress_kernel";
const char* kGdbFlag = "gdb_flag";
const char* kKernelCmdline = "kernel_cmdline";
const char* kRamdiskImagePath = "ramdisk_image_path";
@@ -85,6 +92,8 @@
const char* kCacheImagePath = "cache_image_path";
const char* kDataImagePath = "data_image_path";
const char* kVendorImagePath = "vendor_image_path";
+const char* kMetadataImagePath = "metadata_image_path";
+const char* kProductImagePath = "product_image_path";
const char* kUsbV1SocketName = "usb_v1_socket_name";
const char* kVhciPort = "vhci_port";
const char* kUsbIpSocketName = "usb_ip_socket_name";
@@ -95,6 +104,7 @@
const char* kLauncherLogPath = "launcher_log_path";
const char* kLauncherMonitorPath = "launcher_monitor_socket";
const char* kDtbPath = "dtb_path";
+const char* kGsiFstabPath = "gsi.fstab_path";
const char* kMempath = "mempath";
const char* kIvshmemQemuSocketPath = "ivshmem_qemu_socket_path";
@@ -103,24 +113,51 @@
const char* kMobileBridgeName = "mobile_bridge_name";
const char* kMobileTapName = "mobile_tap_name";
-const char* kWifiBridgeName = "wifi_bridge_name";
const char* kWifiTapName = "wifi_tap_name";
const char* kWifiGuestMacAddr = "wifi_guest_mac_addr";
const char* kWifiHostMacAddr = "wifi_host_mac_addr";
const char* kEntropySource = "entropy_source";
+const char* kVsockGuestCid = "vsock_guest_cid";
const char* kUuid = "uuid";
-const char* kDisableDacSecurity = "disable_dac_security";
-const char* kDisableAppArmorSecurity = "disable_app_armor_security";
const char* kCuttlefishEnvPath = "cuttlefish_env_path";
const char* kAdbMode = "adb_mode";
const char* kAdbIPAndPort = "adb_ip_and_port";
const char* kSetupWizardMode = "setupwizard_mode";
-const char* kLogXml = "log_xml";
-const char* kHypervisorUri = "hypervisor_uri";
const char* kQemuBinary = "qemu_binary";
+const char* kCrosvmBinary = "crosvm_binary";
+const char* kIvServerBinary = "ivserver_binary";
+const char* kKernelLogMonitorBinary = "kernel_log_monitor_binary";
+
+const char* kEnableVncServer = "enable_vnc_server";
+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";
+const char* kVirtualUsbManagerBinary = "virtual_usb_manager_binary";
+const char* kSocketForwardProxyBinary = "socket_forward_proxy_binary";
+const char* kSocketVsockProxyBinary = "socket_vsock_proxy_binary";
+
+const char* kRunAsDaemon = "run_as_daemon";
+const char* kRunE2eTest = "run_e2e_test";
+const char* kE2eTestBinary = "e2e_test_binary";
+
+const char* kDataPolicy = "data_policy";
+const char* kBlankDataImageMb = "blank_data_image_mb";
+const char* kBlankDataImageFmt = "blank_data_image_fmt";
+
+const char* kLogcatMode = "logcat_mode";
+const char* kLogcatVsockPort = "logcat_vsock_port";
+const char* kFramesVsockPort = "frames_vsock_port";
+const char* kLogcatReceiverBinary = "logcat_receiver_binary";
} // namespace
namespace vsoc {
@@ -139,6 +176,13 @@
(*dictionary_)[kVmManager] = name;
}
+std::string CuttlefishConfig::hardware_name() const {
+ return (*dictionary_)[kHardwareName].asString();
+}
+void CuttlefishConfig::set_hardware_name(const std::string& name) {
+ (*dictionary_)[kHardwareName] = name;
+}
+
std::string CuttlefishConfig::serial_number() const {
return (*dictionary_)[kSerialNumber].asString();
}
@@ -165,6 +209,13 @@
int CuttlefishConfig::y_res() const { return (*dictionary_)[kYRes].asInt(); }
void CuttlefishConfig::set_y_res(int y_res) { (*dictionary_)[kYRes] = y_res; }
+int CuttlefishConfig::num_screen_buffers() const {
+ return (*dictionary_)[kNumScreenBuffers].asInt();
+}
+void CuttlefishConfig::set_num_screen_buffers(int num_screen_buffers) {
+ (*dictionary_)[kNumScreenBuffers] = num_screen_buffers;
+}
+
int CuttlefishConfig::refresh_rate_hz() const {
return (*dictionary_)[kRefreshRateHz].asInt();
}
@@ -188,6 +239,29 @@
SetPath(kKernelImagePath, kernel_image_path);
}
+bool CuttlefishConfig::use_unpacked_kernel() const {
+ return (*dictionary_)[kUseUnpackedKernel].asBool();
+}
+
+void CuttlefishConfig::set_use_unpacked_kernel(bool use_unpacked_kernel) {
+ (*dictionary_)[kUseUnpackedKernel] = use_unpacked_kernel;
+}
+
+bool CuttlefishConfig::decompress_kernel() const {
+ return (*dictionary_)[kDecompressKernel].asBool();
+}
+void CuttlefishConfig::set_decompress_kernel(bool decompress_kernel) {
+ (*dictionary_)[kDecompressKernel] = decompress_kernel;
+}
+
+std::string CuttlefishConfig::decompressed_kernel_image_path() const {
+ return (*dictionary_)[kDecompressedKernelImagePath].asString();
+}
+void CuttlefishConfig::set_decompressed_kernel_image_path(
+ const std::string& path) {
+ SetPath(kDecompressedKernelImagePath, path);
+}
+
std::string CuttlefishConfig::gdb_flag() const {
return (*dictionary_)[kGdbFlag].asString();
}
@@ -207,7 +281,7 @@
void CuttlefishConfig::set_kernel_cmdline(
const std::set<std::string>& kernel_cmdline) {
Json::Value args_json_obj(Json::arrayValue);
- for (auto arg : kernel_cmdline) {
+ for (const auto& arg : kernel_cmdline) {
args_json_obj.append(arg);
}
(*dictionary_)[kKernelCmdline] = args_json_obj;
@@ -215,7 +289,7 @@
void CuttlefishConfig::add_kernel_cmdline(
const std::set<std::string>& extra_args) {
std::set<std::string> cmdline = kernel_cmdline();
- for (auto arg : extra_args) {
+ for (const auto& arg : extra_args) {
if (cmdline.count(arg)) {
LOG(ERROR) << "Kernel argument " << arg << " is duplicated";
}
@@ -278,6 +352,22 @@
SetPath(kVendorImagePath, vendor_image_path);
}
+std::string CuttlefishConfig::metadata_image_path() const {
+ return (*dictionary_)[kMetadataImagePath].asString();
+}
+void CuttlefishConfig::set_metadata_image_path(
+ const std::string& metadata_image_path) {
+ SetPath(kMetadataImagePath, metadata_image_path);
+}
+
+std::string CuttlefishConfig::product_image_path() const {
+ return (*dictionary_)[kProductImagePath].asString();
+}
+void CuttlefishConfig::set_product_image_path(
+ const std::string& product_image_path) {
+ SetPath(kProductImagePath, product_image_path);
+}
+
std::string CuttlefishConfig::dtb_path() const {
return (*dictionary_)[kDtbPath].asString();
}
@@ -285,6 +375,13 @@
SetPath(kDtbPath, dtb_path);
}
+std::string CuttlefishConfig::gsi_fstab_path() const {
+ return (*dictionary_)[kGsiFstabPath].asString();
+}
+void CuttlefishConfig::set_gsi_fstab_path(const std::string& path){
+ SetPath(kGsiFstabPath, path);
+}
+
std::string CuttlefishConfig::mempath() const {
return (*dictionary_)[kMempath].asString();
}
@@ -392,14 +489,6 @@
(*dictionary_)[kMobileBridgeName] = mobile_bridge_name;
}
-std::string CuttlefishConfig::wifi_bridge_name() const {
- return (*dictionary_)[kWifiBridgeName].asString();
-}
-void CuttlefishConfig::set_wifi_bridge_name(
- const std::string& wifi_bridge_name) {
- (*dictionary_)[kWifiBridgeName] = wifi_bridge_name;
-}
-
std::string CuttlefishConfig::wifi_guest_mac_addr() const {
return (*dictionary_)[kWifiGuestMacAddr].asString();
}
@@ -437,6 +526,14 @@
(*dictionary_)[kEntropySource] = entropy_source;
}
+int CuttlefishConfig::vsock_guest_cid() const {
+ return (*dictionary_)[kVsockGuestCid].asInt();
+}
+
+void CuttlefishConfig::set_vsock_guest_cid(int vsock_guest_cid) {
+ (*dictionary_)[kVsockGuestCid] = vsock_guest_cid;
+}
+
std::string CuttlefishConfig::uuid() const {
return (*dictionary_)[kUuid].asString();
}
@@ -444,13 +541,6 @@
(*dictionary_)[kUuid] = uuid;
}
-bool CuttlefishConfig::disable_dac_security() const {
- return (*dictionary_)[kDisableDacSecurity].asBool();
-}
-void CuttlefishConfig::set_disable_dac_security(bool disable_dac_security) {
- (*dictionary_)[kDisableDacSecurity] = disable_dac_security;
-}
-
void CuttlefishConfig::set_cuttlefish_env_path(const std::string& path) {
SetPath(kCuttlefishEnvPath, path);
}
@@ -458,20 +548,20 @@
return (*dictionary_)[kCuttlefishEnvPath].asString();
}
-bool CuttlefishConfig::disable_app_armor_security() const {
- return (*dictionary_)[kDisableAppArmorSecurity].asBool();
-}
-void CuttlefishConfig::set_disable_app_armor_security(
- bool disable_app_armor_security) {
- (*dictionary_)[kDisableAppArmorSecurity] = disable_app_armor_security;
+std::set<std::string> CuttlefishConfig::adb_mode() const {
+ std::set<std::string> args_set;
+ for (auto& mode : (*dictionary_)[kAdbMode]) {
+ args_set.insert(mode.asString());
+ }
+ return args_set;
}
-std::string CuttlefishConfig::adb_mode() const {
- return (*dictionary_)[kAdbMode].asString();
-}
-
-void CuttlefishConfig::set_adb_mode(const std::string& mode) {
- (*dictionary_)[kAdbMode] = mode;
+void CuttlefishConfig::set_adb_mode(const std::set<std::string>& mode) {
+ Json::Value mode_json_obj(Json::arrayValue);
+ for (const auto& arg : mode) {
+ mode_json_obj.append(arg);
+ }
+ (*dictionary_)[kAdbMode] = mode_json_obj;
}
std::string CuttlefishConfig::adb_ip_and_port() const {
@@ -483,9 +573,14 @@
}
std::string CuttlefishConfig::adb_device_name() const {
- if (adb_mode().find("tunnel") != std::string::npos) {
+ // TODO(schuffelen): Deal with duplication between here and launch.cc
+ bool tunnelMode = adb_mode().count("tunnel") > 0;
+ bool vsockTunnel = adb_mode().count("vsock_tunnel") > 0;
+ bool vsockHalfProxy = adb_mode().count("vsock_half_proxy") > 0;
+ bool nativeVsock = adb_mode().count("native_vsock") > 0;
+ if (tunnelMode || vsockTunnel || vsockHalfProxy || nativeVsock) {
return adb_ip_and_port();
- } else if (adb_mode().find("usb") != std::string::npos) {
+ } else if (adb_mode().count("usb") > 0) {
return serial_number();
}
LOG(ERROR) << "no adb_mode found, returning bad device name";
@@ -508,22 +603,6 @@
(*dictionary_)[kSetupWizardMode] = mode;
}
-bool CuttlefishConfig::log_xml() const {
- return (*dictionary_)[kLogXml].asBool();
-}
-
-void CuttlefishConfig::set_log_xml(bool log_xml) {
- (*dictionary_)[kLogXml] = log_xml;
-}
-
-std::string CuttlefishConfig::hypervisor_uri() const {
- return (*dictionary_)[kHypervisorUri].asString();
-}
-
-void CuttlefishConfig::set_hypervisor_uri(const std::string& hypervisor_uri) {
- (*dictionary_)[kHypervisorUri] = hypervisor_uri;
-}
-
std::string CuttlefishConfig::qemu_binary() const {
return (*dictionary_)[kQemuBinary].asString();
}
@@ -532,6 +611,226 @@
(*dictionary_)[kQemuBinary] = qemu_binary;
}
+std::string CuttlefishConfig::crosvm_binary() const {
+ return (*dictionary_)[kCrosvmBinary].asString();
+}
+
+void CuttlefishConfig::set_crosvm_binary(const std::string& crosvm_binary) {
+ (*dictionary_)[kCrosvmBinary] = crosvm_binary;
+}
+
+std::string CuttlefishConfig::ivserver_binary() const {
+ return (*dictionary_)[kIvServerBinary].asString();
+}
+
+void CuttlefishConfig::set_ivserver_binary(const std::string& ivserver_binary) {
+ (*dictionary_)[kIvServerBinary] = ivserver_binary;
+}
+
+std::string CuttlefishConfig::kernel_log_monitor_binary() const {
+ return (*dictionary_)[kKernelLogMonitorBinary].asString();
+}
+
+void CuttlefishConfig::set_kernel_log_monitor_binary(
+ const std::string& kernel_log_monitor_binary) {
+ (*dictionary_)[kKernelLogMonitorBinary] = kernel_log_monitor_binary;
+}
+
+bool CuttlefishConfig::enable_vnc_server() const {
+ return (*dictionary_)[kEnableVncServer].asBool();
+}
+
+void CuttlefishConfig::set_enable_vnc_server(bool enable_vnc_server) {
+ (*dictionary_)[kEnableVncServer] = enable_vnc_server;
+}
+
+std::string CuttlefishConfig::vnc_server_binary() const {
+ return (*dictionary_)[kVncServerBinary].asString();
+}
+
+void CuttlefishConfig::set_vnc_server_binary(
+ const std::string& vnc_server_binary) {
+ (*dictionary_)[kVncServerBinary] = vnc_server_binary;
+}
+
+int CuttlefishConfig::vnc_server_port() const {
+ return (*dictionary_)[kVncServerPort].asInt();
+}
+
+void CuttlefishConfig::set_vnc_server_port(int vnc_server_port) {
+ (*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();
+}
+
+void CuttlefishConfig::set_restart_subprocesses(bool restart_subprocesses) {
+ (*dictionary_)[kRestartSubprocesses] = restart_subprocesses;
+}
+
+bool CuttlefishConfig::run_adb_connector() const {
+ return (*dictionary_)[kRunAdbConnector].asBool();
+}
+
+void CuttlefishConfig::set_run_adb_connector(bool run_adb_connector) {
+ (*dictionary_)[kRunAdbConnector] = run_adb_connector;
+}
+
+std::string CuttlefishConfig::adb_connector_binary() const {
+ return (*dictionary_)[kAdbConnectorBinary].asString();
+}
+
+void CuttlefishConfig::set_adb_connector_binary(
+ const std::string& adb_connector_binary) {
+ (*dictionary_)[kAdbConnectorBinary] = adb_connector_binary;
+}
+
+std::string CuttlefishConfig::virtual_usb_manager_binary() const {
+ return (*dictionary_)[kVirtualUsbManagerBinary].asString();
+}
+
+void CuttlefishConfig::set_virtual_usb_manager_binary(
+ const std::string& virtual_usb_manager_binary) {
+ (*dictionary_)[kVirtualUsbManagerBinary] = virtual_usb_manager_binary;
+}
+
+std::string CuttlefishConfig::socket_forward_proxy_binary() const {
+ return (*dictionary_)[kSocketForwardProxyBinary].asString();
+}
+
+void CuttlefishConfig::set_socket_forward_proxy_binary(
+ const std::string& socket_forward_proxy_binary) {
+ (*dictionary_)[kSocketForwardProxyBinary] = socket_forward_proxy_binary;
+}
+
+std::string CuttlefishConfig::socket_vsock_proxy_binary() const {
+ return (*dictionary_)[kSocketVsockProxyBinary].asString();
+}
+
+void CuttlefishConfig::set_socket_vsock_proxy_binary(
+ const std::string& socket_vsock_proxy_binary) {
+ (*dictionary_)[kSocketVsockProxyBinary] = socket_vsock_proxy_binary;
+}
+
+bool CuttlefishConfig::run_as_daemon() const {
+ return (*dictionary_)[kRunAsDaemon].asBool();
+}
+
+void CuttlefishConfig::set_run_as_daemon(bool run_as_daemon) {
+ (*dictionary_)[kRunAsDaemon] = run_as_daemon;
+}
+
+bool CuttlefishConfig::run_e2e_test() const {
+ return (*dictionary_)[kRunE2eTest].asBool();
+}
+
+void CuttlefishConfig::set_run_e2e_test(bool run_e2e_test) {
+ (*dictionary_)[kRunE2eTest] = run_e2e_test;
+}
+
+std::string CuttlefishConfig::e2e_test_binary() const {
+ return (*dictionary_)[kE2eTestBinary].asString();
+}
+
+void CuttlefishConfig::set_e2e_test_binary(const std::string& e2e_test_binary) {
+ (*dictionary_)[kE2eTestBinary] = e2e_test_binary;
+}
+
+std::string CuttlefishConfig::data_policy() const {
+ return (*dictionary_)[kDataPolicy].asString();
+}
+
+void CuttlefishConfig::set_data_policy(const std::string& data_policy) {
+ (*dictionary_)[kDataPolicy] = data_policy;
+}
+
+int CuttlefishConfig::blank_data_image_mb() const {
+ return (*dictionary_)[kBlankDataImageMb].asInt();
+}
+
+void CuttlefishConfig::set_blank_data_image_mb(int blank_data_image_mb) {
+ (*dictionary_)[kBlankDataImageMb] = blank_data_image_mb;
+}
+
+std::string CuttlefishConfig::blank_data_image_fmt() const {
+ return (*dictionary_)[kBlankDataImageFmt].asString();
+}
+
+void CuttlefishConfig::set_blank_data_image_fmt(const std::string& blank_data_image_fmt) {
+ (*dictionary_)[kBlankDataImageFmt] = blank_data_image_fmt;
+}
+
+
+void CuttlefishConfig::set_logcat_mode(const std::string& mode) {
+ (*dictionary_)[kLogcatMode] = mode;
+}
+
+std::string CuttlefishConfig::logcat_mode() const {
+ return (*dictionary_)[kLogcatMode].asString();
+}
+
+void CuttlefishConfig::set_logcat_vsock_port(int port) {
+ (*dictionary_)[kLogcatVsockPort] = port;
+}
+
+int CuttlefishConfig::logcat_vsock_port() const {
+ return (*dictionary_)[kLogcatVsockPort].asInt();
+}
+
+void CuttlefishConfig::set_frames_vsock_port(int port) {
+ (*dictionary_)[kFramesVsockPort] = port;
+}
+
+int CuttlefishConfig::frames_vsock_port() const {
+ return (*dictionary_)[kFramesVsockPort].asInt();
+}
+
+void CuttlefishConfig::set_logcat_receiver_binary(const std::string& binary) {
+ SetPath(kLogcatReceiverBinary, binary);
+}
+
+std::string CuttlefishConfig::logcat_receiver_binary() const {
+ return (*dictionary_)[kLogcatReceiverBinary].asString();
+}
+
+bool CuttlefishConfig::enable_ivserver() const {
+ return hardware_name() == "cutf_ivsh";
+}
+
+std::string CuttlefishConfig::touch_socket_path() const {
+ return PerInstancePath("touch.sock");
+}
+
+std::string CuttlefishConfig::keyboard_socket_path() const {
+ return PerInstancePath("keyboard.sock");
+}
+
// Creates the (initially empty) config object and populates it with values from
// the config file if the CUTTLEFISH_CONFIG_FILE env variable is present.
// Returns nullptr if there was an error loading from file
@@ -614,12 +913,7 @@
std::string GetDefaultPerInstanceDir() {
std::ostringstream stream;
- if (HostSupportsQemuCli()) {
- stream << std::getenv("HOME") << "/cuttlefish_runtime";
- } else {
- stream << "/var/run/libvirt-" << kDefaultUuidPrefix << std::setfill('0')
- << std::setw(2) << GetInstance();
- }
+ stream << std::getenv("HOME") << "/cuttlefish_runtime";
return stream.str();
}
@@ -627,6 +921,11 @@
return GetPerInstanceDefault("/var/run/shm/cvd-");
}
+int GetDefaultPerInstanceVsockCid() {
+ constexpr int kFirstGuestCid = 3;
+ return vsoc::HostSupportsVsock() ? GetPerInstanceDefault(kFirstGuestCid) : 0;
+}
+
std::string DefaultHostArtifactsPath(const std::string& file_name) {
return (cvd::StringFromEnv("ANDROID_HOST_OUT",
cvd::StringFromEnv("HOME", ".")) +
@@ -647,4 +946,11 @@
"/usr/lib/cuttlefish-common/bin/capability_query.py qemu_cli") == 0;
return supported;
}
+
+bool HostSupportsVsock() {
+ static bool supported =
+ std::system(
+ "/usr/lib/cuttlefish-common/bin/capability_query.py vsock") == 0;
+ return supported;
+}
} // namespace vsoc
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index fd1a26e..668badc 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -57,6 +57,9 @@
std::string vm_manager() const;
void set_vm_manager(const std::string& name);
+ std::string hardware_name() const;
+ void set_hardware_name(const std::string& name);
+
std::string serial_number() const;
void set_serial_number(const std::string& serial_number);
@@ -75,12 +78,36 @@
int y_res() const;
void set_y_res(int y_res);
+ int num_screen_buffers() const;
+ void set_num_screen_buffers(int num_screen_buffers);
+
int refresh_rate_hz() const;
void set_refresh_rate_hz(int refresh_rate_hz);
+ // Returns kernel image extracted from the boot image or the user-provided one
+ // if given by command line to the launcher. This function should not be used
+ // to get the kernel image the vmm should boot, GetKernelImageToUse() should
+ // be used instead.
std::string kernel_image_path() const;
void set_kernel_image_path(const std::string& kernel_image_path);
+ bool decompress_kernel() const;
+ void set_decompress_kernel(bool decompress_kernel);
+
+ // Returns the path to the kernel image that should be given to the vm manager
+ // to boot, takes into account whether the original image was decompressed or
+ // not.
+ std::string GetKernelImageToUse() const {
+ return decompress_kernel() ? decompressed_kernel_image_path()
+ : kernel_image_path();
+ }
+
+ std::string decompressed_kernel_image_path() const;
+ void set_decompressed_kernel_image_path(const std::string& path);
+
+ bool use_unpacked_kernel() const;
+ void set_use_unpacked_kernel(bool use_unpacked_kernel);
+
std::set<std::string> kernel_cmdline() const;
void set_kernel_cmdline(const std::set<std::string>& kernel_cmdline);
void add_kernel_cmdline(const std::string& arg);
@@ -105,9 +132,18 @@
std::string vendor_image_path() const;
void set_vendor_image_path(const std::string& vendor_image_path);
+ std::string metadata_image_path() const;
+ void set_metadata_image_path(const std::string& metadata_image_path);
+
+ std::string product_image_path() const;
+ void set_product_image_path(const std::string& product_image_path);
+
std::string dtb_path() const;
void set_dtb_path(const std::string& dtb_path);
+ std::string gsi_fstab_path() const;
+ void set_gsi_fstab_path(const std::string& path);
+
std::string mempath() const;
void set_mempath(const std::string& mempath);
@@ -145,6 +181,9 @@
std::string logcat_path() const;
void set_logcat_path(const std::string& logcat_path);
+ std::string logcat_receiver_binary() const;
+ void set_logcat_receiver_binary(const std::string& binary);
+
std::string launcher_log_path() const;
void set_launcher_log_path(const std::string& launcher_log_path);
@@ -158,9 +197,6 @@
std::string mobile_tap_name() const;
void set_mobile_tap_name(const std::string& mobile_tap_name);
- std::string wifi_bridge_name() const;
- void set_wifi_bridge_name(const std::string& wifi_bridge_name);
-
std::string wifi_tap_name() const;
void set_wifi_tap_name(const std::string& wifi_tap_name);
@@ -173,20 +209,17 @@
std::string entropy_source() const;
void set_entropy_source(const std::string& entropy_source);
+ void set_vsock_guest_cid(int vsock_guest_cid);
+ int vsock_guest_cid() const;
+
std::string uuid() const;
void set_uuid(const std::string& uuid);
- bool disable_dac_security() const;
- void set_disable_dac_security(bool disable_dac_security);
-
- bool disable_app_armor_security() const;
- void set_disable_app_armor_security(bool disable_app_armor_security);
-
void set_cuttlefish_env_path(const std::string& path);
std::string cuttlefish_env_path() const;
- void set_adb_mode(const std::string& mode);
- std::string adb_mode() const;
+ void set_adb_mode(const std::set<std::string>& modes);
+ std::set<std::string> adb_mode() const;
void set_adb_ip_and_port(const std::string& ip_port);
std::string adb_ip_and_port() const;
@@ -199,15 +232,87 @@
void set_setupwizard_mode(const std::string& title);
std::string setupwizard_mode() const;
- void set_log_xml(bool log_xml);
- bool log_xml() const;
-
- void set_hypervisor_uri(const std::string& hypervisor_uri);
- std::string hypervisor_uri() const;
-
void set_qemu_binary(const std::string& qemu_binary);
std::string qemu_binary() const;
+ void set_crosvm_binary(const std::string& crosvm_binary);
+ std::string crosvm_binary() const;
+
+ void set_ivserver_binary(const std::string& ivserver_binary);
+ std::string ivserver_binary() const;
+
+ void set_kernel_log_monitor_binary(
+ const std::string& kernel_log_monitor_binary);
+ std::string kernel_log_monitor_binary() const;
+
+ void set_enable_vnc_server(bool enable_vnc_server);
+ bool enable_vnc_server() const;
+
+ void set_vnc_server_port(int vnc_server_port);
+ int vnc_server_port() const;
+
+ 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;
+
+ void set_run_adb_connector(bool run_adb_connector);
+ bool run_adb_connector() const;
+
+ void set_adb_connector_binary(const std::string& adb_connector_binary);
+ std::string adb_connector_binary() const;
+
+ void set_virtual_usb_manager_binary(const std::string& binary);
+ std::string virtual_usb_manager_binary() const;
+
+ void set_socket_forward_proxy_binary(const std::string& binary);
+ std::string socket_forward_proxy_binary() const;
+
+ void set_socket_vsock_proxy_binary(const std::string& binary);
+ std::string socket_vsock_proxy_binary() const;
+
+ void set_run_as_daemon(bool run_as_daemon);
+ bool run_as_daemon() const;
+
+ void set_run_e2e_test(bool run_e2e_test);
+ bool run_e2e_test() const;
+
+ void set_e2e_test_binary(const std::string& e2e_test_binary);
+ std::string e2e_test_binary() const;
+
+ void set_data_policy(const std::string& data_policy);
+ std::string data_policy() const;
+
+ void set_blank_data_image_mb(int blank_data_image_mb);
+ int blank_data_image_mb() const;
+
+ void set_blank_data_image_fmt(const std::string& blank_data_image_fmt);
+ std::string blank_data_image_fmt() const;
+
+ void set_logcat_mode(const std::string& mode);
+ std::string logcat_mode() const;
+
+ void set_logcat_vsock_port(int port);
+ int logcat_vsock_port() const;
+
+ void set_frames_vsock_port(int port);
+ int frames_vsock_port() const;
+
+ bool enable_ivserver() const;
+
+ std::string touch_socket_path() const;
+ std::string keyboard_socket_path() const;
+
private:
std::unique_ptr<Json::Value> dictionary_;
@@ -237,11 +342,12 @@
std::string GetDefaultPerInstanceDir();
std::string GetDefaultMempath();
+int GetDefaultPerInstanceVsockCid();
std::string DefaultHostArtifactsPath(const std::string& file);
std::string DefaultGuestImagePath(const std::string& file);
-// Whether the installed host packages support calling qemu directly instead of
-// through libvirt
+// Whether the host supports qemu
bool HostSupportsQemuCli();
+bool HostSupportsVsock();
} // namespace vsoc
diff --git a/host/libs/vm_manager/Android.bp b/host/libs/vm_manager/Android.bp
index 0d077ea..6c638ea 100644
--- a/host/libs/vm_manager/Android.bp
+++ b/host/libs/vm_manager/Android.bp
@@ -16,7 +16,7 @@
cc_library_host_static {
name: "libcuttlefish_vm_manager",
srcs: [
- "libvirt_manager.cpp",
+ "crosvm_manager.cpp",
"qemu_manager.cpp",
"vm_manager.cpp",
],
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index ee46ef2..e037c3c 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -16,6 +16,33 @@
# limitations under the License.
#
+print_command() {
+ binary=$1; shift
+ binary_args=("$@")
+ printf %s "${binary}"
+ for i in "${binary_args[@]}"; do
+ case "$i" in
+ -*) printf "\\%s %s " $'\n' "$i" ;;
+ *) printf "%s " "$i" ;;
+ esac
+ done
+ echo
+}
+
+exec_run() {
+ binary=$1; shift
+ binary_args=("$@")
+ print_command "${binary}" "${binary_args[@]}"
+ exec "${binary}" "${binary_args[@]}"
+}
+
+run() {
+ binary=$1; shift
+ binary_args=("$@")
+ print_command "${binary}" "${binary_args[@]}"
+ "${binary}" "${binary_args[@]}"
+}
+
default_instance_number() {
if [[ "${USER::5}" == "vsoc-" ]]; then
echo "${USER: -2}"
@@ -30,15 +57,33 @@
default_mobile_tap_name="cvd-mtap-${CUTTLEFISH_INSTANCE}"
default_wifi_tap_name="cvd-wtap-${CUTTLEFISH_INSTANCE}"
+qemu_binary=${qemu_binary=/usr/bin/qemu-system-x86_64}
+dtc_binary=${dtc_binary:-dtc}
+
if [[ -z "${ivshmem_vector_count}" ]]; then
echo "The required ivshmem_vector_count environment variable is not set" >&2
exit 1
fi
+if [[ "${qemu_binary##*/}" = "qemu-system-aarch64" ]]; then
+ # On ARM, the early console can be PCI, and ISA is not supported
+ kernel_console_serial="pci-serial"
+ machine="virt,gic_version=2"
+ cpu=cortex-a53
+else
+ # On x86, the early console must be ISA, not PCI, so we start to get kernel
+ # messages as soon as possible. ISA devices do not have 'addr' assignments.
+ kernel_console_serial="isa-serial"
+ machine="pc-i440fx-2.8,accel=kvm"
+ cpu=host
+fi
+
+# Put anything here that might affect the machine configuration generated by
+# QEMU. Anything which connects statefully to another service (like a socket)
+# should be added in another section below.
args=(
- -enable-kvm
-name "guest=${instance_name:-${default_instance_name}},debug-threads=on"
- -machine "pc-i440fx-2.8,accel=kvm,usb=off,dump-guest-core=off"
+ -machine "${machine},usb=off,dump-guest-core=off"
-m "${memory_mb:-2048}"
-realtime mlock=off
-smp "${cpus:-2},sockets=${cpus:-2},cores=1,threads=1"
@@ -46,43 +91,115 @@
-display none
-no-user-config
-nodefaults
- -chardev "socket,id=charmonitor,path=${monitor_path:-${default_dir}/qemu_monitor.sock},nowait"
- -mon "chardev=charmonitor,id=monitor,mode=control"
-rtc "base=utc"
-no-shutdown
-boot "strict=on"
-kernel "${kernel_image_path:-${HOME}/kernel}"
-append "${kernel_cmdline:-"loop.max_part=7 console=ttyS0 androidboot.console=ttyS1 androidboot.hardware=vsoc enforcing=0 audit=1 androidboot.selinux=permissive mac80211_hwsim.radios=0 security=selinux buildvariant=userdebug androidboot.serialno=CUTTLEFISHCVD01 androidboot.lcd_density=160"}"
-dtb "${dtb_path:-${HOME}/config/cuttlefish.dtb}"
- -device "piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2"
- -device "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4"
+ -device "piix3-usb-uhci,id=usb,addr=0x1.0x2"
+ -device "virtio-serial-pci,id=virtio-serial0"
-drive "file=${system_image_path:-${HOME}/system.img},format=raw,if=none,id=drive-virtio-disk0,aio=threads"
- -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
-drive "file=${data_image_path:-${HOME}/userdata.img},format=raw,if=none,id=drive-virtio-disk1,aio=threads"
- -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk1,id=virtio-disk1"
-drive "file=${cache_image_path:-${HOME}/cache.img},format=raw,if=none,id=drive-virtio-disk2,aio=threads"
- -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x7,drive=drive-virtio-disk2,id=virtio-disk2"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk2,id=virtio-disk2"
-drive "file=${vendor_image_path:-${HOME}/vendor.img},format=raw,if=none,id=drive-virtio-disk3,aio=threads"
- -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x8,drive=drive-virtio-disk3,id=virtio-disk3"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk3,id=virtio-disk3"
+ -drive "file=${metadata_image_path:-${HOME}/metadata.img},format=raw,if=none,id=drive-virtio-disk4,aio=threads"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk4,id=virtio-disk4"
+ -drive "file=${product_image_path:-${HOME}/product.img},format=raw,if=none,id=drive-virtio-disk5,aio=threads"
+ -device "virtio-blk-pci,scsi=off,drive=drive-virtio-disk5,id=virtio-disk5"
-netdev "tap,id=hostnet0,ifname=${wifi_tap_name:-${default_wifi_tap_name}},script=no,downscript=no"
- -device "virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=0x2"
+ -device "virtio-net-pci,netdev=hostnet0,id=net0"
-netdev "tap,id=hostnet1,ifname=${mobile_tap_name:-${default_mobile_tap_name}},script=no,downscript=no"
- -device "virtio-net-pci,netdev=hostnet1,id=net1,bus=pci.0,addr=0x3"
- -chardev "socket,id=charserial0,path=${kernel_log_socket_name:-${default_dir}/kernel-log}"
- -device "isa-serial,chardev=charserial0,id=serial0"
- -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
- -device "isa-serial,chardev=charserial1,id=serial1"
- -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
- -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
- -device "virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9"
+ -device "virtio-net-pci,netdev=hostnet1,id=net1"
+ -device "virtio-balloon-pci,id=balloon0"
-object "rng-random,id=objrng0,filename=/dev/urandom"
- -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000,bus=pci.0,addr=0xa"
+ -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000"
+ -cpu "${cpu}"
+ -msg "timestamp=on"
+ -device "AC97"
+)
+
+if [[ -n "${dtb_path}" ]]; then
+ if [[ "${qemu_binary##*/}" = "qemu-system-aarch64" ]]; then
+ # Decompile the dt fragment to include in our machine FDT
+ dtsi_path="${default_dir}/android.dtsi"
+ dtc_args=(
+ -I dtb
+ "${dtb_path}"
+ -O dts
+ -o "${dtsi_path}"
+ )
+ run "${dtc_binary}" "${dtc_args[@]}"
+
+ # Remove duplicate version definition from the dtsi
+ sed_binary=sed
+ sed_args=(
+ -i "/^\/dts-v1\/;$/d"
+ ${dtsi_path}
+ )
+ run "${sed_binary}" "${sed_args[@]}"
+
+ # Dump the machine FDT blob
+ dts_path="${default_dir}/cuttlefish.dts"
+ dtb_path="${default_dir}/cuttlefish.dtb"
+ dtb_args=(-machine "dumpdtb=${dtb_path}")
+ run "${qemu_binary}" "${args[@]}" "${dtb_args[@]}"
+
+ # Decompile the FDT blob
+ dtc_args=(
+ -I dtb
+ ${dtb_path}
+ -O dts
+ -o ${dts_path}
+ )
+ run "${dtc_binary}" "${dtc_args[@]}"
+
+ # Concatenate the dts and dtsi sources
+ echo "cat ${dtsi_path} >>${dts_path}"
+ echo
+ cat ${dtsi_path} >>${dts_path}
+
+ # Compile the patched machine FDT
+ dtc_args=(
+ -i "${dts_path%/*}"
+ -I dts
+ "${dts_path}"
+ -O dtb
+ -o "${dtb_path}"
+ )
+ run "${dtc_binary}" "${dtc_args[@]}"
+ fi
+
+ args+=(-dtb "${dtb_path}")
+fi
+
+# The services providing these sockets don't expect multiple connections,
+# so we must not have them in 'args' when we dump the machine FDT. It's
+# OK to add them now, after the dumping and patching has completed.
+# The (maybe patched) DTB can also be provided now.
+
+args+=(
+ -chardev "socket,id=charmonitor,path=${monitor_path:-${default_dir}/qemu_monitor.sock},server,nowait"
+ -mon "chardev=charmonitor,id=monitor,mode=control"
+ -chardev "socket,id=charserial0,path=${kernel_log_socket_name:-${default_dir}/kernel-log}"
+ -device "${kernel_console_serial},chardev=charserial0,id=serial0"
+ -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
+ -device "pci-serial,chardev=charserial1,id=serial1"
-chardev "socket,path=${ivshmem_qemu_socket_path:-${default_dir}/ivshmem_socket_qemu},id=ivsocket"
-device "ivshmem-doorbell,chardev=ivsocket,vectors=${ivshmem_vector_count}"
- -cpu host
- -msg "timestamp=on"
)
+if [[ "${logcat_mode}" == "serial" ]]; then
+ args+=(
+ -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
+ -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
+ )
+fi
+
if [[ -n "${gdb_flag}" ]]; then
args+=(-gdb "${gdb_flag}")
fi
@@ -97,14 +214,10 @@
-device "virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel1,id=channel1,name=cf-gadget-usb-v1"
)
fi
-printf %s "exec ${qemu_binary=/usr/bin/qemu-system-x86_64}"
-for i in "${args[@]}"; do
- case "$i" in
- -*) printf "\\%s %s " $'\n' "$i" ;;
- *) printf "%s " "$i" ;;
- esac
-done
-echo
-exec "${qemu_binary=/usr/bin/qemu-system-x86_64}" \
- "${args[@]}"
+if [[ ${vsock_guest_cid:-0} -gt 2 ]]; then
+ args+=(-device "vhost-vsock-pci,guest-cid=${vsock_guest_cid}")
+fi
+
+export QEMU_AUDIO_DRV=none
+exec_run "${qemu_binary}" "${args[@]}"
diff --git a/host/libs/vm_manager/crosvm_manager.cpp b/host/libs/vm_manager/crosvm_manager.cpp
new file mode 100644
index 0000000..1605931
--- /dev/null
+++ b/host/libs/vm_manager/crosvm_manager.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "host/libs/vm_manager/crosvm_manager.h"
+
+#include <string>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "common/libs/utils/network.h"
+#include "common/libs/utils/subprocess.h"
+#include "host/libs/config/cuttlefish_config.h"
+#include "host/libs/vm_manager/qemu_manager.h"
+
+namespace vm_manager {
+
+namespace {
+
+std::string GetControlSocketPath(const vsoc::CuttlefishConfig* config) {
+ return config->PerInstancePath("crosvm_control.sock");
+}
+
+cvd::SharedFD ConnectToLogMonitor(const std::string& log_monitor_name) {
+ return cvd::SharedFD::SocketLocalClient(log_monitor_name.c_str(), false,
+ SOCK_STREAM);
+}
+
+void AddTapFdParameter(cvd::Command* crosvm_cmd, const std::string& tap_name) {
+ auto tap_fd = cvd::OpenTapInterface(tap_name);
+ if (tap_fd->IsOpen()) {
+ crosvm_cmd->AddParameter("--tap-fd=", tap_fd);
+ } else {
+ LOG(ERROR) << "Unable to connect to " << tap_name << ": "
+ << tap_fd->StrError();
+ }
+}
+
+} // namespace
+
+const std::string CrosvmManager::name() { return "crosvm"; }
+
+CrosvmManager::CrosvmManager(const vsoc::CuttlefishConfig* config)
+ : VmManager(config) {}
+
+cvd::Command CrosvmManager::StartCommand() {
+ if (!config_->ramdisk_image_path().empty()) {
+ // TODO re-enable ramdisk when crosvm supports it
+ LOG(FATAL) << "initramfs not supported";
+ return cvd::Command("/bin/false");
+ }
+
+ // TODO Add aarch64 support
+ // TODO Add the tap interfaces (--tap-fd)
+ // TODO Redirect logcat output
+
+ // Run crosvm directly instead of through a cf_crosvm.sh script. The kernel
+ // logs are on crosvm's standard output, so we need to redirect it to the log
+ // monitor socket, a helper script will print more than just the logs to
+ // standard output.
+ cvd::Command command(config_->crosvm_binary());
+ command.AddParameter("run");
+
+ command.AddParameter("--null-audio");
+ command.AddParameter("--mem=", config_->memory_mb());
+ command.AddParameter("--cpus=", config_->cpus());
+ command.AddParameter("--params=", config_->kernel_cmdline_as_string());
+ command.AddParameter("--rwdisk=", config_->system_image_path());
+ command.AddParameter("--rwdisk=", config_->data_image_path());
+ command.AddParameter("--rwdisk=", config_->cache_image_path());
+ command.AddParameter("--rwdisk=", config_->vendor_image_path());
+ command.AddParameter("--rwdisk=", config_->metadata_image_path());
+ command.AddParameter("--rwdisk=", config_->product_image_path());
+ command.AddParameter("--socket=", GetControlSocketPath(config_));
+ command.AddParameter("--android-fstab=", config_->gsi_fstab_path());
+ command.AddParameter("--single-touch=", config_->touch_socket_path(), ":",
+ config_->x_res(), ":", config_->y_res());
+ command.AddParameter("--keyboard=", config_->keyboard_socket_path());
+
+ AddTapFdParameter(&command, config_->wifi_tap_name());
+ AddTapFdParameter(&command, config_->mobile_tap_name());
+
+ // TODO remove this (use crosvm's seccomp files)
+ command.AddParameter("--disable-sandbox");
+
+ if (config_->vsock_guest_cid() >= 2) {
+ command.AddParameter("--cid=", config_->vsock_guest_cid());
+ }
+
+ auto kernel_log_connection =
+ ConnectToLogMonitor(config_->kernel_log_socket_name());
+ if (!kernel_log_connection->IsOpen()) {
+ LOG(WARNING) << "Unable to connect to log monitor: "
+ << kernel_log_connection->StrError();
+ } else {
+ command.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdOut,
+ kernel_log_connection);
+ }
+
+ auto dev_null = cvd::SharedFD::Open("/dev/null", O_RDONLY);
+ if (dev_null->IsOpen()) {
+ command.RedirectStdIO(cvd::Subprocess::StdIOChannel::kStdIn, dev_null);
+ } else {
+ LOG(ERROR) << "Unable to open /dev/null for stdin redirection";
+ }
+
+ // This needs to be the last parameter
+ command.AddParameter(config_->GetKernelImageToUse());
+
+ return command;
+}
+
+bool CrosvmManager::Stop() {
+ cvd::Command command(config_->crosvm_binary());
+ command.AddParameter("stop");
+ command.AddParameter(GetControlSocketPath(config_));
+
+ auto process = command.Start();
+
+ return process.Wait() == 0;
+}
+
+} // namespace vm_manager
diff --git a/host/libs/vm_manager/libvirt_manager.h b/host/libs/vm_manager/crosvm_manager.h
similarity index 65%
rename from host/libs/vm_manager/libvirt_manager.h
rename to host/libs/vm_manager/crosvm_manager.h
index 6bb99d1..7574e51 100644
--- a/host/libs/vm_manager/libvirt_manager.h
+++ b/host/libs/vm_manager/crosvm_manager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -15,25 +15,25 @@
*/
#pragma once
-#include <string>
-
#include "host/libs/vm_manager/vm_manager.h"
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/utils/subprocess.h"
+
namespace vm_manager {
-class LibvirtManager : public VmManager {
+// Starts a guest VM with crosvm. It requires the host package to support the
+// qemu-cli capability (for network only).
+class CrosvmManager : public VmManager {
public:
static const std::string name();
static bool EnsureInstanceDirExists(const std::string& instance_dir);
- LibvirtManager(const vsoc::CuttlefishConfig* config);
- virtual ~LibvirtManager() = default;
+ CrosvmManager(const vsoc::CuttlefishConfig* config);
+ virtual ~CrosvmManager() = default;
- bool Start() override;
+ cvd::Command StartCommand() override;
bool Stop() override;
-
- bool ValidateHostConfiguration(
- std::vector<std::string>* config_commands) const override;
};
} // namespace vm_manager
diff --git a/host/libs/vm_manager/libvirt_manager.cpp b/host/libs/vm_manager/libvirt_manager.cpp
deleted file mode 100644
index 24ff33b..0000000
--- a/host/libs/vm_manager/libvirt_manager.cpp
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * 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/libs/vm_manager/libvirt_manager.h"
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <cstdlib>
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-#include <glog/logging.h>
-#include <libxml/tree.h>
-
-#include "common/libs/utils/files.h"
-#include "common/libs/utils/subprocess.h"
-#include "common/libs/utils/users.h"
-#include "host/libs/config/cuttlefish_config.h"
-
-// A lot of useful information about the document created here can be found on
-// these websites:
-// - https://libvirt.org/formatdomain.html
-// - https://wiki.libvirt.org/page/Virtio
-namespace vm_manager {
-
-namespace {
-// This trivial no-op helper function serves purpose of making libxml2 happy.
-// Apparently, *most* (not all!) string literals in libxml2 have to be of
-// unsigned char* (aka xmlChar*) type.
-inline const xmlChar* xc(const char* str) {
- return reinterpret_cast<const xmlChar*>(str);
-}
-
-// Helper functions that allow us to combine any set of arguments to a single
-// string.
-// Example:
-// concat("Answer", ' ', "is: ", 42);
-// will produce string "Answer is: 42"
-template <typename Arg>
-inline std::ostream& concat_helper(std::ostream& o, Arg a) {
- o << a;
- return o;
-}
-
-template <typename Arg, typename... Args>
-inline std::ostream& concat_helper(std::ostream& o, Arg a, Args... args) {
- o << a;
- concat_helper(o, args...);
- return o;
-}
-
-template <typename... Args>
-inline std::string concat(Args... args) {
- std::ostringstream str;
- concat_helper(str, args...);
- return str.str();
-}
-
-enum class DeviceSourceType {
- kFile,
- kUnixSocketClient,
- kUnixSocketServer,
-};
-
-// Basic VM configuration.
-// This section configures name, basic resource allocation and response to
-// events.
-void ConfigureVM(xmlNode* root, const std::string& instance_name, int cpus,
- int mem_mb, const std::string& uuid) {
- xmlNewChild(root, nullptr, xc("name"), xc(instance_name.c_str()));
-
- // TODO(ender): should this all be 'restart'?
- xmlNewChild(root, nullptr, xc("on_poweroff"), xc("destroy"));
- xmlNewChild(root, nullptr, xc("on_reboot"), xc("restart"));
- xmlNewChild(root, nullptr, xc("on_crash"), xc("restart"));
- xmlNewChild(root, nullptr, xc("vcpu"), xc(concat(cpus).c_str()));
- xmlNewChild(root, nullptr, xc("memory"), xc(concat(mem_mb << 10).c_str()));
- if (uuid.size()) {
- xmlNewChild(root, nullptr, xc("uuid"), xc(uuid.c_str()));
- }
-}
-
-// Configure VM features.
-// This section takes care of the <features> section of the target XML file.
-void ConfigureVMFeatures(xmlNode* root,
- const std::initializer_list<std::string>& features) {
- auto ch = xmlNewChild(root, nullptr, xc("features"), nullptr);
- for (const auto& str : features) {
- xmlNewChild(ch, nullptr, xc(str.c_str()), nullptr);
- }
-}
-
-// Configure VM OS.
-// This section configures target os (<os>).
-void ConfigureOperatingSystem(xmlNode* root, const std::string& kernel,
- const std::string& initrd,
- const std::string& args, const std::string& dtb) {
- auto os = xmlNewChild(root, nullptr, xc("os"), nullptr);
-
- auto type = xmlNewChild(os, nullptr, xc("type"), xc("hvm"));
- xmlNewProp(type, xc("arch"), xc("x86_64"));
- xmlNewProp(type, xc("machine"), xc("pc"));
-
- xmlNewChild(os, nullptr, xc("kernel"), xc(kernel.c_str()));
- if (!initrd.empty()) {
- xmlNewChild(os, nullptr, xc("initrd"), xc(initrd.c_str()));
- }
- xmlNewChild(os, nullptr, xc("cmdline"), xc(args.c_str()));
- xmlNewChild(os, nullptr, xc("dtb"), xc(dtb.c_str()));
-}
-
-// Configure QEmu specific arguments.
-// This section adds the <qemu:commandline> node.
-xmlNodePtr ConfigureQEmuSpecificOptions(
- xmlNode* root, std::initializer_list<std::string> qemu_args,
- xmlNode* existing_options = nullptr) {
- xmlNs* qemu_ns{xmlNewNs(
- root, xc("http://libvirt.org/schemas/domain/qemu/1.0"), xc("qemu"))};
-
- xmlNode* cmd;
- if (existing_options) {
- cmd = existing_options;
- } else {
- cmd = xmlNewChild(root, qemu_ns, xc("commandline"), nullptr);
- }
-
- for (const auto& str : qemu_args) {
- auto arg = xmlNewChild(cmd, qemu_ns, xc("arg"), nullptr);
- xmlNewProp(arg, xc("value"), xc(str.c_str()));
- }
- return cmd;
-}
-
-void ConfigureDeviceSource(xmlNode* device, DeviceSourceType type,
- const std::string& path) {
- auto source = xmlNewChild(device, nullptr, xc("source"), nullptr);
- xmlNewProp(source, xc("path"), xc(path.c_str()));
-
- switch (type) {
- case DeviceSourceType::kFile:
- xmlNewProp(device, xc("type"), xc("file"));
- break;
-
- case DeviceSourceType::kUnixSocketClient:
- xmlNewProp(device, xc("type"), xc("unix"));
- xmlNewProp(source, xc("mode"), xc("connect"));
- break;
-
- case DeviceSourceType::kUnixSocketServer:
- xmlNewProp(device, xc("type"), xc("unix"));
- xmlNewProp(source, xc("mode"), xc("bind"));
- break;
- }
-}
-
-// Configure serial port.
-// This section adds <serial> elements to <device> node.
-void ConfigureSerialPort(xmlNode* devices, int port, DeviceSourceType type,
- const std::string& path) {
- auto tty = xmlNewChild(devices, nullptr, xc("serial"), nullptr);
- ConfigureDeviceSource(tty, type, path);
-
- auto tgt = xmlNewChild(tty, nullptr, xc("target"), nullptr);
- xmlNewProp(tgt, xc("port"), xc(concat(port).c_str()));
-}
-
-// Configure disk partition.
-// This section adds <disk> elements to <devices> node.
-void ConfigureDisk(xmlNode* devices, const std::string& name,
- const std::string& path) {
- auto ch = xmlNewChild(devices, nullptr, xc("disk"), nullptr);
- xmlNewProp(ch, xc("type"), xc("file"));
-
- auto dr = xmlNewChild(ch, nullptr, xc("driver"), nullptr);
- xmlNewProp(dr, xc("name"), xc("qemu"));
- xmlNewProp(dr, xc("type"), xc("raw"));
- xmlNewProp(dr, xc("io"), xc("threads"));
-
- auto tg = xmlNewChild(ch, nullptr, xc("target"), nullptr);
- xmlNewProp(tg, xc("dev"), xc(name.c_str()));
- xmlNewProp(tg, xc("bus"), xc("virtio"));
-
- auto sr = xmlNewChild(ch, nullptr, xc("source"), nullptr);
- xmlNewProp(sr, xc("file"), xc(path.c_str()));
-}
-
-// Configure virtio channel.
-// This section adds <channel> elements to <devices> node.
-void ConfigureVirtioChannel(xmlNode* devices, int port, const std::string& name,
- DeviceSourceType type, const std::string& path) {
- if (path.empty()) {
- return;
- }
- auto vch = xmlNewChild(devices, nullptr, xc("channel"), nullptr);
- ConfigureDeviceSource(vch, type, path);
-
- auto tgt = xmlNewChild(vch, nullptr, xc("target"), nullptr);
- xmlNewProp(tgt, xc("type"), xc("virtio"));
- xmlNewProp(tgt, xc("name"), xc(name.c_str()));
-
- auto adr = xmlNewChild(vch, nullptr, xc("address"), nullptr);
- xmlNewProp(adr, xc("type"), xc("virtio-serial"));
- xmlNewProp(adr, xc("controller"), xc("0"));
- xmlNewProp(adr, xc("bus"), xc("0"));
- xmlNewProp(adr, xc("port"), xc(concat(port).c_str()));
-}
-
-// Configure network interface.
-// This section adds <interface> elements to <devices> node.
-void ConfigureNIC(xmlNode* devices, const std::string& name,
- const std::string& bridge, int guest_id, int nic_id) {
- auto nic = xmlNewChild(devices, nullptr, xc("interface"), nullptr);
- xmlNewProp(nic, xc("type"), xc("bridge"));
-
- auto brg = xmlNewChild(nic, nullptr, xc("source"), nullptr);
- xmlNewProp(brg, xc("bridge"), xc(bridge.c_str()));
-
- auto mac = xmlNewChild(nic, nullptr, xc("mac"), nullptr);
- xmlNewProp(mac, xc("address"),
- xc(concat("00:43:56:44:", std::setfill('0'), std::hex,
- std::setw(2), guest_id, ':', std::setw(2), nic_id)
- .c_str()));
-
- auto mdl = xmlNewChild(nic, nullptr, xc("model"), nullptr);
- xmlNewProp(mdl, xc("type"), xc("virtio"));
-
- auto tgt = xmlNewChild(nic, nullptr, xc("target"), nullptr);
- xmlNewProp(tgt, xc("dev"), xc(name.c_str()));
-}
-
-// Configure Harwdare Random Number Generator.
-// This section adds <rng> element to <devices> node.
-void ConfigureHWRNG(xmlNode* devices, const std::string& entsrc) {
- auto rng = xmlNewChild(devices, nullptr, xc("rng"), nullptr);
- xmlNewProp(rng, xc("model"), xc("virtio"));
-
- auto rate = xmlNewChild(rng, nullptr, xc("rate"), nullptr);
- xmlNewProp(rate, xc("period"), xc("2000"));
- xmlNewProp(rate, xc("bytes"), xc("1024"));
-
- auto bend = xmlNewChild(rng, nullptr, xc("backend"), xc(entsrc.c_str()));
- xmlNewProp(bend, xc("model"), xc("random"));
-}
-
-static std::string GetLibvirtCommand(const vsoc::CuttlefishConfig* config) {
- std::string cmd = "virsh";
- if (!config->hypervisor_uri().empty()) {
- cmd += " -c " + config->hypervisor_uri();
- }
- return cmd;
-}
-
-std::string BuildXmlConfig(const vsoc::CuttlefishConfig* config) {
- std::string instance_name = config->instance_name();
-
- std::unique_ptr<xmlDoc, void (*)(xmlDocPtr)> xml{xmlNewDoc(xc("1.0")),
- xmlFreeDoc};
- auto root{xmlNewNode(nullptr, xc("domain"))};
- xmlDocSetRootElement(xml.get(), root);
- xmlNewProp(root, xc("type"), xc("kvm"));
-
- ConfigureVM(root, instance_name, config->cpus(), config->memory_mb(),
- config->uuid());
- ConfigureVMFeatures(root, {"acpi", "apic", "hap"});
- ConfigureOperatingSystem(root, config->kernel_image_path(),
- config->ramdisk_image_path(),
- config->kernel_cmdline_as_string(),
- config->dtb_path());
- auto qemu_options = ConfigureQEmuSpecificOptions(
- root, {"-chardev",
- concat("socket,path=", config->ivshmem_qemu_socket_path(),
- ",id=ivsocket"),
- "-device",
- concat("ivshmem-doorbell,chardev=ivsocket,vectors=",
- config->ivshmem_vector_count()),
- "-cpu", "host"});
- if (config->gdb_flag().size()) {
- ConfigureQEmuSpecificOptions(root, {"-gdb", config->gdb_flag().c_str(),
- "-S"},
- qemu_options);
- }
-
- if (config->disable_app_armor_security()) {
- auto seclabel = xmlNewChild(root, nullptr, xc("seclabel"), nullptr);
- xmlNewProp(seclabel, xc("type"), xc("none"));
- xmlNewProp(seclabel, xc("model"), xc("apparmor"));
- }
- if (config->disable_dac_security()) {
- auto seclabel = xmlNewChild(root, nullptr, xc("seclabel"), nullptr);
- xmlNewProp(seclabel, xc("type"), xc("none"));
- xmlNewProp(seclabel, xc("model"), xc("dac"));
- }
-
- auto devices = xmlNewChild(root, nullptr, xc("devices"), nullptr);
-
- ConfigureSerialPort(devices, 0, DeviceSourceType::kUnixSocketClient,
- config->kernel_log_socket_name());
- ConfigureSerialPort(devices, 1, DeviceSourceType::kUnixSocketServer,
- config->console_path());
- ConfigureVirtioChannel(devices, 1, "cf-logcat", DeviceSourceType::kFile,
- config->logcat_path());
- ConfigureVirtioChannel(devices, 2, "cf-gadget-usb-v1",
- DeviceSourceType::kUnixSocketClient,
- config->usb_v1_socket_name());
-
- ConfigureDisk(devices, "vda", config->system_image_path());
- ConfigureDisk(devices, "vdb", config->data_image_path());
- ConfigureDisk(devices, "vdc", config->cache_image_path());
- ConfigureDisk(devices, "vdd", config->vendor_image_path());
-
- ConfigureNIC(devices, config->wifi_tap_name(), config->wifi_bridge_name(),
- vsoc::GetInstance(), 1);
- ConfigureNIC(devices, config->mobile_tap_name(), config->mobile_bridge_name(),
- vsoc::GetInstance(), 2);
- ConfigureHWRNG(devices, config->entropy_source());
-
- xmlChar* tgt;
- int tgt_len;
-
- xmlDocDumpFormatMemoryEnc(xml.get(), &tgt, &tgt_len, "utf-8", true);
- std::string out((const char*)(tgt), tgt_len);
- xmlFree(tgt);
- return out;
-}
-} // namespace
-
-const std::string LibvirtManager::name() { return "libvirt"; }
-
-LibvirtManager::LibvirtManager(const vsoc::CuttlefishConfig* config)
- : VmManager(config) {}
-
-bool LibvirtManager::Start() {
- std::string start_command = GetLibvirtCommand(config_);
- start_command += " create /dev/fd/0";
-
- std::string xml = BuildXmlConfig(config_);
- if (config_->log_xml()) {
- LOG(INFO) << "Using XML:\n" << xml;
- }
-
- FILE* launch = popen(start_command.c_str(), "w");
- if (!launch) {
- LOG(ERROR) << "Unable to execute " << start_command;
- return false;
- }
- int rval = fputs(xml.c_str(), launch);
- if (rval == EOF) {
- LOG(ERROR) << "Launch command exited while accepting XML";
- return false;
- }
- int exit_code = pclose(launch);
- if (exit_code != 0) {
- LOG(ERROR) << "Launch command exited with status " << exit_code;
- return false;
- }
- return true;
-}
-
-bool LibvirtManager::Stop() {
- auto stop_command = GetLibvirtCommand(config_);
- stop_command += " destroy " + config_->instance_name();
- return std::system(stop_command.c_str()) == 0;
-}
-
-bool LibvirtManager::EnsureInstanceDirExists(const std::string& instance_dir) {
- if (!cvd::DirectoryExists(instance_dir)) {
- LOG(INFO) << "Setting up " << instance_dir;
- cvd::execute({"/usr/bin/sudo", "/bin/mkdir", "-m", "0775", instance_dir});
-
- // When created with sudo the owner and group is root.
- std::string user_group = getenv("USER");
- user_group += ":libvirt-qemu";
- cvd::execute({"/usr/bin/sudo", "/bin/chown", user_group, instance_dir});
- }
- return true;
-}
-
-bool LibvirtManager::ValidateHostConfiguration(
- std::vector<std::string>* config_commands) const {
- return VmManager::UserInGroup("libvirt", config_commands);
-}
-} // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 8a46825..bb8e0af 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -51,39 +51,6 @@
LOG(INFO) << key << "=" << value;
}
-cvd::Subprocess BuildAndRunQemuCmd(const vsoc::CuttlefishConfig* config) {
- // Set the config values in the environment
- LogAndSetEnv("qemu_binary", config->qemu_binary());
- LogAndSetEnv("instance_name", config->instance_name());
- LogAndSetEnv("memory_mb", std::to_string(config->memory_mb()));
- LogAndSetEnv("cpus", std::to_string(config->cpus()));
- LogAndSetEnv("uuid", config->uuid());
- LogAndSetEnv("monitor_path", GetMonitorPath(config));
- LogAndSetEnv("kernel_image_path", config->kernel_image_path());
- LogAndSetEnv("gdb_flag", config->gdb_flag());
- LogAndSetEnv("ramdisk_image_path", config->ramdisk_image_path());
- LogAndSetEnv("kernel_cmdline", config->kernel_cmdline_as_string());
- LogAndSetEnv("dtb_path", config->dtb_path());
- LogAndSetEnv("system_image_path", config->system_image_path());
- LogAndSetEnv("data_image_path", config->data_image_path());
- LogAndSetEnv("cache_image_path", config->cache_image_path());
- LogAndSetEnv("vendor_image_path", config->vendor_image_path());
- LogAndSetEnv("wifi_tap_name", config->wifi_tap_name());
- LogAndSetEnv("mobile_tap_name", config->mobile_tap_name());
- LogAndSetEnv("kernel_log_socket_name",
- config->kernel_log_socket_name());
- LogAndSetEnv("console_path", config->console_path());
- LogAndSetEnv("logcat_path", config->logcat_path());
- LogAndSetEnv("ivshmem_qemu_socket_path",
- config->ivshmem_qemu_socket_path());
- LogAndSetEnv("ivshmem_vector_count",
- std::to_string(config->ivshmem_vector_count()));
- LogAndSetEnv("usb_v1_socket_name", config->usb_v1_socket_name());
-
- cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"));
- return qemu_cmd.Start();
-}
-
} // namespace
const std::string QemuManager::name() { return "qemu_cli"; }
@@ -91,51 +58,64 @@
QemuManager::QemuManager(const vsoc::CuttlefishConfig* config)
: VmManager(config) {}
-bool QemuManager::Start() {
- if (monitor_conn_->IsOpen()) {
- LOG(ERROR) << "Already started, should call Stop() first";
- return false;
- }
- auto monitor_path = GetMonitorPath(config_);
- auto monitor_sock = cvd::SharedFD::SocketLocalServer(
- monitor_path.c_str(), false, SOCK_STREAM, 0666);
+cvd::Command QemuManager::StartCommand(){
+ // Set the config values in the environment
+ LogAndSetEnv("qemu_binary", config_->qemu_binary());
+ LogAndSetEnv("instance_name", config_->instance_name());
+ LogAndSetEnv("memory_mb", std::to_string(config_->memory_mb()));
+ LogAndSetEnv("cpus", std::to_string(config_->cpus()));
+ LogAndSetEnv("uuid", config_->uuid());
+ LogAndSetEnv("monitor_path", GetMonitorPath(config_));
+ LogAndSetEnv("kernel_image_path", config_->GetKernelImageToUse());
+ LogAndSetEnv("gdb_flag", config_->gdb_flag());
+ LogAndSetEnv("ramdisk_image_path", config_->ramdisk_image_path());
+ LogAndSetEnv("kernel_cmdline", config_->kernel_cmdline_as_string());
+ LogAndSetEnv("dtb_path", config_->dtb_path());
+ LogAndSetEnv("system_image_path", config_->system_image_path());
+ LogAndSetEnv("data_image_path", config_->data_image_path());
+ LogAndSetEnv("cache_image_path", config_->cache_image_path());
+ LogAndSetEnv("vendor_image_path", config_->vendor_image_path());
+ LogAndSetEnv("metadata_image_path", config_->metadata_image_path());
+ LogAndSetEnv("product_image_path", config_->product_image_path());
+ LogAndSetEnv("wifi_tap_name", config_->wifi_tap_name());
+ LogAndSetEnv("mobile_tap_name", config_->mobile_tap_name());
+ LogAndSetEnv("kernel_log_socket_name",
+ config_->kernel_log_socket_name());
+ LogAndSetEnv("console_path", config_->console_path());
+ LogAndSetEnv("logcat_path", config_->logcat_path());
+ LogAndSetEnv("ivshmem_qemu_socket_path",
+ config_->ivshmem_qemu_socket_path());
+ LogAndSetEnv("ivshmem_vector_count",
+ std::to_string(config_->ivshmem_vector_count()));
+ LogAndSetEnv("usb_v1_socket_name", config_->usb_v1_socket_name());
+ LogAndSetEnv("vsock_guest_cid", std::to_string(config_->vsock_guest_cid()));
+ LogAndSetEnv("logcat_mode", config_->logcat_mode());
- BuildAndRunQemuCmd(config_);
-
- cvd::SharedFDSet fdset;
- fdset.Set(monitor_sock);
- struct timeval timeout {5, 0}; // Wait at most 5 seconds for qemu to connect
- int select_result = cvd::Select(&fdset, 0, 0, &timeout);
- if (select_result < 0) {
- LOG(ERROR) << "Error when calling seletct: " << strerror(errno);
- return false;
- }
- if (select_result == 0) {
- LOG(ERROR) << "Timed out waiting for qemu to connect to monitor";
- return false;
- }
- monitor_conn_ = cvd::SharedFD::Accept(*monitor_sock);
- monitor_conn_->Fcntl(F_SETFD, FD_CLOEXEC);
- return monitor_conn_->IsOpen();
+ cvd::Command qemu_cmd(vsoc::DefaultHostArtifactsPath("bin/cf_qemu.sh"));
+ return qemu_cmd;
}
bool QemuManager::Stop() {
- if (!monitor_conn_->IsOpen()) {
+ auto monitor_path = GetMonitorPath(config_);
+ auto monitor_sock = cvd::SharedFD::SocketLocalClient(
+ monitor_path.c_str(), false, SOCK_STREAM);
+
+ if (!monitor_sock->IsOpen()) {
LOG(ERROR) << "The connection to qemu is closed, is it still running?";
return false;
}
char msg[] = "{\"execute\":\"qmp_capabilities\"}{\"execute\":\"quit\"}";
ssize_t len = sizeof(msg) - 1;
while (len > 0) {
- int tmp = monitor_conn_->Write(msg, len);
+ int tmp = monitor_sock->Write(msg, len);
if (tmp < 0) {
- LOG(ERROR) << "Error writing to socket: " << monitor_conn_->StrError();
+ LOG(ERROR) << "Error writing to socket: " << monitor_sock->StrError();
return false;
}
len -= tmp;
}
// Log the reply
char buff[1000];
- while ((len = monitor_conn_->Read(buff, sizeof(buff) - 1)) > 0) {
+ while ((len = monitor_sock->Read(buff, sizeof(buff) - 1)) > 0) {
buff[len] = '\0';
LOG(INFO) << "From qemu monitor: " << buff;
}
@@ -143,24 +123,4 @@
return true;
}
-bool QemuManager::EnsureInstanceDirExists(const std::string& instance_dir) {
- if (!cvd::DirectoryExists(instance_dir.c_str())) {
- LOG(INFO) << "Setting up " << instance_dir;
- if (mkdir(instance_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
- LOG(ERROR) << "Unable to create " << instance_dir << ". Error: " << errno;
- return false;
- }
- }
- return true;
-
-}
-
-bool QemuManager::ValidateHostConfiguration(
- std::vector<std::string>* config_commands) const {
- // the check for cvdnetwork needs to happen even if the user is not in kvm, so
- // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
- auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
- auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
- return in_kvm && in_cvdnetwork;
-}
} // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 6ae1926..a0c6540 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -26,18 +26,12 @@
class QemuManager : public VmManager {
public:
static const std::string name();
- static bool EnsureInstanceDirExists(const std::string& instance_dir);
QemuManager(const vsoc::CuttlefishConfig* config);
virtual ~QemuManager() = default;
- bool Start() override;
+ cvd::Command StartCommand() override;
bool Stop() override;
-
- bool ValidateHostConfiguration(
- std::vector<std::string>* config_commands) const override;
- private:
- cvd::SharedFD monitor_conn_;
};
} // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index b452bf9..5433d6d 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -22,8 +22,8 @@
#include "common/libs/utils/users.h"
#include "host/libs/config/cuttlefish_config.h"
-#include "host/libs/vm_manager/libvirt_manager.h"
#include "host/libs/vm_manager/qemu_manager.h"
+#include "host/libs/vm_manager/crosvm_manager.h"
namespace vm_manager {
@@ -40,22 +40,17 @@
std::map<std::string, VmManager::VmManagerHelper>
VmManager::vm_manager_helpers_ = {
- {LibvirtManager::name(),
- {[](const vsoc::CuttlefishConfig* config) {
- return GetManagerSingleton<LibvirtManager>(config);
- },
- []() { return true; },
- [](const std::string& dir_path) {
- return LibvirtManager::EnsureInstanceDirExists(dir_path);
- }}},
{QemuManager::name(),
{[](const vsoc::CuttlefishConfig* config) {
return GetManagerSingleton<QemuManager>(config);
},
- []() { return vsoc::HostSupportsQemuCli(); },
- [](const std::string& dir_path) {
- return QemuManager::EnsureInstanceDirExists(dir_path);
- }}}};
+ []() { return vsoc::HostSupportsQemuCli(); }}},
+ {CrosvmManager::name(),
+ {[](const vsoc::CuttlefishConfig* config) {
+ return GetManagerSingleton<CrosvmManager>(config);
+ },
+ // Same as Qemu for the time being
+ []() { return vsoc::HostSupportsQemuCli(); }}}};
VmManager* VmManager::Get(const std::string& vm_manager_name,
const vsoc::CuttlefishConfig* config) {
@@ -77,7 +72,7 @@
std::vector<std::string> VmManager::GetValidNames() {
std::vector<std::string> ret = {};
- for (auto key_val: vm_manager_helpers_) {
+ for (const auto& key_val: vm_manager_helpers_) {
ret.push_back(key_val.first);
}
return ret;
@@ -94,9 +89,12 @@
return true;
}
-bool VmManager::EnsureInstanceDirExists(const std::string& vm_manager_name,
- const std::string& instance_dir_path) {
- return vm_manager_helpers_[vm_manager_name].instance_dir_creator(
- instance_dir_path);
+bool VmManager::ValidateHostConfiguration(
+ std::vector<std::string>* config_commands) const {
+ // the check for cvdnetwork needs to happen even if the user is not in kvm, so
+ // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
+ auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
+ auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
+ return in_kvm && in_cvdnetwork;
}
} // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index 98dd1d3..84bf77c 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -20,6 +20,7 @@
#include <utility>
#include <vector>
+#include <common/libs/utils/subprocess.h>
#include <host/libs/config/cuttlefish_config.h>
namespace vm_manager {
@@ -36,16 +37,14 @@
static bool IsValidName(const std::string& name);
static bool IsVmManagerSupported(const std::string& name);
static std::vector<std::string> GetValidNames();
- static bool EnsureInstanceDirExists(const std::string& vm_manager_name,
- const std::string& instance_dir_path);
virtual ~VmManager() = default;
- virtual bool Start() = 0;
+ virtual cvd::Command StartCommand() = 0;
virtual bool Stop() = 0;
virtual bool ValidateHostConfiguration(
- std::vector<std::string>* config_commands) const = 0;
+ std::vector<std::string>* config_commands) const;
protected:
static bool UserInGroup(const std::string& group,
@@ -59,8 +58,6 @@
std::function<VmManager*(const vsoc::CuttlefishConfig*)> builder;
// Whether the host packages support this vm manager
std::function<bool()> support_checker;
- // Creates the instance directory if it doesn't exist
- std::function<bool(const std::string&)> instance_dir_creator;
};
// Asociates a vm manager helper to every valid vm manager name
static std::map<std::string, VmManagerHelper> vm_manager_helpers_;
diff --git a/tools/build-qemu-packages.sh b/tools/build-qemu-packages.sh
deleted file mode 100755
index 7d30541..0000000
--- a/tools/build-qemu-packages.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-VIRGL_PACKAGE=libvirglrenderer-dev
-
-sudo apt update
-sudo apt upgrade -y
-sudo apt install -y debhelper ubuntu-dev-tools equivs
-dpkg -l "${VIRGL_PACKAGE}" || (
- # Try from the distribution
- sudo apt install -y "${VIRGL_PACKAGE}"
- dpkg -l "${VIRGL_PACKAGE}" || (
- echo That failed. Building from source
- curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0-2.dsc
- curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0.orig.tar.bz2
- curl -L -O http://http.debian.net/debian/pool/main/v/virglrenderer/virglrenderer_0.6.0-2.debian.tar.xz
- sudo mk-build-deps -i virglrenderer_0.6.0-2.dsc -t "apt-get -y"
- rm -f virglrenderer-build-deps_0.6.0-2_all.deb
- dpkg-source -x virglrenderer_0.6.0-2.dsc
- pushd virglrenderer*0
- dpkg-buildpackage -uc -us
- popd
- sudo apt install -y ./*.deb
- )
-)
-curl -L -O http://http.debian.net/debian/pool/main/q/qemu/qemu_2.12+dfsg.orig.tar.xz
-git clone https://salsa.debian.org/qemu-team/qemu.git
-pushd qemu
-debian/rules debian/control
-chmod +w debian/control
-popd
-sudo mk-build-deps -i qemu/debian/control -t "apt-get -y"
-rm -f ./qemu-build-deps_2.12+dfsg-3_amd64.deb
-pushd qemu
-dpkg-buildpackage -uc -us --jobs=auto
-popd
diff --git a/tools/create_base_image_gce.sh b/tools/create_base_image_gce.sh
index 2438dab..237760e 100755
--- a/tools/create_base_image_gce.sh
+++ b/tools/create_base_image_gce.sh
@@ -33,22 +33,6 @@
# Now gather all of the *.deb files to copy them into the image
debs=(*.deb)
-# See if we are using a local build of qemu.
-if [[ -x build-qemu-packages.sh ]]; then
- ./build-qemu-packages.sh
- # We want only a subset of the debs that will be generated
- for i in *.deb; do
- case "$i" in
- qemu-kvm_*) debs+=("$i") ;;
- qemu-system-common_*) debs+=("$i") ;;
- qemu-system-x86_*) debs+=("$i") ;;
- qemu-utils_*) debs+=("$i") ;;
- qemu-system-data_*) debs+=("$i") ;;
- libvirglrenderer0*) debs+=("$i") ;;
- esac
- done
-fi
-
tmp_debs=()
for i in "${debs[@]}"; do
tmp_debs+=(/tmp/"$(basename "$i")")
diff --git a/tools/create_base_image_hostlib.sh b/tools/create_base_image_hostlib.sh
index 26bf4f5..d8e583c 100755
--- a/tools/create_base_image_hostlib.sh
+++ b/tools/create_base_image_hostlib.sh
@@ -5,13 +5,21 @@
# INTERNAL_extra_source may be set to a directory containing the source for
# extra package to build.
+# INTERNAL_IP can be set to --internal-ip run on a GCE instance
+# The instance will need --scope compute-rw
+
source "${ANDROID_BUILD_TOP}/external/shflags/src/shflags"
DEFINE_string build_instance \
"${USER}-build" "Instance name to create for the build" "i"
+DEFINE_string build_project "$(gcloud config get-value project)" \
+ "Project to use for scratch"
+DEFINE_string build_zone "$(gcloud config get-value compute/zone)" \
+ "Zone to use for scratch resources"
DEFINE_string dest_image "vsoc-host-scratch-${USER}" "Image to create" "o"
DEFINE_string dest_family "" "Image family to add the image to" "f"
-DEFINE_string dest_project "" "Project to use for the new image" "p"
+DEFINE_string dest_project "$(gcloud config get-value project)" \
+ "Project to use for the new image" "p"
DEFINE_string launch_instance "" \
"Name of the instance to launch with the new image" "l"
DEFINE_string source_image_family debian-9 "Image familty to use as the base" \
@@ -26,11 +34,14 @@
DEFINE_string variant master \
"Variant to build: generally master or stable"
+
+SSH_FLAGS=(${INTERNAL_IP})
+
wait_for_instance() {
alive=""
while [[ -z "${alive}" ]]; do
sleep 5
- alive="$(gcloud compute ssh "$@" -- uptime || true)"
+ alive="$(gcloud compute ssh "${SSH_FLAGS[@]}" "$@" -- uptime || true)"
done
}
@@ -50,11 +61,7 @@
main() {
set -o errexit
set -x
- if [[ -n "${FLAGS_dest_project}" ]]; then
- dest_project_flag=("--project=${FLAGS_dest_project}")
- else
- dest_project_flag=()
- fi
+ PZ=(--project=${FLAGS_build_project} --zone=${FLAGS_build_zone})
if [[ -n "${FLAGS_dest_family}" ]]; then
dest_family_flag=("--family=${FLAGS_dest_family}")
else
@@ -69,9 +76,6 @@
"${ANDROID_BUILD_TOP}/device/google/cuttlefish_common/tools/create_base_image_gce.sh"
${scratch_dir}/*
)
- if [[ "${FLAGS_variant}" == master ]]; then
- source_files+=("${ANDROID_BUILD_TOP}/device/google/cuttlefish_common/tools/build-qemu-packages.sh")
- fi
if [[ -n "${INTERNAL_extra_source}" ]]; then
source_files+=("${INTERNAL_extra_source}"/*)
fi
@@ -81,50 +85,64 @@
delete_instances+=("${FLAGS_launch_instance}")
fi
gcloud compute instances delete -q \
- "${dest_project_flag[@]}" "${delete_instances[@]}" || \
+ "${PZ[@]}" "${delete_instances[@]}" || \
echo Not running
gcloud compute disks delete -q \
- "${dest_project_flag[@]}" "${FLAGS_dest_image}" || echo No scratch disk
+ "${PZ[@]}" "${FLAGS_dest_image}" || echo No scratch disk
gcloud compute images delete -q \
- "${dest_project_flag[@]}" "${FLAGS_dest_image}" || echo Not respinning
+ --project="${FLAGS_build_project}" "${FLAGS_dest_image}" || echo Not respinning
gcloud compute disks create \
- "${dest_project_flag[@]}" \
+ "${PZ[@]}" \
--image-family="${FLAGS_source_image_family}" \
--image-project="${FLAGS_source_image_project}" \
"${FLAGS_dest_image}"
gcloud compute instances create \
- "${dest_project_flag[@]}" \
+ "${PZ[@]}" \
--machine-type=n1-standard-16 \
--image-family="${FLAGS_source_image_family}" \
--image-project="${FLAGS_source_image_project}" \
--boot-disk-size=200GiB \
"${FLAGS_build_instance}"
- wait_for_instance "${dest_project_flag[@]}" "${FLAGS_build_instance}"
+ wait_for_instance "${PZ[@]}" "${FLAGS_build_instance}"
# Ubuntu tends to mount the wrong disk as root, so help it by waiting until
# it has booted before giving it access to the clean image disk
gcloud compute instances attach-disk \
- "${dest_project_flag[@]}" \
+ "${PZ[@]}" \
"${FLAGS_build_instance}" --disk="${FLAGS_dest_image}"
- gcloud compute scp "${dest_project_flag[@]}" \
+ # beta for the --internal-ip flag that may be passed via SSH_FLAGS
+ gcloud beta compute scp "${SSH_FLAGS[@]}" "${PZ[@]}" \
"${source_files[@]}" \
"${FLAGS_build_instance}:"
- gcloud compute ssh \
- "${dest_project_flag[@]}" "${FLAGS_build_instance}" -- \
+ gcloud compute ssh "${SSH_FLAGS[@]}" \
+ "${PZ[@]}" "${FLAGS_build_instance}" -- \
./create_base_image_gce.sh
gcloud compute instances delete -q \
- "${dest_project_flag[@]}" "${FLAGS_build_instance}"
- gcloud compute images create "${dest_project_flag[@]}" \
+ "${PZ[@]}" "${FLAGS_build_instance}"
+ gcloud compute images create \
+ --project="${FLAGS_build_project}" \
--source-disk="${FLAGS_dest_image}" \
+ --source-disk-zone="${FLAGS_build_zone}" \
--licenses=https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx \
"${dest_family_flag[@]}" \
"${FLAGS_dest_image}"
- gcloud compute disks delete -q "${dest_project_flag[@]}" \
+ gcloud compute disks delete -q "${PZ[@]}" \
"${FLAGS_dest_image}"
if [[ -n "${FLAGS_launch_instance}" ]]; then
- gcloud compute instances create "${dest_project_flag[@]}" \
+ gcloud compute instances create "${PZ[@]}" \
+ --image-project="${FLAGS_build_project}" \
--image="${FLAGS_dest_image}" \
--machine-type=n1-standard-4 \
--scopes storage-ro \
"${FLAGS_launch_instance}"
fi
+ cat <<EOF
+ echo Test and if this looks good, consider releasing it via:
+
+ gcloud compute images create \
+ --project="${FLAGS_dest_project}" \
+ --source-image="${FLAGS_dest_image}" \
+ --source-image-project="${FLAGS_build_project}" \
+ "${dest_family_flag[@]}" \
+ "${FLAGS_dest_image}"
+EOF
}
diff --git a/tools/play_audio/.gitignore b/tools/play_audio/.gitignore
new file mode 100644
index 0000000..a6795ba
--- /dev/null
+++ b/tools/play_audio/.gitignore
@@ -0,0 +1,3 @@
+*.o
+play_audio
+opuscpp
diff --git a/tools/play_audio/Makefile b/tools/play_audio/Makefile
new file mode 100644
index 0000000..30730ef
--- /dev/null
+++ b/tools/play_audio/Makefile
@@ -0,0 +1,12 @@
+CXXFLAGS := -Wextra -Wall -pedantic-errors -std=c++17
+LDLIBS := -lopus -lgflags -lglog -lSDL2
+LINK.o := $(LINK.cc)
+
+PROG := play_audio
+OBJS := $(PROG).o client_socket.o sdl_wrapper.o opuscpp/opus_wrapper.o
+
+$(PROG): $(OBJS)
+
+clean:
+ rm -f $(OBJS) $(PROG)
+
diff --git a/tools/play_audio/README.md b/tools/play_audio/README.md
new file mode 100644
index 0000000..28c227f
--- /dev/null
+++ b/tools/play_audio/README.md
@@ -0,0 +1,30 @@
+# Play Audio
+
+Audio receiver for a cuttlefish instance. A binary to play audio from a remote
+Android virtual device.
+
+## Install Dependencies
+
+```
+git clone https://github.com/google/opuscpp
+sudo apt install libsdl2-2.0-0 libsdl2-dev libopus-dev libopus0 libgflags-dev libgoogle-glog-dev
+```
+
+## Build
+
+```
+make
+```
+
+## Run
+Use ssh port forwarding to forward `7444` from a running instance. Then run the
+`play_audio` binary.
+
+```
+./play_audio
+```
+
+If you are running multiple virtual devices on a host, you must pass the
+`device_num` flag to specify the device corresponding to the `vsoc-##` username,
+and you will have to add `1 - device_num` to the port that you forward (vsoc-02
+= port 7445).
diff --git a/tools/play_audio/client_socket.cpp b/tools/play_audio/client_socket.cpp
new file mode 100644
index 0000000..97c6b13
--- /dev/null
+++ b/tools/play_audio/client_socket.cpp
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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 "client_socket.h"
+
+#include "glog/logging.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+namespace {
+std::uint16_t HostOrderUInt16(const void* src) {
+ std::uint16_t result{};
+ std::memcpy(&result, src, sizeof result);
+ return htons(result);
+}
+
+std::uint32_t HostOrderUInt32(const void* src) {
+ std::uint32_t result{};
+ std::memcpy(&result, src, sizeof result);
+ return htonl(result);
+}
+} // namespace
+
+using cfp::ClientSocket;
+
+ClientSocket::ClientSocket(std::uint16_t port)
+ : socket_fd_{socket(AF_INET, SOCK_STREAM, 0)} {
+ sockaddr_in server_addr{};
+
+ if (socket_fd_ < 0) {
+ LOG(ERROR) << "couldn't create socket\n";
+ return;
+ }
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_port = htons(port);
+
+ if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) {
+ LOG(ERROR) << "couldn't convert localhost address\n";
+ close();
+ return;
+ }
+
+ if (connect(socket_fd_, reinterpret_cast<sockaddr*>(&server_addr),
+ sizeof server_addr) < 0) {
+ LOG(ERROR) << "connection failed\n";
+ close();
+ return;
+ }
+}
+
+ClientSocket::~ClientSocket() { close(); }
+
+ClientSocket::ClientSocket(ClientSocket&& other)
+ : socket_fd_{other.socket_fd_} {
+ other.socket_fd_ = -1;
+}
+
+ClientSocket& ClientSocket::operator=(ClientSocket&& other) {
+ close();
+ socket_fd_ = other.socket_fd_;
+ other.socket_fd_ = -1;
+ return *this;
+}
+
+std::vector<unsigned char> ClientSocket::RecvAll(ssize_t count) {
+ std::vector<unsigned char> buf(count);
+ size_t total_read = 0;
+ while (total_read < buf.size()) {
+ auto just_read =
+ read(socket_fd_, buf.data() + total_read, buf.size() - total_read);
+ if (just_read <= 0) {
+ LOG(ERROR) << "read failed";
+ return {};
+ }
+ total_read += static_cast<size_t>(just_read);
+ }
+ return buf;
+}
+
+std::uint16_t ClientSocket::RecvUInt16() {
+ return HostOrderUInt16(RecvAll(sizeof(std::uint16_t)).data());
+}
+
+std::uint32_t ClientSocket::RecvUInt32() {
+ return HostOrderUInt32(RecvAll(sizeof(std::uint32_t)).data());
+}
+
+void ClientSocket::close() {
+ if (socket_fd_ >= 0) {
+ ::close(socket_fd_);
+ socket_fd_ = -1;
+ }
+}
diff --git a/tools/play_audio/client_socket.h b/tools/play_audio/client_socket.h
new file mode 100644
index 0000000..f51cb6a
--- /dev/null
+++ b/tools/play_audio/client_socket.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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.
+ */
+#ifndef CFP_CLIENT_SOCKET_
+#define CFP_CLIENT_SOCKET_
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <vector>
+
+namespace cfp {
+
+class ClientSocket {
+ public:
+ ClientSocket(std::uint16_t port);
+ ~ClientSocket();
+
+ ClientSocket(ClientSocket&& other);
+ ClientSocket& operator=(ClientSocket&& other);
+
+ ClientSocket(const ClientSocket&) = delete;
+ ClientSocket& operator=(const ClientSocket&) = delete;
+
+ bool valid() const { return socket_fd_ >= 0; }
+
+ std::vector<unsigned char> RecvAll(ssize_t count);
+
+ std::uint16_t RecvUInt16();
+ std::uint32_t RecvUInt32();
+
+ private:
+ void close();
+ int socket_fd_ = -1;
+};
+
+} // namespace cfp
+
+#endif
diff --git a/tools/play_audio/play_audio.cpp b/tools/play_audio/play_audio.cpp
new file mode 100644
index 0000000..2594396
--- /dev/null
+++ b/tools/play_audio/play_audio.cpp
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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 "client_socket.h"
+#include "sdl_wrapper.h"
+
+#include "glog/logging.h"
+#include "gflags/gflags.h"
+#include "opuscpp/opus_wrapper.h"
+#include <SDL2/SDL.h>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+DEFINE_int32(
+ device_num, 1,
+ "Cuttlefish device number, corresponding to username vsoc-## number");
+
+namespace {
+std::uint16_t AudioPort() {
+ constexpr std::uint16_t kAudioStreamBasePort = 7444;
+ std::uint16_t audio_port = kAudioStreamBasePort + (FLAGS_device_num - 1);
+ return audio_port;
+}
+
+cfp::ClientSocket Connect() {
+ const auto port = AudioPort();
+ auto conn = cfp::ClientSocket{port};
+ if (!conn.valid()) {
+ LOG(FATAL) << "couldn't connect on port " << port;
+ }
+ return conn;
+}
+
+std::tuple<std::uint16_t, std::uint16_t> RecvHeader(cfp::ClientSocket* conn) {
+ // creating variables because these must be received in order
+ auto num_channels = conn->RecvUInt16();
+ auto frame_rate = conn->RecvUInt16();
+ LOG(INFO) << "\nnum_channels: " << num_channels
+ << "\nframe_rate: " << frame_rate << '\n';
+ return {num_channels, frame_rate};
+}
+
+// Returns frame_size and encoded audio
+std::tuple<std::uint32_t, std::vector<unsigned char>> RecvEncodedAudio(
+ cfp::ClientSocket* conn) {
+ auto length = conn->RecvUInt32();
+ auto frame_size = conn->RecvUInt32();
+ auto encoded = conn->RecvAll(length);
+
+ if (encoded.size() < length) {
+ encoded.clear();
+ }
+ return {frame_size, std::move(encoded)};
+}
+
+void PlayDecodedAudio(cfp::SDLAudioDevice* audio_device,
+ const std::vector<opus_int16>& audio) {
+ auto sz = audio.size() * sizeof audio[0];
+ auto ret = audio_device->QueueAudio(audio.data(), sz);
+ if (ret < 0) {
+ LOG(ERROR) << "failed to queue audio: " << SDL_GetError() << '\n';
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ ::google::InitGoogleLogging(argv[0]);
+ ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ cfp::SDLLib sdl{};
+
+ auto conn = Connect();
+ const auto& [num_channels, frame_rate] = RecvHeader(&conn);
+
+ auto audio_device = sdl.OpenAudioDevice(frame_rate, num_channels);
+ auto dec =
+ opus::Decoder{static_cast<std::uint32_t>(frame_rate), num_channels};
+ CHECK(dec.valid()) << "Could not construct Decoder. Maybe bad frame_rate ("
+ << frame_rate <<") or num_channels (" << num_channels
+ << ")?";
+
+ while (true) {
+ CHECK(dec.valid()) << "decoder in invalid state";
+ const auto& [frame_size, encoded] = RecvEncodedAudio(&conn);
+ if (encoded.empty()) {
+ break;
+ }
+ auto decoded = dec.Decode(encoded, frame_size, false);
+ if (decoded.empty()) {
+ break;
+ }
+ PlayDecodedAudio(&audio_device, decoded);
+ }
+}
diff --git a/tools/play_audio/sdl_wrapper.cpp b/tools/play_audio/sdl_wrapper.cpp
new file mode 100644
index 0000000..11f76df
--- /dev/null
+++ b/tools/play_audio/sdl_wrapper.cpp
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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 "sdl_wrapper.h"
+
+#include "glog/logging.h"
+#include <SDL2/SDL.h>
+
+#include <cstdint>
+
+using cfp::SDLAudioDevice;
+using cfp::SDLLib;
+
+SDLAudioDevice::SDLAudioDevice(SDLAudioDevice&& other)
+ : device_id_{other.device_id_} {
+ other.device_id_ = 0;
+}
+SDLAudioDevice& SDLAudioDevice::operator=(SDLAudioDevice&& other) {
+ close();
+ device_id_ = other.device_id_;
+ other.device_id_ = 0;
+ return *this;
+}
+
+SDLAudioDevice::~SDLAudioDevice() { close(); }
+
+int SDLAudioDevice::QueueAudio(const void* data, std::uint32_t len) {
+ return SDL_QueueAudio(device_id_, data, len);
+}
+
+SDLAudioDevice::SDLAudioDevice(SDL_AudioDeviceID device_id)
+ : device_id_{device_id} {}
+
+void SDLAudioDevice::close() {
+ if (device_id_ != 0) {
+ SDL_CloseAudioDevice(device_id_);
+ }
+}
+
+SDLLib::SDLLib() { SDL_Init(SDL_INIT_AUDIO); }
+SDLLib::~SDLLib() { SDL_Quit(); }
+
+SDLAudioDevice SDLLib::OpenAudioDevice(int freq, std::uint8_t num_channels) {
+ SDL_AudioSpec wav_spec{};
+ wav_spec.freq = freq;
+ wav_spec.format = AUDIO_S16LSB;
+ wav_spec.channels = num_channels;
+ wav_spec.silence = 0;
+ // .samples seems to work as low as 256,
+ // docs say this is 4096 when used with SDL_LoadWAV so I'm sticking with
+ // that
+ wav_spec.samples = 4096;
+ wav_spec.size = 0;
+
+ auto audio_device_id = SDL_OpenAudioDevice(nullptr, 0, &wav_spec, nullptr, 0);
+ if (audio_device_id == 0) {
+ LOG(FATAL) << "failed to open audio device: " << SDL_GetError() << '\n';
+ }
+ SDL_PauseAudioDevice(audio_device_id, false);
+ return SDLAudioDevice{audio_device_id};
+}
diff --git a/tools/play_audio/sdl_wrapper.h b/tools/play_audio/sdl_wrapper.h
new file mode 100644
index 0000000..9be1df5
--- /dev/null
+++ b/tools/play_audio/sdl_wrapper.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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 <SDL2/SDL.h>
+#include <cstdint>
+
+namespace cfp {
+
+class SDLLib;
+
+class SDLAudioDevice {
+ public:
+ SDLAudioDevice(SDLAudioDevice&& other);
+ SDLAudioDevice& operator=(SDLAudioDevice&& other);
+
+ SDLAudioDevice(const SDLAudioDevice&) = delete;
+ SDLAudioDevice& operator=(const SDLAudioDevice&) = delete;
+
+ ~SDLAudioDevice();
+
+ int QueueAudio(const void* data, std::uint32_t len);
+
+ private:
+ friend SDLLib;
+ explicit SDLAudioDevice(SDL_AudioDeviceID device_id);
+ void close();
+
+ SDL_AudioDeviceID device_id_{};
+};
+
+class SDLLib {
+ public:
+ SDLLib();
+ ~SDLLib();
+
+ SDLLib(const SDLLib&) = delete;
+ SDLLib& operator=(const SDLLib&) = delete;
+
+ SDLAudioDevice OpenAudioDevice(int freq, std::uint8_t num_channels);
+};
+
+} // namespace cfp
diff --git a/tools/tombstone_to_line.py b/tools/tombstone_to_line.py
new file mode 100755
index 0000000..7320bd5
--- /dev/null
+++ b/tools/tombstone_to_line.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+#
+# 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.
+#
+"""Use addr2line to interpret tombstone contents
+
+The defaults should work if this is run after running lunch.
+"""
+
+from __future__ import print_function
+import argparse
+import collections
+import functools
+import multiprocessing
+import os
+import re
+import subprocess
+import sys
+
+
+# Patterns of things that we might want to match.
+
+patterns = [
+ (re.compile('(.* pc )([0-9a-f]+) +([^ ]+) .*'), 1, 3, 2),
+ (re.compile('(.*)#[0-9]+ 0x[0-9a-f]+ +\((.*)\+0x([0-9a-f]+)\)'), 1, 2, 3)]
+
+
+LookupInfo = collections.namedtuple('LookupInfo',
+ ['line_number', 'details', 'file_name'])
+
+
+def lookup_addr(args, object_path, address):
+ try:
+ if object_path[0] == os.path.sep:
+ object_path = object_path[1:]
+ parms = [args.addr2line, '-e',
+ os.path.join(args.symbols, object_path), address]
+ details = subprocess.check_output(parms).strip().split(':')
+ return LookupInfo(
+ line_number=details[-1],
+ details=details,
+ file_name=':'.join(details[:-1]))
+ except subprocess.CalledProcessError:
+ return None
+
+
+def simple_match(line, info, indent, out_file):
+ print('{} // From {}:{}'.format(
+ line, info.file_name, info.line_number), file=out_file)
+
+
+def source_match(line, info, indent, out_file):
+ source = ''
+ try:
+ with open(info.file_name, 'r') as f:
+ for i in range(int(info.line_number.split(' ')[0])):
+ source = f.readline()
+ # Fall back to the simple formatter on any error
+ except Exception:
+ simple_match(line, info, indent, out_file)
+ return
+ print(line, file=out_file)
+ print('{}// From {}:{}'.format(
+ ' ' * indent, info.file_name, info.line_number), file=out_file)
+ print('{} {}'.format(' ' * indent, ' '.join(source.strip().split())),
+ file=out_file)
+
+
+def process(in_file, out_file, args):
+ for line in in_file:
+ line = line.rstrip()
+ groups = None
+ for p in patterns:
+ groups = p[0].match(line)
+ if groups:
+ break
+ info = None
+ if groups is not None:
+ info = lookup_addr(args, groups.group(p[2]), groups.group(p[3]))
+ if info is None:
+ print(line, file=out_file)
+ continue
+ if args.source:
+ source_match(line, info, len(groups.group(p[1])), out_file)
+ else:
+ simple_match(line, info, len(groups.group(p[1])), out_file)
+
+
+def process_file(path, args):
+ with open(path + args.suffix, 'w') as out_file:
+ with open(path, 'r') as in_file:
+ process(in_file, out_file, args)
+
+
+def common_arg_parser():
+ parser = argparse.ArgumentParser(description=
+ 'Add line information to a tombstone')
+ parser.add_argument('--addr2line', type=str,
+ help='Path to addr2line',
+ default=os.path.join(
+ os.environ.get('ANDROID_TOOLCHAIN', ''),
+ 'x86_64-linux-android-addr2line'))
+ parser.add_argument('files', metavar='FILE', type=str, nargs='+',
+ help='a list of files to process')
+ parser.add_argument('--jobs', type=int, default=32,
+ help='Number of parallel jobs to run')
+ parser.add_argument('--source', default=False, action='store_true',
+ help='Attempt to print the source')
+ parser.add_argument('--suffix', type=str, default='.txt',
+ help='Suffix to add to the processed file')
+ return parser
+
+
+
+def process_all(args):
+ multiprocessing.Pool(32).map(functools.partial(process_file, args=args),
+ args.files)
+
+
+if __name__ == '__main__':
+ parser = common_arg_parser()
+ parser.add_argument('--symbols', type=str,
+ help='Path to the symbols',
+ default=os.path.join(
+ os.environ.get('ANDROID_PRODUCT_OUT', ''), 'symbols'))
+ process_all(parser.parse_args())
diff --git a/tools/upload_to_gce_and_run.py b/tools/upload_to_gce_and_run.py
index 9d04b73..9ceee1f 100755
--- a/tools/upload_to_gce_and_run.py
+++ b/tools/upload_to_gce_and_run.py
@@ -9,21 +9,22 @@
def upload_artifacts(args):
- image_pat = os.path.join(args.dist_dir, '%s-img-*.zip' % args.product)
- images = glob.glob(image_pat)
- if len(images) == 0:
- raise OSError('File not found: %s' + image_pat)
- if len(images) > 1:
- raise OSError('%s matches multiple images: %s' % (
- image_pat, images))
- subprocess.check_call(
- 'gcloud compute ssh %s@%s -- /usr/bin/install_zip.sh . < %s' % (
+ dir = os.getcwd()
+ try:
+ os.chdir(args.image_dir)
+ images = glob.glob('*.img')
+ if len(images) == 0:
+ raise OSError('File not found: %s' + image_pat)
+ subprocess.check_call(
+ 'tar -c -f - --lzop -S ' + ' '.join(images) +
+ ' | gcloud compute ssh %s@%s -- tar -x -f - --lzop -S' % (
args.user,
- args.instance,
- images[0]),
+ args.instance),
shell=True)
+ finally:
+ os.chdir(dir)
- host_package = os.path.join(args.dist_dir, 'cvd-host_package.tar.gz')
+ host_package = os.path.join(args.host_dir, 'cvd-host_package.tar.gz')
# host_package
subprocess.check_call(
'gcloud compute ssh %s@%s -- tar -x -z -f - < %s' % (
@@ -63,20 +64,19 @@
parser = argparse.ArgumentParser(
description='Upload a local build to Google Compute Engine and run it')
parser.add_argument(
- '-dist_dir',
+ '-host_dir',
type=str,
- default=os.path.join(os.environ.get('ANDROID_BUILD_TOP', '.'),
- 'out', 'dist'),
+ default=os.environ.get('ANDROID_HOST_OUT', '.'),
help='path to the dist directory')
parser.add_argument(
+ '-image_dir',
+ type=str,
+ default=os.environ.get('ANDROID_PRODUCT_OUT', '.'),
+ help='path to the img files')
+ parser.add_argument(
'-instance', type=str, required=True,
help='instance to update')
parser.add_argument(
- '-product',
- type=str,
- default=os.environ.get('TARGET_PRODUCT', 'cf_x86_phone'),
- help='product to upload')
- parser.add_argument(
'-user', type=str, default='vsoc-01',
help='user to update on the instance')
parser.add_argument(
@@ -85,10 +85,15 @@
parser.add_argument(
'-blank-data-image-mb', type=int, default=4098,
help='custom userdata image size in megabytes')
+ parser.add_argument(
+ '-launch', default=False,
+ action='store_true',
+ help='launch the device')
args = parser.parse_args()
stop_cvd(args)
upload_artifacts(args)
- launch_cvd(args)
+ if args.launch:
+ launch_cvd(args)
if __name__ == '__main__':